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

daisytuner / sdfglib / 20789600894

07 Jan 2026 05:03PM UTC coverage: 62.056% (+0.06%) from 61.994%
20789600894

Pull #440

github

web-flow
Merge 090500b12 into acd6225ac
Pull Request #440: Faster block sorting

101 of 150 new or added lines in 2 files covered. (67.33%)

6 existing lines in 1 file now uncovered.

14940 of 24075 relevant lines covered (62.06%)

88.19 hits per line

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

70.61
/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() < 2) {
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() - 1; i++) {
10✔
202
        auto* node = &sequence.at(i).first;
5✔
203
        if (dynamic_cast<structured_control_flow::Return*>(node) ||
5✔
204
            dynamic_cast<structured_control_flow::Break*>(node) ||
5✔
205
            dynamic_cast<structured_control_flow::Continue*>(node)) {
5✔
206
            // Sorting after return, break, and continue is useless
NEW
207
            break;
×
208
        } else if (auto* sequence2 = dynamic_cast<structured_control_flow::Sequence*>(node)) {
5✔
NEW
209
            applied |= this->sort(builder, analysis_manager, *sequence2);
×
210
        } else if (auto* while_loop = dynamic_cast<structured_control_flow::While*>(node)) {
5✔
NEW
211
            applied |= this->sort(builder, analysis_manager, while_loop->root());
×
212
        } else if (auto* loop = dynamic_cast<structured_control_flow::StructuredLoop*>(node)) {
5✔
NEW
213
            applied |= this->sort(builder, analysis_manager, loop->root());
×
214
        } else if (auto* if_else = dynamic_cast<structured_control_flow::IfElse*>(node)) {
5✔
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
        bool local_applied = false;
5✔
221
        long long index = i;
5✔
222
        while (index >= 0 && this->bubble_up(builder, analysis_manager, sequence, index)) {
9✔
223
            local_applied = true;
4✔
224
            index--;
4✔
225
        }
4✔
226
        applied |= local_applied;
5✔
227
    }
5✔
228

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

240
    return applied;
5✔
241
}
5✔
242

243
bool BlockSortingPass::run_pass(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager) {
5✔
244
    return this->sort(builder, analysis_manager, builder.subject().root());
5✔
245
}
5✔
246

247
bool BlockSortingPass::is_libnode_side_effect_white_listed(data_flow::LibraryNode* libnode) {
3✔
248
    return dynamic_cast<stdlib::AllocaNode*>(libnode) || dynamic_cast<stdlib::CallocNode*>(libnode) ||
3✔
249
           dynamic_cast<stdlib::FreeNode*>(libnode) || dynamic_cast<stdlib::MallocNode*>(libnode) ||
3✔
250
           dynamic_cast<stdlib::MemsetNode*>(libnode);
3✔
251
}
3✔
252

253
bool BlockSortingPass::can_be_bubbled_up(structured_control_flow::Block& block) {
5✔
254
    if (this->is_reference_block(block)) {
5✔
255
        return true;
2✔
256
    }
2✔
257

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

264
    return false;
×
265
}
3✔
266

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

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

292
    return {INT_MIN, ""};
2✔
293
}
12✔
294

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

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

323
} // namespace passes
324
} // 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