• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daisytuner / sdfglib / 20844345261

09 Jan 2026 07:17AM UTC coverage: 62.448% (+0.07%) from 62.374%
20844345261

push

github

web-flow
Faster block sorting (#440)

Reduced runtime of CodeMotion pipeline from 104323 ms to 71224 ms on 'cloudsc_c'.

104 of 153 new or added lines in 2 files covered. (67.97%)

6 existing lines in 1 file now uncovered.

15070 of 24132 relevant lines covered (62.45%)

88.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.0
/src/passes/code_motion/block_sorting.cpp
1
#include "sdfg/passes/code_motion/block_sorting.h"
2

3
#include <climits>
4
#include <string>
5
#include <unordered_set>
6
#include <utility>
7

8
#include "sdfg/analysis/analysis.h"
9
#include "sdfg/analysis/users.h"
10
#include "sdfg/builder/structured_sdfg_builder.h"
11
#include "sdfg/data_flow/access_node.h"
12
#include "sdfg/data_flow/data_flow_graph.h"
13
#include "sdfg/data_flow/library_node.h"
14
#include "sdfg/data_flow/library_nodes/stdlib/alloca.h"
15
#include "sdfg/data_flow/library_nodes/stdlib/calloc.h"
16
#include "sdfg/data_flow/library_nodes/stdlib/free.h"
17
#include "sdfg/data_flow/library_nodes/stdlib/malloc.h"
18
#include "sdfg/data_flow/library_nodes/stdlib/memset.h"
19
#include "sdfg/data_flow/memlet.h"
20
#include "sdfg/data_flow/tasklet.h"
21
#include "sdfg/element.h"
22
#include "sdfg/structured_control_flow/block.h"
23
#include "sdfg/structured_control_flow/control_flow_node.h"
24
#include "sdfg/structured_control_flow/if_else.h"
25
#include "sdfg/structured_control_flow/return.h"
26
#include "sdfg/structured_control_flow/sequence.h"
27
#include "sdfg/structured_control_flow/structured_loop.h"
28
#include "sdfg/structured_control_flow/while.h"
29

30
namespace sdfg {
31
namespace passes {
32

33
bool BlockSortingPass::bubble_up(
34
    builder::StructuredSDFGBuilder& builder,
35
    analysis::AnalysisManager& analysis_manager,
36
    structured_control_flow::Sequence& sequence,
37
    long long index
38
) {
5✔
39
    // Skip assignments
40
    auto current_child = sequence.at(index);
5✔
41
    if (!current_child.second.empty()) {
5✔
NEW
42
        return false;
×
NEW
43
    }
×
44
    auto next_child = sequence.at(index + 1);
5✔
45
    if (!next_child.second.empty()) {
5✔
NEW
46
        return false;
×
NEW
47
    }
×
48

49
    // Childs must be blocks
50
    auto* current_block = dynamic_cast<structured_control_flow::Block*>(&current_child.first);
5✔
51
    if (!current_block) {
5✔
NEW
52
        return false;
×
NEW
53
    }
×
54
    auto* next_block = dynamic_cast<structured_control_flow::Block*>(&next_child.first);
5✔
55
    if (!next_block) {
5✔
56
        return false;
×
57
    }
×
58

59
    // Check if current block has side-effects
60
    bool side_effect = false;
5✔
61
    for (auto* libnode : current_block->dataflow().library_nodes()) {
5✔
62
        if (this->is_libnode_side_effect_white_listed(libnode)) {
2✔
63
            continue;
2✔
64
        }
2✔
NEW
65
        if (libnode->side_effect()) {
×
NEW
66
            side_effect = true;
×
NEW
67
            break;
×
UNCOV
68
        }
×
NEW
69
    }
×
70
    if (side_effect) {
5✔
NEW
71
        return false;
×
NEW
72
    }
×
73

74
    // Check if happens-before relation allows swapping
75
    auto& users_analysis = analysis_manager.get<analysis::Users>();
5✔
76
    analysis::UsersView current_users(users_analysis, *current_block);
5✔
77
    analysis::UsersView next_users(users_analysis, *next_block);
5✔
78
    bool safe = true;
5✔
79
    for (auto user : next_users.uses()) {
7✔
80
        if (current_users.uses(user->container()).size() > 0) {
7✔
NEW
81
            safe = false;
×
82
            break;
×
83
        }
×
84
    }
7✔
85
    if (!safe) {
5✔
NEW
86
        return false;
×
NEW
87
    }
×
88

89
    // Check if libnode/reference can be bubbled up
90
    if (!this->can_be_bubbled_up(*next_block)) {
5✔
NEW
91
        return false;
×
NEW
92
    }
×
93

94
    // Compare priority and order
95
    auto [current_prio, current_order] = this->get_prio_and_order(current_block);
5✔
96
    auto [next_prio, next_order] = this->get_prio_and_order(next_block);
5✔
97
    if (current_prio > next_prio) {
5✔
98
        return false;
1✔
99
    }
1✔
100
    if (current_prio == next_prio && current_order <= next_order) {
4✔
NEW
101
        return false;
×
NEW
102
    }
×
103

104
    // Swap blocks
105
    builder.move_child(sequence, index + 1, sequence, index);
4✔
106
    analysis_manager.invalidate_all();
4✔
107

108
    return true;
4✔
109
}
4✔
110

111
bool BlockSortingPass::bubble_down(
112
    builder::StructuredSDFGBuilder& builder,
113
    analysis::AnalysisManager& analysis_manager,
114
    structured_control_flow::Sequence& sequence,
115
    long long index
116
) {
5✔
117
    // Skip assignments
118
    auto current_child = sequence.at(index);
5✔
119
    if (!current_child.second.empty()) {
5✔
NEW
120
        return false;
×
NEW
121
    }
×
122
    auto next_child = sequence.at(index - 1);
5✔
123
    if (!next_child.second.empty()) {
5✔
NEW
124
        return false;
×
NEW
125
    }
×
126

127
    // Childs must be blocks
128
    auto* current_block = dynamic_cast<structured_control_flow::Block*>(&current_child.first);
5✔
129
    if (!current_block) {
5✔
NEW
130
        return false;
×
NEW
131
    }
×
132
    auto* next_block = dynamic_cast<structured_control_flow::Block*>(&next_child.first);
5✔
133
    if (!next_block) {
5✔
NEW
134
        return false;
×
UNCOV
135
    }
×
136

137
    // Check if current block has side-effects
138
    bool side_effect = false;
5✔
139
    for (auto* libnode : current_block->dataflow().library_nodes()) {
5✔
140
        if (this->is_libnode_side_effect_white_listed(libnode)) {
1✔
141
            continue;
1✔
142
        }
1✔
NEW
143
        if (libnode->side_effect()) {
×
NEW
144
            side_effect = true;
×
NEW
145
            break;
×
UNCOV
146
        }
×
NEW
147
    }
×
148
    if (side_effect) {
5✔
NEW
149
        return false;
×
NEW
150
    }
×
151

152
    // Check if happens-before relation allows swapping
153
    auto& users_analysis = analysis_manager.get<analysis::Users>();
5✔
154
    analysis::UsersView current_users(users_analysis, *current_block);
5✔
155
    analysis::UsersView next_users(users_analysis, *next_block);
5✔
156
    bool safe = true;
5✔
157
    for (auto user : next_users.uses()) {
7✔
158
        if (current_users.uses(user->container()).size() > 0) {
7✔
NEW
159
            safe = false;
×
NEW
160
            break;
×
UNCOV
161
        }
×
162
    }
7✔
163
    if (!safe) {
5✔
NEW
164
        return false;
×
NEW
165
    }
×
166

167
    // Check if libnode can be bubbled down
168
    if (!this->can_be_bubbled_down(*next_block)) {
5✔
169
        return false;
4✔
170
    }
4✔
171

172
    // Compare priority and order
173
    auto [current_prio, current_order] = this->get_prio_and_order(current_block);
1✔
174
    auto [next_prio, next_order] = this->get_prio_and_order(next_block);
1✔
175
    if (current_prio > next_prio) {
1✔
NEW
176
        return false;
×
NEW
177
    }
×
178
    if (current_prio == next_prio && current_order <= next_order) {
1✔
NEW
179
        return false;
×
NEW
180
    }
×
181

182
    // Swap blocks
183
    builder.move_child(sequence, index - 1, sequence, index);
1✔
184
    analysis_manager.invalidate_all();
1✔
185

186
    return true;
1✔
187
}
1✔
188

189
bool BlockSortingPass::sort(
190
    builder::StructuredSDFGBuilder& builder,
191
    analysis::AnalysisManager& analysis_manager,
192
    structured_control_flow::Sequence& sequence
193
) {
5✔
194
    if (sequence.size() == 0) {
5✔
NEW
195
        return false;
×
NEW
196
    }
×
197
    bool applied = false;
5✔
198

199
    // Bubble up
200
    size_t i;
5✔
201
    for (i = 0; i < sequence.size(); i++) {
10✔
202
        auto* node = &sequence.at(i).first;
10✔
203
        if (dynamic_cast<structured_control_flow::Return*>(node) ||
10✔
204
            dynamic_cast<structured_control_flow::Break*>(node) ||
10✔
205
            dynamic_cast<structured_control_flow::Continue*>(node)) {
10✔
206
            // Sorting after return, break, and continue is useless
NEW
207
            break;
×
208
        } else if (auto* sequence2 = dynamic_cast<structured_control_flow::Sequence*>(node)) {
10✔
NEW
209
            applied |= this->sort(builder, analysis_manager, *sequence2);
×
210
        } else if (auto* while_loop = dynamic_cast<structured_control_flow::While*>(node)) {
10✔
NEW
211
            applied |= this->sort(builder, analysis_manager, while_loop->root());
×
212
        } else if (auto* loop = dynamic_cast<structured_control_flow::StructuredLoop*>(node)) {
10✔
NEW
213
            applied |= this->sort(builder, analysis_manager, loop->root());
×
214
        } else if (auto* if_else = dynamic_cast<structured_control_flow::IfElse*>(node)) {
10✔
NEW
215
            for (size_t k = 0; k < if_else->size(); k++) {
×
NEW
216
                applied |= this->sort(builder, analysis_manager, if_else->at(k).first);
×
UNCOV
217
            }
×
UNCOV
218
        }
×
219

220
        if (i == sequence.size() - 1) {
10✔
221
            break;
5✔
222
        }
5✔
223

224
        bool local_applied = false;
5✔
225
        long long index = i;
5✔
226
        while (index >= 0 && this->bubble_up(builder, analysis_manager, sequence, index)) {
9✔
227
            local_applied = true;
4✔
228
            index--;
4✔
229
        }
4✔
230
        applied |= local_applied;
5✔
231
    }
5✔
232

233
    // Bubble down
234
    for (size_t j = i; j > 0; j--) {
10✔
235
        bool local_applied = false;
5✔
236
        long long index = j;
5✔
237
        while (index <= i && this->bubble_down(builder, analysis_manager, sequence, index)) {
6✔
238
            local_applied = true;
1✔
239
            index++;
1✔
240
        }
1✔
241
        applied |= local_applied;
5✔
242
    }
5✔
243

244
    return applied;
5✔
245
}
5✔
246

247
bool BlockSortingPass::run_pass(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
5✔
248
    return this->sort(builder, analysis_manager, builder.subject().root());
5✔
249
}
5✔
250

251
bool BlockSortingPass::is_libnode_side_effect_white_listed(data_flow::LibraryNode* libnode) {
3✔
252
    return dynamic_cast<stdlib::AllocaNode*>(libnode) || dynamic_cast<stdlib::CallocNode*>(libnode) ||
3✔
253
           dynamic_cast<stdlib::FreeNode*>(libnode) || dynamic_cast<stdlib::MallocNode*>(libnode) ||
3✔
254
           dynamic_cast<stdlib::MemsetNode*>(libnode);
3✔
255
}
3✔
256

257
bool BlockSortingPass::can_be_bubbled_up(structured_control_flow::Block& block) {
5✔
258
    if (this->is_reference_block(block)) {
5✔
259
        return true;
2✔
260
    }
2✔
261

262
    if (this->is_libnode_block(block)) {
3✔
263
        auto* libnode = *block.dataflow().library_nodes().begin();
3✔
264
        return dynamic_cast<stdlib::AllocaNode*>(libnode) || dynamic_cast<stdlib::CallocNode*>(libnode) ||
3✔
265
               dynamic_cast<stdlib::MallocNode*>(libnode) || dynamic_cast<stdlib::MemsetNode*>(libnode);
3✔
266
    }
3✔
267

268
    return false;
×
269
}
3✔
270

271
bool BlockSortingPass::can_be_bubbled_down(structured_control_flow::Block& block) {
5✔
272
    if (!this->is_libnode_block(block)) {
5✔
273
        return false;
1✔
274
    }
1✔
275
    auto* libnode = *block.dataflow().library_nodes().begin();
4✔
276
    return dynamic_cast<stdlib::FreeNode*>(libnode);
4✔
277
}
5✔
278

279
std::pair<int, std::string> BlockSortingPass::get_prio_and_order(structured_control_flow::Block* block) {
12✔
280
    if (this->is_libnode_block(*block)) {
12✔
281
        auto* libnode = *block->dataflow().library_nodes().begin();
6✔
282
        if (dynamic_cast<stdlib::AllocaNode*>(libnode) || dynamic_cast<stdlib::CallocNode*>(libnode) ||
6✔
283
            dynamic_cast<stdlib::FreeNode*>(libnode) || dynamic_cast<stdlib::MallocNode*>(libnode)) {
6✔
284
            auto& oedge = *block->dataflow().out_edges(*libnode).begin();
6✔
285
            auto& dst = static_cast<data_flow::AccessNode&>(oedge.dst());
6✔
286
            return {300, dst.data()};
6✔
287
        } else if (dynamic_cast<stdlib::MemsetNode*>(libnode)) {
6✔
288
            auto& oedge = *block->dataflow().out_edges(*libnode).begin();
×
289
            auto& dst = static_cast<data_flow::AccessNode&>(oedge.dst());
×
290
            return {200, dst.data()};
×
291
        }
×
292
    } else if (this->is_reference_block(*block)) {
6✔
293
        return {100, ""};
4✔
294
    }
4✔
295

296
    return {INT_MIN, ""};
2✔
297
}
12✔
298

299
bool BlockSortingPass::is_reference_block(structured_control_flow::Block& block) {
11✔
300
    auto& dfg = block.dataflow();
11✔
301
    if (dfg.nodes().size() != 2) {
11✔
302
        return false;
2✔
303
    }
2✔
304
    if (dfg.edges().size() != 1) {
9✔
305
        return false;
×
306
    }
×
307
    auto& edge = *dfg.edges().begin();
9✔
308
    if (edge.type() != data_flow::MemletType::Reference && edge.type() != data_flow::MemletType::Dereference_Src &&
9✔
309
        edge.type() != data_flow::MemletType::Dereference_Dst) {
9✔
310
        return false;
3✔
311
    }
3✔
312
    return true;
6✔
313
}
9✔
314

315
bool BlockSortingPass::is_libnode_block(structured_control_flow::Block& next_block) {
20✔
316
    auto& next_dfg = next_block.dataflow();
20✔
317
    if (next_dfg.library_nodes().size() != 1) {
20✔
318
        return false;
7✔
319
    }
7✔
320
    auto* libnode = *next_dfg.library_nodes().begin();
13✔
321
    if (next_dfg.edges().size() != libnode->inputs().size() + libnode->outputs().size()) {
13✔
322
        return false;
×
323
    }
×
324
    return true;
13✔
325
}
13✔
326

327
} // namespace passes
328
} // namespace sdfg
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc