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

daisytuner / sdfglib / 19336243385

13 Nov 2025 03:14PM UTC coverage: 62.407% (+0.6%) from 61.76%
19336243385

push

github

web-flow
Extended BlockHoisting and BlockSorting (#345)

* Added hoisting moves and views from branching to BlockHoistingPass

* Added hoisting library nodes (alloca, memcpy, memmove, memset) to BlockHoistingPass

* Added hoisting library nodes from branches to BlockHoistingPass

* Added hoisting for last child of loops and branches to BlockHoistingPass

* Little bugfies

* Adapted BlockSortingPass to handle library nodes (without side effects)

* Bugfix for BlockSorting

Library nodes with side effects were swapped which lead to dead code because an exit was bubbled up

369 of 463 new or added lines in 3 files covered. (79.7%)

14 existing lines in 2 files now uncovered.

10905 of 17474 relevant lines covered (62.41%)

112.11 hits per line

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

81.97
/src/passes/code_motion/block_hoisting.cpp
1
#include "sdfg/passes/code_motion/block_hoisting.h"
2
#include <cstddef>
3
#include <string>
4
#include <unordered_map>
5
#include <vector>
6

7
#include "sdfg/analysis/analysis.h"
8
#include "sdfg/analysis/scope_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/memcpy.h"
16
#include "sdfg/data_flow/library_nodes/stdlib/memmove.h"
17
#include "sdfg/data_flow/library_nodes/stdlib/memset.h"
18
#include "sdfg/data_flow/memlet.h"
19
#include "sdfg/data_flow/tasklet.h"
20
#include "sdfg/element.h"
21
#include "sdfg/structured_control_flow/block.h"
22
#include "sdfg/structured_control_flow/control_flow_node.h"
23
#include "sdfg/structured_control_flow/for.h"
24
#include "sdfg/structured_control_flow/if_else.h"
25
#include "sdfg/structured_control_flow/map.h"
26
#include "sdfg/structured_control_flow/sequence.h"
27
#include "sdfg/structured_control_flow/structured_loop.h"
28
#include "sdfg/symbolic/symbolic.h"
29
#include "sdfg/visitor/structured_sdfg_visitor.h"
30

31
namespace sdfg {
32
namespace passes {
33

34
BlockHoisting::BlockHoisting(builder::StructuredSDFGBuilder& builder, analysis::AnalysisManager& analysis_manager)
20✔
35
    : visitor::NonStoppingStructuredSDFGVisitor(builder, analysis_manager) {};
20✔
36

37
bool BlockHoisting::accept(structured_control_flow::Map& map_stmt) {
12✔
38
    auto& scope_analysis = analysis_manager_.get<analysis::ScopeAnalysis>();
12✔
39
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&map_stmt));
12✔
40

41
    bool applied = false;
12✔
42
    applied |= this->map_invariant_front(parent, map_stmt);
12✔
43
    applied |= this->map_invariant_back(parent, map_stmt);
12✔
44

45
    return applied;
12✔
46
}
47

48
bool BlockHoisting::accept(structured_control_flow::For& for_stmt) {
15✔
49
    auto& scope_analysis = analysis_manager_.get<analysis::ScopeAnalysis>();
15✔
50
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&for_stmt));
15✔
51

52
    bool applied = false;
15✔
53
    applied |= this->for_invariant_front(parent, for_stmt);
15✔
54
    applied |= this->for_invariant_back(parent, for_stmt);
15✔
55

56
    return applied;
15✔
57
}
58

59
bool BlockHoisting::accept(structured_control_flow::IfElse& if_else) {
15✔
60
    // Ignore incomplete branches for now
61
    if (if_else.size() == 0 || !if_else.is_complete()) {
15✔
UNCOV
62
        return false;
×
63
    }
64

65
    auto& scope_analysis = analysis_manager_.get<analysis::ScopeAnalysis>();
15✔
66
    auto& parent = static_cast<structured_control_flow::Sequence&>(*scope_analysis.parent_scope(&if_else));
15✔
67

68
    bool applied = false;
15✔
69
    applied |= this->if_else_invariant_front(parent, if_else);
15✔
70
    applied |= this->if_else_invariant_back(parent, if_else);
15✔
71

72
    return applied;
15✔
73
}
15✔
74

75
bool BlockHoisting::is_libnode_allowed(
24✔
76
    structured_control_flow::Sequence& body, data_flow::DataFlowGraph& dfg, data_flow::LibraryNode* libnode
77
) {
78
    if (dynamic_cast<stdlib::AllocaNode*>(libnode)) {
24✔
79
        return true;
4✔
80
    } else if (dynamic_cast<stdlib::MemcpyNode*>(libnode)) {
20✔
81
        return true;
8✔
82
    } else if (dynamic_cast<stdlib::MemmoveNode*>(libnode)) {
12✔
83
        return true;
8✔
84
    } else if (dynamic_cast<stdlib::MemsetNode*>(libnode)) {
4✔
85
        return true;
4✔
86
    } else {
UNCOV
87
        return false;
×
88
    }
89
}
24✔
90

91
bool BlockHoisting::equal_libnodes(structured_control_flow::Block& block1, structured_control_flow::Block& block2) {
6✔
92
    auto& dfg1 = block1.dataflow();
6✔
93
    auto& dfg2 = block2.dataflow();
6✔
94

95
    // Get library nodes
96
    auto* libnode1 = *dfg1.library_nodes().begin();
6✔
97
    auto* libnode2 = *dfg2.library_nodes().begin();
6✔
98

99
    // Collect in edges
100
    std::unordered_map<std::string, data_flow::Memlet*> iedges1, iedges2;
6✔
101
    for (auto& iedge : dfg1.in_edges(*libnode1)) {
10✔
102
        iedges1.insert({iedge.dst_conn(), &iedge});
4✔
103
    }
104
    for (auto& iedge : dfg2.in_edges(*libnode2)) {
10✔
105
        iedges2.insert({iedge.dst_conn(), &iedge});
4✔
106
    }
107

108
    // In edges must have the same type, subset, and container
109
    if (iedges1.size() != iedges2.size()) {
6✔
NEW
110
        return false;
×
111
    }
112
    for (auto [conn, iedge1] : iedges1) {
26✔
113
        if (!iedges2.contains(conn)) {
4✔
NEW
114
            return false;
×
115
        }
116
        auto* iedge2 = iedges2.at(conn);
4✔
117

118
        // Compare types
119
        if (iedge1->type() != iedge2->type()) {
4✔
NEW
120
            return false;
×
121
        }
122

123
        // Compare subsets
124
        if (iedge1->subset().size() != iedge2->subset().size()) {
4✔
NEW
125
            return false;
×
126
        }
127
        for (size_t i = 0; i < iedge1->subset().size(); i++) {
4✔
NEW
128
            if (!symbolic::eq(iedge1->subset().at(i), iedge2->subset().at(i))) {
×
NEW
129
                return false;
×
130
            }
UNCOV
131
        }
×
132

133
        // Compare containers
134
        auto& src1 = static_cast<data_flow::AccessNode&>(iedge1->src());
4✔
135
        auto& src2 = static_cast<data_flow::AccessNode&>(iedge2->src());
4✔
136
        if (src1.data() != src2.data()) {
4✔
NEW
137
            return false;
×
138
        }
139
    }
4✔
140

141
    // Collect out edges
142
    std::unordered_map<std::string, data_flow::Memlet*> oedges1, oedges2;
6✔
143
    for (auto& oedge : dfg1.out_edges(*libnode1)) {
12✔
144
        oedges1.insert({oedge.src_conn(), &oedge});
6✔
145
    }
146
    for (auto& oedge : dfg2.out_edges(*libnode2)) {
12✔
147
        oedges2.insert({oedge.src_conn(), &oedge});
6✔
148
    }
149

150
    // Out edges must have the same type, subset, and container
151
    if (oedges1.size() != oedges2.size()) {
6✔
NEW
152
        return false;
×
153
    }
154
    for (auto [conn, oedge1] : oedges1) {
36✔
155
        if (!oedges2.contains(conn)) {
6✔
NEW
156
            return false;
×
157
        }
158
        auto& oedge2 = oedges2.at(conn);
6✔
159

160
        // Compare types
161
        if (oedge1->type() != oedge2->type()) {
6✔
NEW
162
            return false;
×
163
        }
164

165
        // Compare subsets
166
        if (oedge1->subset().size() != oedge2->subset().size()) {
6✔
NEW
167
            return false;
×
168
        }
169
        for (size_t i = 0; i < oedge1->subset().size(); i++) {
6✔
NEW
170
            if (!symbolic::eq(oedge1->subset().at(i), oedge2->subset().at(i))) {
×
NEW
171
                return false;
×
172
            }
NEW
173
        }
×
174

175
        // Compare containers
176
        auto& dst1 = static_cast<data_flow::AccessNode&>(oedge1->dst());
6✔
177
        auto& dst2 = static_cast<data_flow::AccessNode&>(oedge2->dst());
6✔
178
        if (dst1.data() != dst2.data()) {
6✔
NEW
179
            return false;
×
180
        }
181
    }
6✔
182

183
    // Checks library node internals
184
    return this->equal_libnodes(libnode1, libnode2);
6✔
185
}
6✔
186

187
void BlockHoisting::if_else_extract_invariant_libnode_front(
4✔
188
    structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else
189
) {
190
    // This function is a wrapper that can be overridden in sub-classes
191
    this->if_else_extract_invariant_front(parent, if_else);
4✔
192
}
4✔
193

194
void BlockHoisting::if_else_extract_invariant_libnode_back(
2✔
195
    structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else
196
) {
197
    // This function is a wrapper that can be overridden in sub-classes
198
    this->if_else_extract_invariant_back(parent, if_else);
2✔
199
}
2✔
200

201
bool BlockHoisting::is_invariant_move(
43✔
202
    structured_control_flow::Sequence& body, data_flow::DataFlowGraph& dfg, bool no_loop_carried_dependencies
203
) {
204
    if (dfg.nodes().size() != 2) {
43✔
205
        return false;
26✔
206
    }
207
    if (dfg.edges().size() != 1) {
17✔
NEW
208
        return false;
×
209
    }
210
    auto& edge = *dfg.edges().begin();
17✔
211
    if (edge.type() != data_flow::MemletType::Dereference_Src) {
17✔
212
        return false;
13✔
213
    }
214

215
    if (no_loop_carried_dependencies) {
4✔
216
        return true;
3✔
217
    }
218
    auto& src = static_cast<data_flow::AccessNode&>(edge.src());
1✔
219

220
    auto& users_analysis = analysis_manager_.get<analysis::Users>();
1✔
221
    analysis::UsersView body_view(users_analysis, body);
1✔
222
    if (!body_view.writes(src.data()).empty() || !body_view.moves(src.data()).empty()) {
1✔
NEW
223
        return false;
×
224
    }
225

226
    return true;
1✔
227
}
43✔
228

229
bool BlockHoisting::is_invariant_view(
42✔
230
    structured_control_flow::Sequence& body,
231
    data_flow::DataFlowGraph& dfg,
232
    symbolic::Symbol indvar,
233
    bool no_loop_carried_dependencies
234
) {
235
    if (dfg.nodes().size() != 2) {
42✔
236
        return false;
26✔
237
    }
238
    if (dfg.edges().size() != 1) {
16✔
NEW
239
        return false;
×
240
    }
241
    auto& edge = *dfg.edges().begin();
16✔
242
    if (edge.type() != data_flow::MemletType::Reference) {
16✔
243
        return false;
6✔
244
    }
245

246
    if (!indvar.is_null()) {
10✔
247
        auto& subset = edge.subset();
4✔
248
        for (const auto& dim : subset) {
8✔
249
            if (symbolic::uses(dim, indvar)) {
4✔
NEW
250
                return false;
×
251
            }
252
        }
253
    }
4✔
254

255
    if (no_loop_carried_dependencies) {
10✔
256
        return true;
7✔
257
    }
258
    auto& src = static_cast<data_flow::AccessNode&>(edge.src());
3✔
259

260
    auto& users_analysis = analysis_manager_.get<analysis::Users>();
3✔
261
    analysis::UsersView body_view(users_analysis, body);
3✔
262
    if (!body_view.writes(src.data()).empty() || !body_view.moves(src.data()).empty()) {
3✔
NEW
263
        return false;
×
264
    }
265

266
    return true;
3✔
267
}
42✔
268

269
bool BlockHoisting::is_invariant_libnode(
80✔
270
    structured_control_flow::Sequence& body,
271
    data_flow::DataFlowGraph& dfg,
272
    symbolic::Symbol indvar,
273
    bool no_loop_carried_dependencies
274
) {
275
    if (dfg.library_nodes().size() != 1) {
80✔
276
        return false;
56✔
277
    }
278
    if (dfg.tasklets().size() != 0) {
24✔
NEW
279
        return false;
×
280
    }
281
    auto* libnode = *dfg.library_nodes().begin();
24✔
282
    if (!libnode) {
24✔
NEW
283
        return false;
×
284
    }
285
    if (dfg.data_nodes().size() != libnode->outputs().size() + libnode->inputs().size()) {
24✔
NEW
286
        return false;
×
287
    }
288

289
    if (!this->is_libnode_allowed(body, dfg, libnode)) {
24✔
NEW
290
        return false;
×
291
    }
292

293
    if (!indvar.is_null()) {
24✔
294
        if (libnode->symbols().contains(indvar)) {
12✔
NEW
295
            return false;
×
296
        }
297
        for (auto& oedge : dfg.out_edges(*libnode)) {
24✔
298
            for (const auto& dim : oedge.subset()) {
12✔
NEW
299
                if (symbolic::uses(dim, indvar)) {
×
NEW
300
                    return false;
×
301
                }
302
            }
303
        }
304
        for (auto& iedge : dfg.in_edges(*libnode)) {
20✔
305
            for (const auto& dim : iedge.subset()) {
8✔
NEW
306
                if (symbolic::uses(dim, indvar)) {
×
NEW
307
                    return false;
×
308
                }
309
            }
310
        }
311
    }
12✔
312

313
    if (no_loop_carried_dependencies) {
24✔
314
        return true;
18✔
315
    }
316

317
    auto& users_analysis = analysis_manager_.get<analysis::Users>();
6✔
318
    analysis::UsersView body_view(users_analysis, body);
6✔
319
    for (auto& iedge : dfg.in_edges(*libnode)) {
10✔
320
        auto& src = static_cast<data_flow::AccessNode&>(iedge.src());
4✔
321
        if (!body_view.writes(src.data()).empty() || !body_view.moves(src.data()).empty()) {
4✔
NEW
322
            return false;
×
323
        }
324
    }
325

326
    return true;
6✔
327
}
80✔
328

329
bool BlockHoisting::equal_moves(structured_control_flow::Block& block1, structured_control_flow::Block& block2) {
1✔
330
    // Edges must have same type and subset
331
    auto& edge1 = *block1.dataflow().edges().begin();
1✔
332
    auto& edge2 = *block2.dataflow().edges().begin();
1✔
333
    if (edge1.type() != edge2.type()) {
1✔
NEW
334
        return false;
×
335
    }
336
    if (edge1.subset().size() != edge2.subset().size()) {
1✔
NEW
337
        return false;
×
338
    }
339
    for (size_t i = 0; i < edge1.subset().size(); i++) {
2✔
340
        if (!symbolic::eq(edge1.subset().at(i), edge2.subset().at(i))) {
1✔
NEW
341
            return false;
×
342
        }
343
    }
1✔
344

345
    // Directions must be the same
346
    if (edge1.src_conn() != edge2.src_conn()) {
1✔
NEW
347
        return false;
×
348
    }
349
    if (edge1.dst_conn() != edge2.dst_conn()) {
1✔
NEW
350
        return false;
×
351
    }
352

353
    // src's must have the same containers
354
    auto& src1 = static_cast<data_flow::AccessNode&>(edge1.src());
1✔
355
    auto& src2 = static_cast<data_flow::AccessNode&>(edge2.src());
1✔
356
    if (src1.data() != src2.data()) {
1✔
NEW
357
        return false;
×
358
    }
359

360
    // dst's must have the same containers
361
    auto& dst1 = static_cast<data_flow::AccessNode&>(edge1.dst());
1✔
362
    auto& dst2 = static_cast<data_flow::AccessNode&>(edge2.dst());
1✔
363
    if (dst1.data() != dst2.data()) {
1✔
NEW
364
        return false;
×
365
    }
366

367
    return true;
1✔
368
}
1✔
369

370
bool BlockHoisting::equal_views(structured_control_flow::Block& block1, structured_control_flow::Block& block2) {
3✔
371
    // Edges must have same type and subset
372
    auto& edge1 = *block1.dataflow().edges().begin();
3✔
373
    auto& edge2 = *block2.dataflow().edges().begin();
3✔
374
    if (edge1.type() != edge2.type()) {
3✔
NEW
375
        return false;
×
376
    }
377
    if (edge1.subset().size() != edge2.subset().size()) {
3✔
NEW
378
        return false;
×
379
    }
380
    for (size_t i = 0; i < edge1.subset().size(); i++) {
6✔
381
        if (!symbolic::eq(edge1.subset().at(i), edge2.subset().at(i))) {
3✔
NEW
382
            return false;
×
383
        }
384
    }
3✔
385

386
    // src's must have the same containers
387
    auto& src1 = static_cast<data_flow::AccessNode&>(edge1.src());
3✔
388
    auto& src2 = static_cast<data_flow::AccessNode&>(edge2.src());
3✔
389
    if (src1.data() != src2.data()) {
3✔
NEW
390
        return false;
×
391
    }
392

393
    // dst's must have the same containers
394
    auto& dst1 = static_cast<data_flow::AccessNode&>(edge1.dst());
3✔
395
    auto& dst2 = static_cast<data_flow::AccessNode&>(edge2.dst());
3✔
396
    if (dst1.data() != dst2.data()) {
3✔
NEW
397
        return false;
×
398
    }
399

400
    return true;
3✔
401
}
3✔
402

403
bool BlockHoisting::equal_libnodes(data_flow::LibraryNode* libnode1, data_flow::LibraryNode* libnode2) {
6✔
404
    if (auto* alloca_node1 = dynamic_cast<stdlib::AllocaNode*>(libnode1)) {
6✔
405
        if (auto* alloca_node2 = dynamic_cast<stdlib::AllocaNode*>(libnode2)) {
1✔
406
            return symbolic::eq(alloca_node1->size(), alloca_node2->size());
1✔
407
        }
NEW
408
    }
×
409
    if (auto* memcpy_node1 = dynamic_cast<stdlib::MemcpyNode*>(libnode1)) {
5✔
410
        if (auto* memcpy_node2 = dynamic_cast<stdlib::MemcpyNode*>(libnode2)) {
2✔
411
            return symbolic::eq(memcpy_node1->count(), memcpy_node2->count());
2✔
412
        }
NEW
413
    }
×
414
    if (auto* memmove_node1 = dynamic_cast<stdlib::MemmoveNode*>(libnode1)) {
3✔
415
        if (auto* memmove_node2 = dynamic_cast<stdlib::MemmoveNode*>(libnode2)) {
2✔
416
            return symbolic::eq(memmove_node1->count(), memmove_node2->count());
2✔
417
        }
NEW
418
    }
×
419
    if (auto* memset_node1 = dynamic_cast<stdlib::MemsetNode*>(libnode1)) {
1✔
420
        if (auto* memset_node2 = dynamic_cast<stdlib::MemsetNode*>(libnode2)) {
1✔
421
            return symbolic::eq(memset_node1->value(), memset_node2->value()) &&
2✔
422
                   symbolic::eq(memset_node1->num(), memset_node2->num());
1✔
423
        }
NEW
424
    }
×
NEW
425
    return false;
×
426
}
6✔
427

428
bool BlockHoisting::map_invariant_front(structured_control_flow::Sequence& parent, structured_control_flow::Map& map_stmt) {
12✔
429
    // Extract first child
430
    auto& body = map_stmt.root();
12✔
431
    if (body.size() == 0) {
12✔
432
        return false;
×
433
    }
434
    auto first_child = body.at(0);
12✔
435
    if (!first_child.second.assignments().empty()) {
12✔
436
        return false;
×
437
    }
438
    auto& first_node = first_child.first;
12✔
439

440
    auto* block = dynamic_cast<structured_control_flow::Block*>(&first_node);
12✔
441
    if (!block) {
12✔
NEW
442
        return false;
×
443
    }
444
    if (this->map_invariant_move(parent, map_stmt, *block)) {
12✔
445
        return true;
1✔
446
    }
447
    if (this->map_invariant_view(parent, map_stmt, *block)) {
11✔
448
        return true;
1✔
449
    }
450
    if (this->map_invariant_libnode_front(parent, map_stmt, *block)) {
10✔
451
        return true;
4✔
452
    }
453

454
    return false;
6✔
455
}
12✔
456

457
bool BlockHoisting::map_invariant_back(structured_control_flow::Sequence& parent, structured_control_flow::Map& map_stmt) {
12✔
458
    // Extract last child
459
    auto& body = map_stmt.root();
12✔
460
    if (body.size() == 0) {
12✔
NEW
461
        return false;
×
462
    }
463
    auto last_child = body.at(body.size() - 1);
12✔
464
    if (!last_child.second.assignments().empty()) {
12✔
NEW
465
        return false;
×
466
    }
467
    auto& last_node = last_child.first;
12✔
468

469
    auto* block = dynamic_cast<structured_control_flow::Block*>(&last_node);
12✔
470
    if (!block) {
12✔
NEW
471
        return false;
×
472
    }
473
    return this->map_invariant_libnode_back(parent, map_stmt, *block);
12✔
474
}
12✔
475

476
bool BlockHoisting::map_invariant_move(
12✔
477
    structured_control_flow::Sequence& parent,
478
    structured_control_flow::Map& map_stmt,
479
    structured_control_flow::Block& block
480
) {
481
    auto& body = map_stmt.root();
12✔
482
    if (!this->is_invariant_move(body, block.dataflow())) {
12✔
483
        return false;
11✔
484
    }
485

486
    size_t map_index = parent.index(map_stmt);
1✔
487
    builder_.move_child(body, 0, parent, map_index);
1✔
488
    return true;
1✔
489
}
12✔
490

491
bool BlockHoisting::map_invariant_view(
11✔
492
    structured_control_flow::Sequence& parent,
493
    structured_control_flow::Map& map_stmt,
494
    structured_control_flow::Block& block
495
) {
496
    auto& body = map_stmt.root();
11✔
497
    if (!this->is_invariant_view(body, block.dataflow(), map_stmt.indvar())) {
11✔
498
        return false;
10✔
499
    }
500

501
    size_t map_index = parent.index(map_stmt);
1✔
502
    builder_.move_child(body, 0, parent, map_index);
1✔
503
    return true;
1✔
504
}
11✔
505

506
bool BlockHoisting::map_invariant_libnode_front(
10✔
507
    structured_control_flow::Sequence& parent,
508
    structured_control_flow::Map& map_stmt,
509
    structured_control_flow::Block& block
510
) {
511
    // For now, only allow libnode hoisting on sequential maps
512
    if (map_stmt.schedule_type().value() != ScheduleType_Sequential::value()) {
10✔
UNCOV
513
        return false;
×
514
    }
515

516
    auto& body = map_stmt.root();
10✔
517
    if (!this->is_invariant_libnode(body, block.dataflow(), map_stmt.indvar())) {
10✔
518
        return false;
6✔
519
    }
520

521
    size_t map_index = parent.index(map_stmt);
4✔
522
    builder_.move_child(body, 0, parent, map_index);
4✔
523
    return true;
4✔
524
}
10✔
525

526
bool BlockHoisting::map_invariant_libnode_back(
12✔
527
    structured_control_flow::Sequence& parent,
528
    structured_control_flow::Map& map_stmt,
529
    structured_control_flow::Block& block
530
) {
531
    // For now, only allow libnode hoisting on sequential maps
532
    if (map_stmt.schedule_type().value() != ScheduleType_Sequential::value()) {
12✔
NEW
533
        return false;
×
534
    }
535

536
    auto& body = map_stmt.root();
12✔
537
    if (!this->is_invariant_libnode(body, block.dataflow(), map_stmt.indvar())) {
12✔
538
        return false;
10✔
539
    }
540

541
    size_t map_index = parent.index(map_stmt);
2✔
542
    builder_.move_child(body, body.size() - 1, parent, map_index + 1);
2✔
543
    return true;
2✔
544
}
12✔
545

546
bool BlockHoisting::for_invariant_front(structured_control_flow::Sequence& parent, structured_control_flow::For& for_stmt) {
15✔
547
    // Extract first child
548
    auto& body = for_stmt.root();
15✔
549
    if (body.size() == 0) {
15✔
NEW
550
        return false;
×
551
    }
552
    auto first_child = body.at(0);
15✔
553
    if (!first_child.second.assignments().empty()) {
15✔
NEW
554
        return false;
×
555
    }
556
    auto& first_node = first_child.first;
15✔
557

558
    auto* block = dynamic_cast<structured_control_flow::Block*>(&first_node);
15✔
559
    if (!block) {
15✔
UNCOV
560
        return false;
×
561
    }
562
    if (this->for_invariant_move(parent, for_stmt, *block)) {
15✔
563
        return true;
1✔
564
    }
565
    if (this->for_invariant_view(parent, for_stmt, *block)) {
14✔
566
        return true;
3✔
567
    }
568
    if (this->for_invariant_libnode_front(parent, for_stmt, *block)) {
11✔
569
        return true;
4✔
570
    }
571

572
    return false;
7✔
573
}
15✔
574

575
bool BlockHoisting::for_invariant_back(structured_control_flow::Sequence& parent, structured_control_flow::For& for_stmt) {
15✔
576
    // Extract last child
577
    auto& body = for_stmt.root();
15✔
578
    if (body.size() == 0) {
15✔
UNCOV
579
        return false;
×
580
    }
581
    auto last_child = body.at(body.size() - 1);
15✔
582
    if (!last_child.second.assignments().empty()) {
15✔
UNCOV
583
        return false;
×
584
    }
585
    auto& last_node = last_child.first;
15✔
586

587
    auto* block = dynamic_cast<structured_control_flow::Block*>(&last_node);
15✔
588
    if (!block) {
15✔
NEW
589
        return false;
×
590
    }
591
    return this->for_invariant_libnode_back(parent, for_stmt, *block);
15✔
592
}
15✔
593

594
bool BlockHoisting::for_invariant_move(
15✔
595
    structured_control_flow::Sequence& parent,
596
    structured_control_flow::For& for_stmt,
597
    structured_control_flow::Block& block
598
) {
599
    auto& body = for_stmt.root();
15✔
600
    if (!this->is_invariant_move(body, block.dataflow(), false)) {
15✔
601
        return false;
14✔
602
    }
603

604
    size_t for_index = parent.index(for_stmt);
1✔
605
    builder_.move_child(body, 0, parent, for_index);
1✔
606
    return true;
1✔
607
}
15✔
608

609
bool BlockHoisting::for_invariant_view(
14✔
610
    structured_control_flow::Sequence& parent,
611
    structured_control_flow::For& for_stmt,
612
    structured_control_flow::Block& block
613
) {
614
    auto& body = for_stmt.root();
14✔
615
    if (!this->is_invariant_view(body, block.dataflow(), for_stmt.indvar(), false)) {
14✔
616
        return false;
11✔
617
    }
618

619
    size_t for_index = parent.index(for_stmt);
3✔
620
    builder_.move_child(body, 0, parent, for_index);
3✔
621
    return true;
3✔
622
}
14✔
623

624
bool BlockHoisting::for_invariant_libnode_front(
11✔
625
    structured_control_flow::Sequence& parent,
626
    structured_control_flow::For& for_stmt,
627
    structured_control_flow::Block& block
628
) {
629
    auto& body = for_stmt.root();
11✔
630
    if (!this->is_invariant_libnode(body, block.dataflow(), for_stmt.indvar(), false)) {
11✔
631
        return false;
7✔
632
    }
633

634
    size_t map_index = parent.index(for_stmt);
4✔
635
    builder_.move_child(body, 0, parent, map_index);
4✔
636
    return true;
4✔
637
}
11✔
638

639
bool BlockHoisting::for_invariant_libnode_back(
15✔
640
    structured_control_flow::Sequence& parent,
641
    structured_control_flow::For& for_stmt,
642
    structured_control_flow::Block& block
643
) {
644
    auto& body = for_stmt.root();
15✔
645
    if (!this->is_invariant_libnode(body, block.dataflow(), for_stmt.indvar(), false)) {
15✔
646
        return false;
13✔
647
    }
648

649
    size_t map_index = parent.index(for_stmt);
2✔
650
    builder_.move_child(body, body.size() - 1, parent, map_index + 1);
2✔
651
    return true;
2✔
652
}
15✔
653

654
bool BlockHoisting::
655
    if_else_invariant_front(structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else) {
15✔
656
    // Extract the first block of first case
657
    if (if_else.at(0).first.size() == 0) {
15✔
UNCOV
658
        return false;
×
659
    }
660
    auto first_child = if_else.at(0).first.at(0);
15✔
661
    if (!first_child.second.assignments().empty()) {
15✔
NEW
662
        return false;
×
663
    }
664
    auto* first_block = dynamic_cast<structured_control_flow::Block*>(&first_child.first);
15✔
665
    if (!first_block) {
15✔
UNCOV
666
        return false;
×
667
    }
668

669
    // Collect the first block of all other cases
670
    std::vector<structured_control_flow::Block*> other_blocks;
15✔
671
    for (size_t i = 1; i < if_else.size(); i++) {
30✔
672
        if (if_else.at(i).first.size() == 0) {
15✔
NEW
673
            return false;
×
674
        }
675
        auto other_child = if_else.at(i).first.at(0);
15✔
676
        if (!other_child.second.assignments().empty()) {
15✔
NEW
677
            return false;
×
678
        }
679
        auto* other_block = dynamic_cast<structured_control_flow::Block*>(&other_child.first);
15✔
680
        if (!other_block) {
15✔
NEW
681
            return false;
×
682
        }
683
        other_blocks.push_back(other_block);
15✔
684
    }
15✔
685

686
    // Check the first block of all cases for invariant move / view / libnode
687
    if (this->is_invariant_move(if_else.at(0).first, first_block->dataflow())) {
15✔
688
        for (size_t i = 0; i < other_blocks.size(); i++) {
2✔
689
            if (!this->is_invariant_move(if_else.at(i + 1).first, other_blocks[i]->dataflow())) {
1✔
NEW
690
                return false;
×
691
            }
692
            if (!this->equal_moves(*first_block, *other_blocks[i])) {
1✔
NEW
693
                return false;
×
694
            }
695
        }
1✔
696
        this->if_else_extract_invariant_front(parent, if_else);
1✔
697
        return true;
1✔
698
    } else if (this->is_invariant_view(if_else.at(0).first, first_block->dataflow())) {
14✔
699
        for (size_t i = 0; i < other_blocks.size(); i++) {
6✔
700
            if (!this->is_invariant_view(if_else.at(i + 1).first, other_blocks[i]->dataflow())) {
3✔
NEW
701
                return false;
×
702
            }
703
            if (!this->equal_views(*first_block, *other_blocks[i])) {
3✔
NEW
704
                return false;
×
705
            }
706
        }
3✔
707
        this->if_else_extract_invariant_front(parent, if_else);
3✔
708
        return true;
3✔
709
    } else if (this->is_invariant_libnode(if_else.at(0).first, first_block->dataflow())) {
11✔
710
        for (size_t i = 0; i < other_blocks.size(); i++) {
8✔
711
            if (!this->is_invariant_libnode(if_else.at(i + 1).first, other_blocks[i]->dataflow())) {
4✔
NEW
712
                return false;
×
713
            }
714
            if (!this->equal_libnodes(*first_block, *other_blocks[i])) {
4✔
NEW
715
                return false;
×
716
            }
717
        }
4✔
718
        this->if_else_extract_invariant_libnode_front(parent, if_else);
4✔
719
        return true;
4✔
720
    }
721

722
    return false;
7✔
723
}
15✔
724

725
bool BlockHoisting::
726
    if_else_invariant_back(structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else) {
15✔
727
    // Extract the last block of first case
728
    if (if_else.at(0).first.size() == 0) {
15✔
NEW
729
        return false;
×
730
    }
731
    auto last_child = if_else.at(0).first.at(if_else.at(0).first.size() - 1);
15✔
732
    if (!last_child.second.assignments().empty()) {
15✔
NEW
733
        return false;
×
734
    }
735
    auto* last_block = dynamic_cast<structured_control_flow::Block*>(&last_child.first);
15✔
736
    if (!last_block) {
15✔
UNCOV
737
        return false;
×
738
    }
739

740
    // Collect the last block of all other cases
741
    std::vector<structured_control_flow::Block*> other_blocks;
15✔
742
    for (size_t i = 1; i < if_else.size(); i++) {
30✔
743
        if (if_else.at(i).first.size() == 0) {
15✔
NEW
744
            return false;
×
745
        }
746
        auto other_child = if_else.at(i).first.at(if_else.at(i).first.size() - 1);
15✔
747
        if (!other_child.second.assignments().empty()) {
15✔
NEW
748
            return false;
×
749
        }
750
        auto* other_block = dynamic_cast<structured_control_flow::Block*>(&other_child.first);
15✔
751
        if (!other_block) {
15✔
UNCOV
752
            return false;
×
753
        }
754
        other_blocks.push_back(other_block);
15✔
755
    }
15✔
756

757
    // Check the first block of all cases for invariant move / view / libnode
758
    if (this->is_invariant_libnode(if_else.at(0).first, last_block->dataflow())) {
15✔
759
        for (size_t i = 0; i < other_blocks.size(); i++) {
4✔
760
            if (!this->is_invariant_libnode(if_else.at(i + 1).first, other_blocks[i]->dataflow())) {
2✔
NEW
761
                return false;
×
762
            }
763
            if (!this->equal_libnodes(*last_block, *other_blocks[i])) {
2✔
NEW
764
                return false;
×
765
            }
766
        }
2✔
767
        this->if_else_extract_invariant_libnode_back(parent, if_else);
2✔
768
        return true;
2✔
769
    }
770

771
    return false;
13✔
772
}
15✔
773

774
void BlockHoisting::if_else_extract_invariant_front(
8✔
775
    structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else
776
) {
777
    size_t if_else_index = parent.index(if_else);
8✔
778
    builder_.move_child(if_else.at(0).first, 0, parent, if_else_index);
8✔
779

780
    for (size_t i = 1; i < if_else.size(); i++) {
16✔
781
        builder_.remove_child(if_else.at(i).first, 0);
8✔
782
    }
8✔
783
}
8✔
784

785
void BlockHoisting::if_else_extract_invariant_back(
2✔
786
    structured_control_flow::Sequence& parent, structured_control_flow::IfElse& if_else
787
) {
788
    size_t if_else_index = parent.index(if_else);
2✔
789
    builder_.move_child(if_else.at(0).first, if_else.at(0).first.size() - 1, parent, if_else_index + 1);
2✔
790

791
    for (size_t i = 1; i < if_else.size(); i++) {
4✔
792
        builder_.remove_child(if_else.at(i).first, if_else.at(i).first.size() - 1);
2✔
793
    }
2✔
794
}
2✔
795

796
} // namespace passes
797
} // 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

© 2025 Coveralls, Inc