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

daisytuner / docc / 28806128926

06 Jul 2026 04:16PM UTC coverage: 62.96%. First build
28806128926

push

github

web-flow
New LibNodeExpansion pass (#740)

* Switched expansion "pipeline" to new LibNodeExpansionPass that can recursively expand using a LibNodeExpander impl.
- removed access to analysis_manager from expansion methods, as those would currently not be appropriately maintained between nodes
+ New expansion API handles more of the boilerplate code (checking for standalone, creating boundary access nodes, removing old elements)
* Also migrated sdfg-json-to-c.cpp to not using the expansion pipeline anymore
* toStr() for ReduceNodes and giving PyStructuredSDFG access to the output_dir for additional dumping
~ DotVisualizer : Fix on nested sequences.
+ DotVisualizer: visualize empty sequences
* DotVisualizer default-enabled show block/loop ids
 * while MathNodes still have an expand-method, the new infrastructure is based upon "Expander" classes. The MathNodeExpander just redirects to the method for now
 ~ Broadcast node still used old ptr-output semantics
 * updated tensor & blas node expand to new expand API, that handles more of the boilerplate code (checking, removing old nodes, creating standalone-replacement nodes)
 - removed Transpose Node. Was unused and on old ptr-semantics
 + StructuredSDFGBuilder.add_sequence_at, add_for_at, add_map_at
 * switched StructuredSDFGBuilder internally to use ptr of Assignments to express using default assignments when needed
 * updated tests to use the new expand_single_math_node helper function, instead of the method directly
 + pass and expand-single helper functions are ready to use other expanders
 + EinsumExpansionPass is basically the legacy ExpansionPass, only restricted to EinsumNodes. It still needs to run in a pipeline to ensure finding multiple EinsumNodes per block. This is temporary, because Einsum is the only node that already supported splitting a block, which the new expansion disallows, as it should handle it itself when needed (but that part is not yet implemented)

594 of 962 new or added lines in 31 files covered. (61.75%)

40554 of 64412 relevant lines covered (62.96%)

963.11 hits per line

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

74.59
/sdfg/src/builder/structured_sdfg_builder.cpp
1
#include "sdfg/builder/structured_sdfg_builder.h"
2

3
#include <cstddef>
4

5
#include "sdfg/data_flow/library_node.h"
6
#include "sdfg/structured_control_flow/map.h"
7
#include "sdfg/structured_control_flow/sequence.h"
8
#include "sdfg/structured_control_flow/structured_loop.h"
9
#include "sdfg/types/utils.h"
10

11
#define TRAVERSE_CUTOFF 30
100✔
12

13
using namespace sdfg::control_flow;
14
using namespace sdfg::structured_control_flow;
15

16
namespace sdfg {
17
namespace builder {
18
std::unordered_set<const control_flow::State*> StructuredSDFGBuilder::
19
    determine_loop_nodes(SDFG& sdfg, const control_flow::State& start, const control_flow::State& end) const {
13✔
20
    std::unordered_set<const control_flow::State*> nodes;
13✔
21
    std::unordered_set<const control_flow::State*> visited;
13✔
22
    std::list<const control_flow::State*> queue = {&start};
13✔
23
    while (!queue.empty()) {
56✔
24
        auto curr = queue.front();
43✔
25
        queue.pop_front();
43✔
26
        if (visited.find(curr) != visited.end()) {
43✔
27
            continue;
1✔
28
        }
1✔
29
        visited.insert(curr);
42✔
30

31
        nodes.insert(curr);
42✔
32
        if (curr == &end) {
42✔
33
            continue;
13✔
34
        }
13✔
35

36
        for (auto& iedge : sdfg.in_edges(*curr)) {
30✔
37
            queue.push_back(&iedge.src());
30✔
38
        }
30✔
39
    }
29✔
40

41
    // Iteratively expand nodes to reduce frontier size
42
    auto dom_tree = sdfg.dominator_tree();
13✔
43
    auto dominates = [&](const control_flow::State* a, const control_flow::State* b) {
13✔
44
        const control_flow::State* curr = b;
12✔
45
        while (curr != nullptr) {
42✔
46
            if (curr == a) return true;
42✔
47
            if (dom_tree.find(curr) == dom_tree.end()) break;
30✔
48
            curr = dom_tree.at(curr);
30✔
49
        }
30✔
50
        return false;
×
51
    };
12✔
52

53
    // Identify header exits
54
    std::unordered_set<const control_flow::State*> stop_nodes;
13✔
55
    for (auto& edge : sdfg.out_edges(end)) {
24✔
56
        if (nodes.find(&edge.dst()) == nodes.end()) {
24✔
57
            stop_nodes.insert(&edge.dst());
11✔
58
        }
11✔
59
    }
24✔
60

61
    // If no header exits, check latch exits (Do-While)
62
    if (stop_nodes.empty()) {
13✔
63
        for (auto& edge : sdfg.out_edges(start)) {
4✔
64
            if (nodes.find(&edge.dst()) == nodes.end()) {
4✔
65
                stop_nodes.insert(&edge.dst());
2✔
66
            }
2✔
67
        }
4✔
68
    }
2✔
69

70
    // If still no exits, check any natural loop exit (e.g. infinite loop with break)
71
    if (stop_nodes.empty()) {
13✔
72
        for (auto node : nodes) {
×
73
            for (auto& edge : sdfg.out_edges(*node)) {
×
74
                if (nodes.find(&edge.dst()) == nodes.end()) {
×
75
                    stop_nodes.insert(&edge.dst());
×
76
                }
×
77
            }
×
78
        }
×
79
    }
×
80

81
    while (true) {
21✔
82
        std::unordered_set<const control_flow::State*> frontier;
21✔
83
        for (auto node : nodes) {
85✔
84
            for (auto& edge : sdfg.out_edges(*node)) {
139✔
85
                if (nodes.find(&edge.dst()) == nodes.end()) {
139✔
86
                    frontier.insert(&edge.dst());
40✔
87
                }
40✔
88
            }
139✔
89
        }
85✔
90

91
        bool changed = false;
21✔
92
        for (auto f : frontier) {
33✔
93
            // If f is a stop node, do not include it
94
            if (stop_nodes.find(f) != stop_nodes.end()) {
33✔
95
                continue;
21✔
96
            }
21✔
97
            // If f is dominated by the header, it belongs to the loop body (extended)
98
            if (dominates(&end, f)) {
12✔
99
                nodes.insert(f);
12✔
100
                changed = true;
12✔
101
            }
12✔
102
        }
12✔
103

104
        if (!changed) break;
21✔
105
    }
21✔
106

107
    return nodes;
13✔
108
};
13✔
109

110
bool post_dominates(
111
    const State* pdom,
112
    const State* node,
113
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree
114
) {
21✔
115
    if (pdom == node) {
21✔
116
        return true;
12✔
117
    }
12✔
118

119
    auto current = pdom_tree.at(node);
9✔
120
    while (current != nullptr) {
9✔
121
        if (current == pdom) {
3✔
122
            return true;
3✔
123
        }
3✔
124
        current = pdom_tree.at(current);
×
125
    }
×
126

127
    return false;
6✔
128
}
9✔
129

130

131
void StructuredSDFGBuilder::traverse(SDFG& sdfg) {
16✔
132
    // Start of SDFGS
133
    Sequence& root = *structured_sdfg_->root_;
16✔
134
    const State* start_state = &sdfg.start_state();
16✔
135

136
    auto pdom_tree = sdfg.post_dominator_tree();
16✔
137

138
    std::unordered_set<const InterstateEdge*> breaks;
16✔
139
    std::unordered_set<const InterstateEdge*> continues;
16✔
140
    for (auto& edge : sdfg.back_edges()) {
16✔
141
        continues.insert(edge);
13✔
142
    }
13✔
143

144
    this->current_traverse_loop_ = nullptr;
16✔
145
    std::unordered_set<const control_flow::State*> visited;
16✔
146
    this->structure_region(sdfg, root, start_state, nullptr, continues, breaks, pdom_tree, visited);
16✔
147
};
16✔
148

149
void StructuredSDFGBuilder::structure_region(
150
    SDFG& sdfg,
151
    Sequence& scope,
152
    const State* entry,
153
    const State* exit,
154
    const std::unordered_set<const InterstateEdge*>& continues,
155
    const std::unordered_set<const InterstateEdge*>& breaks,
156
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree,
157
    std::unordered_set<const control_flow::State*>& visited,
158
    bool is_loop_body
159
) {
64✔
160
    const State* current = entry;
64✔
161
    while (current != exit) {
138✔
162
        if (current == nullptr) {
100✔
163
            break;
×
164
        }
×
165

166

167
        // Cutoff
168
        if (this->function().element_counter_ > sdfg.states().size() * TRAVERSE_CUTOFF) {
100✔
169
            throw UnstructuredControlFlowException();
×
170
        }
×
171

172
        if (visited.find(current) != visited.end()) {
100✔
173
            throw UnstructuredControlFlowException();
×
174
        }
×
175
        visited.insert(current);
100✔
176

177
        // Loop detection
178
        bool is_loop_header = false;
100✔
179
        if (!is_loop_body || current != entry) {
100✔
180
            for (auto& iedge : sdfg.in_edges(*current)) {
100✔
181
                if (continues.find(&iedge) != continues.end()) {
100✔
182
                    is_loop_header = true;
9✔
183
                    break;
9✔
184
                }
9✔
185
            }
100✔
186
        }
91✔
187

188
        if (is_loop_header) {
100✔
189
            // 1. Determine nodes of loop body
190
            std::unordered_set<const InterstateEdge*> loop_edges;
9✔
191
            for (auto& iedge : sdfg.in_edges(*current)) {
22✔
192
                if (continues.find(&iedge) != continues.end()) {
22✔
193
                    loop_edges.insert(&iedge);
13✔
194
                }
13✔
195
            }
22✔
196

197
            std::unordered_set<const control_flow::State*> body;
9✔
198
            for (auto back_edge : loop_edges) {
13✔
199
                auto loop_nodes = this->determine_loop_nodes(sdfg, back_edge->src(), back_edge->dst());
13✔
200
                body.insert(loop_nodes.begin(), loop_nodes.end());
13✔
201
            }
13✔
202

203
            // 2. Determine exit states and exit edges
204
            std::unordered_set<const control_flow::State*> exit_states;
9✔
205
            std::unordered_set<const control_flow::InterstateEdge*> exit_edges;
9✔
206
            for (auto node : body) {
34✔
207
                for (auto& edge : sdfg.out_edges(*node)) {
52✔
208
                    if (body.find(&edge.dst()) == body.end()) {
52✔
209
                        if (continues.find(&edge) != continues.end()) {
12✔
210
                            continue;
×
211
                        }
×
212
                        exit_edges.insert(&edge);
12✔
213
                        exit_states.insert(&edge.dst());
12✔
214
                    }
12✔
215
                }
52✔
216
            }
34✔
217

218
            if (exit_states.size() > 1) {
9✔
219
                std::unordered_set<const control_flow::State*> non_return_exits;
×
220
                for (auto s : exit_states) {
×
221
                    if (dynamic_cast<const control_flow::ReturnState*>(s)) {
×
222
                        continue;
×
223
                    }
×
224
                    if (sdfg.out_degree(*s) > 0) {
×
225
                        non_return_exits.insert(s);
×
226
                    }
×
227
                }
×
228
                if (non_return_exits.size() == 1) {
×
229
                    exit_states = non_return_exits;
×
230
                }
×
231
            }
×
232

233
            if (exit_states.size() != 1) {
9✔
234
                throw UnstructuredControlFlowException();
×
235
            }
×
236
            const control_flow::State* exit_state = *exit_states.begin();
9✔
237

238
            for (auto& edge : breaks) {
9✔
239
                exit_edges.insert(edge);
1✔
240
            }
1✔
241

242
            // Collect debug information
243
            DebugInfo dbg_info = current->debug_info();
9✔
244
            for (auto& edge : sdfg.in_edges(*current)) {
22✔
245
                dbg_info = DebugInfo::merge(dbg_info, edge.debug_info());
22✔
246
            }
22✔
247
            for (auto node : body) {
34✔
248
                dbg_info = DebugInfo::merge(dbg_info, node->debug_info());
34✔
249
            }
34✔
250
            for (auto edge : exit_edges) {
13✔
251
                dbg_info = DebugInfo::merge(dbg_info, edge->debug_info());
13✔
252
            }
13✔
253

254
            // 3. Add while loop
255
            While& loop = this->add_while(scope, {}, dbg_info);
9✔
256
            auto last_loop_ = this->current_traverse_loop_;
9✔
257
            this->current_traverse_loop_ = &loop;
9✔
258

259
            std::unordered_set<const control_flow::State*> loop_visited(visited);
9✔
260
            loop_visited.erase(current);
9✔
261

262
            this->structure_region(
9✔
263
                sdfg, loop.root(), current, exit_state, continues, exit_edges, pdom_tree, loop_visited, true
9✔
264
            );
9✔
265
            this->current_traverse_loop_ = last_loop_;
9✔
266

267
            current = exit_state;
9✔
268
            continue;
9✔
269
        }
9✔
270

271
        auto out_edges = sdfg.out_edges(*current);
91✔
272
        auto out_degree = sdfg.out_degree(*current);
91✔
273

274
        // Case 1: Sink node
275
        if (out_degree == 0) {
91✔
276
            if (!std::ranges::empty(current->dataflow().nodes())) {
17✔
277
                this->add_block(scope, current->dataflow(), {}, current->debug_info());
×
278
            }
×
279

280
            auto return_state = dynamic_cast<const control_flow::ReturnState*>(current);
17✔
281
            assert(return_state != nullptr);
17✔
282
            if (return_state->is_data()) {
17✔
283
                this->add_return(scope, return_state->data(), {}, return_state->debug_info());
17✔
284
            } else if (return_state->is_constant()) {
17✔
285
                this->add_constant_return(
×
286
                    scope, return_state->data(), return_state->type(), {}, return_state->debug_info()
×
287
                );
×
288
            } else {
×
289
                assert(false && "Unknown return state type");
×
290
            }
×
291

292
            break;
17✔
293
        }
17✔
294

295
        // Case 2: Transition
296
        if (out_degree == 1) {
74✔
297
            auto& oedge = *out_edges.begin();
45✔
298
            if (!oedge.is_unconditional()) {
45✔
299
                throw UnstructuredControlFlowException();
×
300
            }
×
301

302
            if (!std::ranges::empty(current->dataflow().nodes()) || !oedge.assignments().empty()) {
45✔
303
                this->add_block(scope, current->dataflow(), oedge.assignments(), current->debug_info());
10✔
304
            }
10✔
305

306
            if (continues.find(&oedge) != continues.end()) {
45✔
307
                if (this->current_traverse_loop_ == nullptr) {
7✔
308
                    throw UnstructuredControlFlowException();
×
309
                }
×
310
                this->add_continue(scope, {}, oedge.debug_info());
7✔
311
                break;
7✔
312
            } else if (breaks.find(&oedge) != breaks.end()) {
38✔
313
                if (this->current_traverse_loop_ == nullptr) {
1✔
314
                    throw UnstructuredControlFlowException();
×
315
                }
×
316
                this->add_break(scope, {}, oedge.debug_info());
1✔
317
                break;
1✔
318
            } else {
37✔
319
                current = &oedge.dst();
37✔
320
            }
37✔
321
            continue;
37✔
322
        }
45✔
323

324
        // Case 3: Branches
325
        if (out_degree > 1) {
29✔
326
            if (!std::ranges::empty(current->dataflow().nodes())) {
29✔
327
                this->add_block(scope, current->dataflow(), {}, current->debug_info());
×
328
            }
×
329

330
            // Determine Merge Point
331
            const State* merge = nullptr;
29✔
332
            if (pdom_tree.find(current) != pdom_tree.end()) {
29✔
333
                merge = pdom_tree.at(current);
29✔
334
            }
29✔
335

336

337
            // If merge is beyond exit, clamp to exit
338
            if (exit != nullptr && merge != nullptr) {
29✔
339
                if (post_dominates(merge, exit, pdom_tree)) {
21✔
340
                    merge = exit;
15✔
341
                }
15✔
342
            }
21✔
343

344
            if (merge != nullptr && visited.find(merge) != visited.end()) {
29✔
345
                merge = exit;
4✔
346
            }
4✔
347

348
            if (merge == nullptr && exit != nullptr) {
29✔
349
                merge = exit;
×
350
            }
×
351

352
            auto& if_else = this->add_if_else(scope, {}, current->debug_info());
29✔
353
            for (auto& out_edge : out_edges) {
57✔
354
                auto& branch = this->add_case(if_else, out_edge.condition(), out_edge.debug_info());
57✔
355
                if (!out_edge.assignments().empty()) {
57✔
356
                    this->add_block(branch, out_edge.assignments(), out_edge.debug_info());
5✔
357
                }
5✔
358
                if (continues.find(&out_edge) != continues.end()) {
57✔
359
                    if (this->current_traverse_loop_ == nullptr) {
7✔
360
                        throw UnstructuredControlFlowException();
1✔
361
                    }
1✔
362
                    this->add_continue(branch, {}, out_edge.debug_info());
6✔
363
                } else if (breaks.find(&out_edge) != breaks.end()) {
50✔
364
                    if (this->current_traverse_loop_ == nullptr) {
11✔
365
                        throw UnstructuredControlFlowException();
×
366
                    }
×
367
                    this->add_break(branch, {}, out_edge.debug_info());
11✔
368
                } else {
39✔
369
                    std::unordered_set<const control_flow::State*> branch_visited(visited);
39✔
370
                    this->structure_region(
39✔
371
                        sdfg, branch, &out_edge.dst(), merge, continues, breaks, pdom_tree, branch_visited
39✔
372
                    );
39✔
373
                }
39✔
374
            }
57✔
375

376
            current = merge;
28✔
377
            continue;
28✔
378
        }
29✔
379
    }
29✔
380
}
64✔
381

382
Function& StructuredSDFGBuilder::function() const { return static_cast<Function&>(*this->structured_sdfg_); };
59,536✔
383

384
StructuredSDFGBuilder::StructuredSDFGBuilder(StructuredSDFG& sdfg)
385
    : FunctionBuilder(), structured_sdfg_(&sdfg, owned(false)) {};
×
386

387
StructuredSDFGBuilder::StructuredSDFGBuilder(std::unique_ptr<StructuredSDFG>& sdfg)
388
    : FunctionBuilder(), structured_sdfg_(sdfg.release(), owned(true)) {};
302✔
389

390
StructuredSDFGBuilder::StructuredSDFGBuilder(const std::string& name, FunctionType type)
391
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(name, type), owned(true)) {};
1,934✔
392

393
StructuredSDFGBuilder::StructuredSDFGBuilder(const std::string& name, FunctionType type, const types::IType& return_type)
394
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(name, type, return_type), owned(true)) {};
38✔
395

396
StructuredSDFGBuilder::StructuredSDFGBuilder(SDFG& sdfg)
397
    : FunctionBuilder(),
16✔
398
      structured_sdfg_(new StructuredSDFG(sdfg.name(), sdfg.type(), sdfg.return_type()), owned(true)) {
16✔
399
    for (auto& entry : sdfg.structures_) {
16✔
400
        this->structured_sdfg_->structures_.insert({entry.first, entry.second->clone()});
×
401
    }
×
402

403
    for (auto& entry : sdfg.containers_) {
27✔
404
        this->structured_sdfg_->containers_.insert({entry.first, entry.second->clone()});
27✔
405
    }
27✔
406

407
    for (auto& arg : sdfg.arguments_) {
16✔
408
        this->structured_sdfg_->arguments_.push_back(arg);
2✔
409
    }
2✔
410

411
    for (auto& ext : sdfg.externals_) {
16✔
412
        this->structured_sdfg_->externals_.push_back(ext);
1✔
413
        this->structured_sdfg_->externals_linkage_types_[ext] = sdfg.linkage_type(ext);
1✔
414
    }
1✔
415

416
    for (auto& entry : sdfg.assumptions_) {
25✔
417
        this->structured_sdfg_->assumptions_.insert({entry.first, entry.second});
25✔
418
    }
25✔
419

420
    for (auto& entry : sdfg.metadata_) {
16✔
421
        this->structured_sdfg_->metadata_[entry.first] = entry.second;
2✔
422
    }
2✔
423

424
    this->traverse(sdfg);
16✔
425
};
16✔
426

427
StructuredSDFG& StructuredSDFGBuilder::subject() const { return *this->structured_sdfg_; };
6,919✔
428

429
std::unique_ptr<StructuredSDFG> StructuredSDFGBuilder::move() {
570✔
430
#ifndef NDEBUG
570✔
431
    this->structured_sdfg_->validate();
570✔
432
#endif
570✔
433

434
    if (!structured_sdfg_.get_deleter().should_delete_) {
570✔
435
        throw InvalidSDFGException("StructuredSDFGBuilder: Cannot move a non-owned SDFG");
×
436
    }
×
437

438
    return std::move(std::unique_ptr<StructuredSDFG>(structured_sdfg_.release()));
570✔
439
};
570✔
440

441
void StructuredSDFGBuilder::rename_container(const std::string& old_name, const std::string& new_name) const {
26✔
442
    FunctionBuilder::rename_container(old_name, new_name);
26✔
443

444
    this->structured_sdfg_->root_->replace(symbolic::symbol(old_name), symbolic::symbol(new_name));
26✔
445
};
26✔
446

447
Element* StructuredSDFGBuilder::find_element_by_id(const size_t& element_id) const {
182✔
448
    auto& sdfg = this->subject();
182✔
449
    std::list<Element*> queue = {&sdfg.root()};
182✔
450
    while (!queue.empty()) {
1,599✔
451
        auto current = queue.front();
1,595✔
452
        queue.pop_front();
1,595✔
453

454
        if (current->element_id() == element_id) {
1,595✔
455
            return current;
178✔
456
        }
178✔
457

458
        if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(current)) {
1,417✔
459
            auto& dataflow = block_stmt->dataflow();
55✔
460
            for (auto& node : dataflow.nodes()) {
186✔
461
                queue.push_back(&node);
186✔
462
            }
186✔
463
            for (auto& edge : dataflow.edges()) {
131✔
464
                queue.push_back(&edge);
131✔
465
            }
131✔
466
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
1,362✔
467
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
1,050✔
468
                queue.push_back(&sequence_stmt->at(i).first);
559✔
469
                queue.push_back(&sequence_stmt->at(i).second);
559✔
470
            }
559✔
471
        } else if (dynamic_cast<structured_control_flow::Return*>(current)) {
871✔
472
            // Do nothing
473
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
871✔
474
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
475
                queue.push_back(&if_else_stmt->at(i).first);
×
476
            }
×
477
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
871✔
478
            queue.push_back(&for_stmt->root());
215✔
479
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
656✔
480
            queue.push_back(&while_stmt->root());
×
481
        } else if (dynamic_cast<structured_control_flow::Continue*>(current)) {
656✔
482
            // Do nothing
483
        } else if (dynamic_cast<structured_control_flow::Break*>(current)) {
656✔
484
            // Do nothing
485
        } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(current)) {
656✔
486
            queue.push_back(&map_stmt->root());
116✔
487
        } else if (auto reduce_stmt = dynamic_cast<structured_control_flow::Reduce*>(current)) {
540✔
488
            queue.push_back(&reduce_stmt->root());
×
489
        }
×
490
    }
1,417✔
491

492
    return nullptr;
4✔
493
};
182✔
494

495
Sequence& StructuredSDFGBuilder::
496
    add_sequence(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
29✔
497
    return insert_node_internal<Sequence>(parent, INSERT_AT_END, &assignments, debug_info).first;
29✔
498
}
29✔
499

500
Sequence& StructuredSDFGBuilder::add_sequence_before(
501
    Sequence& parent,
502
    ControlFlowNode& child,
503
    const sdfg::control_flow::Assignments& assignments,
504
    const DebugInfo& debug_info
505
) {
55✔
506
    int index = parent.index(child);
55✔
507
    if (index == -1) {
55✔
508
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
509
    }
×
510

511
    return insert_node_internal<Sequence>(parent, index, &assignments, debug_info).first;
55✔
512
}
55✔
513

514
Sequence& StructuredSDFGBuilder::add_sequence_after(
515
    Sequence& parent,
516
    ControlFlowNode& child,
517
    const sdfg::control_flow::Assignments& assignments,
518
    const DebugInfo& debug_info
519
) {
24✔
520
    int index = parent.index(child);
24✔
521
    if (index == -1) {
24✔
522
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
523
    }
×
524

525
    return insert_node_internal<Sequence>(parent, index + 1, &assignments, debug_info).first;
24✔
526
}
24✔
527

528
Sequence& StructuredSDFGBuilder::add_sequence_at(
529
    Sequence& parent,
530
    InsertionPoint insertion_point,
531
    const DebugInfo& debug_info,
532
    const sdfg::control_flow::Assignments* assignments
533
) {
589✔
534
    return insert_node_internal<Sequence>(parent, insertion_point, assignments, debug_info).first;
589✔
535
}
589✔
536

537
std::pair<Sequence&, Transition&> StructuredSDFGBuilder::
538
    add_sequence_before(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
539
    int index = parent.index(child);
×
540
    if (index == -1) {
×
541
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
542
    }
×
543

544
    return insert_node_internal<Sequence>(parent, index, {}, debug_info);
×
545
}
×
546

547
void StructuredSDFGBuilder::remove_from_parent(ControlFlowNode& child) {
57✔
548
    auto* parent = dynamic_cast<structured_control_flow::Sequence*>(child.get_parent());
57✔
549
    if (parent == nullptr) {
57✔
550
        throw InvalidSDFGException(
×
551
            "StructuredSDFGBuilder: Child has no sequence parent: #" + std::to_string(child.element_id())
×
552
        );
×
553
    }
×
554
    auto idx = parent->index(child);
57✔
555
    if (idx < 0) {
57✔
556
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found in parent");
×
557
    }
×
558
    remove_child(*parent, idx);
57✔
559
}
57✔
560

561
void StructuredSDFGBuilder::remove_child(Sequence& parent, size_t index) {
988✔
562
    parent.children_.erase(parent.children_.begin() + index);
988✔
563
    parent.transitions_.erase(parent.transitions_.begin() + index);
988✔
564
};
988✔
565

566
void StructuredSDFGBuilder::remove_children(Sequence& parent) {
×
567
    parent.children_.clear();
×
568
    parent.transitions_.clear();
×
569
};
×
570

571
void StructuredSDFGBuilder::move_child(Sequence& source, size_t source_index, Sequence& target) {
128✔
572
    size_t target_index = target.size();
128✔
573
    if (&source == &target) {
128✔
574
        target_index--;
×
575
    }
×
576
    this->move_child(source, source_index, target, target_index);
128✔
577
};
128✔
578

579
void StructuredSDFGBuilder::move_child(Sequence& source, size_t source_index, Sequence& target, size_t target_index) {
157✔
580
    auto node_ptr = std::move(source.children_.at(source_index));
157✔
581
    auto trans_ptr = std::move(source.transitions_.at(source_index));
157✔
582
    source.children_.erase(source.children_.begin() + source_index);
157✔
583
    source.transitions_.erase(source.transitions_.begin() + source_index);
157✔
584

585
    node_ptr->parent_ = &target;
157✔
586
    trans_ptr->parent_ = &target;
157✔
587
    target.children_.insert(target.children_.begin() + target_index, std::move(node_ptr));
157✔
588
    target.transitions_.insert(target.transitions_.begin() + target_index, std::move(trans_ptr));
157✔
589
};
157✔
590

591
void StructuredSDFGBuilder::move_children(Sequence& source, Sequence& target) {
274✔
592
    this->move_children(source, target, target.size());
274✔
593
};
274✔
594

595
void StructuredSDFGBuilder::move_children(Sequence& source, Sequence& target, size_t target_index) {
341✔
596
    target.children_.insert(
341✔
597
        target.children_.begin() + target_index,
341✔
598
        std::make_move_iterator(source.children_.begin()),
341✔
599
        std::make_move_iterator(source.children_.end())
341✔
600
    );
341✔
601
    target.transitions_.insert(
341✔
602
        target.transitions_.begin() + target_index,
341✔
603
        std::make_move_iterator(source.transitions_.begin()),
341✔
604
        std::make_move_iterator(source.transitions_.end())
341✔
605
    );
341✔
606
    for (auto& child : target.children_) {
702✔
607
        child->parent_ = &target;
702✔
608
    }
702✔
609
    for (auto& trans : target.transitions_) {
702✔
610
        trans->parent_ = &target;
702✔
611
    }
702✔
612
    source.children_.clear();
341✔
613
    source.transitions_.clear();
341✔
614
};
341✔
615

616
Sequence& StructuredSDFGBuilder::hoist_root() {
×
617
    auto current_root = std::move(this->structured_sdfg_->root_);
×
618

619
    this->structured_sdfg_->root_ =
×
620
        std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), current_root->debug_info(), nullptr));
×
621

622
    current_root->parent_ = this->structured_sdfg_->root_.get();
×
623
    this->structured_sdfg_->root_->children_.push_back(std::move(current_root));
×
624
    this->structured_sdfg_->root_->transitions_.push_back(std::unique_ptr<Transition>(
×
625
        new Transition(this->new_element_id(), current_root->debug_info(), *this->structured_sdfg_->root_)
×
626
    ));
×
627
    return *this->structured_sdfg_->root_;
×
628
};
×
629

630
Block& StructuredSDFGBuilder::
631
    add_block(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
3,618✔
632
    return insert_block_internal(parent, INSERT_AT_END, nullptr, &assignments, debug_info).first;
3,618✔
633
}
3,618✔
634

635
Block& StructuredSDFGBuilder::add_block(
636
    Sequence& parent,
637
    const data_flow::DataFlowGraph& data_flow_graph,
638
    const sdfg::control_flow::Assignments& assignments,
639
    const DebugInfo& debug_info
640
) {
19✔
641
    return insert_block_internal(parent, INSERT_AT_END, &data_flow_graph, &assignments, debug_info).first;
19✔
642
}
19✔
643

644
std::pair<Block&, Transition&> StructuredSDFGBuilder::insert_block_internal(
645
    Sequence& parent,
646
    int32_t insert_idx,
647
    const data_flow::DataFlowGraph* import_from,
648
    const sdfg::control_flow::Assignments* assignments,
649
    const DebugInfo& debug_info
650
) {
3,977✔
651
    auto new_pair = insert_node_internal<Block>(parent, insert_idx, assignments, debug_info);
3,977✔
652

653
    if (import_from) {
3,977✔
654
        this->add_dataflow(*import_from, new_pair.first);
19✔
655
    }
19✔
656

657
    return new_pair;
3,977✔
658
}
3,977✔
659

660
Block& StructuredSDFGBuilder::add_block_before(
661
    Sequence& parent,
662
    ControlFlowNode& child,
663
    const sdfg::control_flow::Assignments& assignments,
664
    const DebugInfo& debug_info
665
) {
164✔
666
    int index = parent.index(child);
164✔
667
    if (index == -1) {
164✔
668
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
669
    }
×
670

671
    return insert_block_internal(parent, index, nullptr, &assignments, debug_info).first;
164✔
672
};
164✔
673

674
Block& StructuredSDFGBuilder::add_block_before(
675
    Sequence& parent,
676
    ControlFlowNode& child,
677
    data_flow::DataFlowGraph& data_flow_graph,
678
    const sdfg::control_flow::Assignments& assignments,
679
    const DebugInfo& debug_info
680
) {
×
681
    int index = parent.index(child);
×
682
    if (index == -1) {
×
683
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
684
    }
×
685

NEW
686
    return insert_block_internal(parent, index, &data_flow_graph, &assignments, debug_info).first;
×
687
};
×
688

689
Block& StructuredSDFGBuilder::add_block_after(
690
    Sequence& parent,
691
    ControlFlowNode& child,
692
    const sdfg::control_flow::Assignments& assignments,
693
    const DebugInfo& debug_info
694
) {
176✔
695
    int index = parent.index(child);
176✔
696
    if (index == -1) {
176✔
697
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
698
    }
×
699

700
    return insert_block_internal(parent, index + 1, nullptr, &assignments, debug_info).first;
176✔
701
}
176✔
702

703
Block& StructuredSDFGBuilder::add_block_after(
704
    Sequence& parent,
705
    ControlFlowNode& child,
706
    data_flow::DataFlowGraph& data_flow_graph,
707
    const sdfg::control_flow::Assignments& assignments,
708
    const DebugInfo& debug_info
709
) {
×
710
    int index = parent.index(child);
×
711
    if (index == -1) {
×
712
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
713
    }
×
714

NEW
715
    return insert_block_internal(parent, index + 1, &data_flow_graph, &assignments, debug_info).first;
×
NEW
716
}
×
717

718
Block& StructuredSDFGBuilder::add_block_at(
719
    Sequence& parent,
720
    InsertionPoint insertion_point,
721
    const DebugInfo& debug_info,
722
    const sdfg::control_flow::Assignments* assignments
NEW
723
) {
×
NEW
724
    return insert_block_internal(parent, insertion_point, nullptr, assignments, debug_info).first;
×
725
}
×
726

727
std::pair<Block&, Transition&> StructuredSDFGBuilder::
728
    add_block_before(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
729
    int index = parent.index(child);
×
730
    if (index == -1) {
×
731
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
732
    }
×
733

734
    return insert_block_internal(parent, index, nullptr, {}, debug_info);
×
735
}
×
736

737
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
738
    Sequence& parent, ControlFlowNode& child, data_flow::DataFlowGraph& data_flow_graph, const DebugInfo& debug_info
739
) {
×
740
    int index = parent.index(child);
×
741
    if (index == -1) {
×
742
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
743
    }
×
744

745
    return insert_block_internal(parent, index, &data_flow_graph, {}, debug_info);
×
746
}
×
747

748
std::pair<Block&, Transition&> StructuredSDFGBuilder::
749
    add_block_after(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
750
    int index = parent.index(child);
×
751
    if (index == -1) {
×
752
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
753
    }
×
754

755
    return insert_block_internal(parent, index + 1, nullptr, {}, debug_info);
×
756
}
×
757

758
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(
759
    Sequence& parent, ControlFlowNode& child, data_flow::DataFlowGraph& data_flow_graph, const DebugInfo& debug_info
760
) {
×
761
    int index = parent.index(child);
×
762
    if (index == -1) {
×
763
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
764
    }
×
765

766
    return insert_block_internal(parent, index + 1, &data_flow_graph, {}, debug_info);
×
767
}
×
768

769
IfElse& StructuredSDFGBuilder::
770
    add_if_else(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
149✔
771
    return insert_node_internal<IfElse>(parent, INSERT_AT_END, &assignments, debug_info).first;
149✔
772
}
149✔
773

774
IfElse& StructuredSDFGBuilder::add_if_else_before(
775
    Sequence& parent,
776
    ControlFlowNode& child,
777
    const sdfg::control_flow::Assignments& assignments,
778
    const DebugInfo& debug_info
779
) {
34✔
780
    int index = parent.index(child);
34✔
781
    if (index == -1) {
34✔
782
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
783
    }
×
784

785
    return insert_node_internal<IfElse>(parent, index, &assignments, debug_info).first;
34✔
786
}
34✔
787

788
IfElse& StructuredSDFGBuilder::add_if_else_after(
789
    Sequence& parent,
790
    ControlFlowNode& child,
791
    const sdfg::control_flow::Assignments& assignments,
792
    const DebugInfo& debug_info
793
) {
×
794
    int index = parent.index(child);
×
795
    if (index == -1) {
×
796
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
797
    }
×
798

NEW
799
    return insert_node_internal<IfElse>(parent, index + 1, &assignments, debug_info).first;
×
800
}
×
801

802
std::pair<IfElse&, Transition&> StructuredSDFGBuilder::
803
    add_if_else_before(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
804
    int index = parent.index(child);
×
805
    if (index == -1) {
×
806
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
807
    }
×
808

809
    return insert_node_internal<IfElse>(parent, INSERT_AT_END, {}, debug_info);
×
810
};
×
811

812
Sequence& StructuredSDFGBuilder::add_case(IfElse& scope, const sdfg::symbolic::Condition cond, const DebugInfo& debug_info) {
285✔
813
    scope.cases_.push_back(std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info, &scope)));
285✔
814

815
    scope.conditions_.push_back(cond);
285✔
816
    return *scope.cases_.back();
285✔
817
};
285✔
818

819
void StructuredSDFGBuilder::remove_case(IfElse& scope, size_t index, const DebugInfo& debug_info) {
×
820
    scope.cases_.erase(scope.cases_.begin() + index);
×
821
    scope.conditions_.erase(scope.conditions_.begin() + index);
×
822
};
×
823

824
While& StructuredSDFGBuilder::
825
    add_while(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
53✔
826
    return insert_node_internal<While>(parent, INSERT_AT_END, &assignments, debug_info).first;
53✔
827
};
53✔
828

829
For& StructuredSDFGBuilder::add_for(
830
    Sequence& parent,
831
    const symbolic::Symbol indvar,
832
    const symbolic::Condition condition,
833
    const symbolic::Expression init,
834
    const symbolic::Expression update,
835
    const sdfg::control_flow::Assignments& assignments,
836
    const DebugInfo& debug_info
837
) {
837✔
838
    return insert_node_internal<For>(parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition)
837✔
839
        .first;
837✔
840
}
837✔
841

842
For& StructuredSDFGBuilder::add_for_before(
843
    Sequence& parent,
844
    ControlFlowNode& child,
845
    const symbolic::Symbol indvar,
846
    const symbolic::Condition condition,
847
    const symbolic::Expression init,
848
    const symbolic::Expression update,
849
    const sdfg::control_flow::Assignments& assignments,
850
    const DebugInfo& debug_info
851
) {
40✔
852
    int index = parent.index(child);
40✔
853
    if (index == -1) {
40✔
854
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
855
    }
×
856

857
    return insert_node_internal<For>(parent, index, &assignments, debug_info, indvar, init, update, condition).first;
40✔
858
}
40✔
859

860
For& StructuredSDFGBuilder::add_for_after(
861
    Sequence& parent,
862
    ControlFlowNode& child,
863
    const symbolic::Symbol indvar,
864
    const symbolic::Condition condition,
865
    const symbolic::Expression init,
866
    const symbolic::Expression update,
867
    const sdfg::control_flow::Assignments& assignments,
868
    const DebugInfo& debug_info
869
) {
62✔
870
    int index = parent.index(child);
62✔
871
    if (index == -1) {
62✔
872
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
873
    }
×
874

875
    return insert_node_internal<For>(parent, index + 1, &assignments, debug_info, indvar, init, update, condition).first;
62✔
876
}
62✔
877

878
For& StructuredSDFGBuilder::add_for_at(
879
    Sequence& parent,
880
    InsertionPoint insertion_point,
881
    const symbolic::Symbol indvar,
882
    const symbolic::Condition condition,
883
    const symbolic::Expression init,
884
    const symbolic::Expression update,
885
    const ScheduleType& schedule_type,
886
    const DebugInfo& debug_info,
887
    const sdfg::control_flow::Assignments* assignments
NEW
888
) {
×
NEW
889
    return insert_node_internal<For>(parent, insertion_point, assignments, debug_info, indvar, init, update, condition)
×
NEW
890
        .first;
×
891
}
×
892

893
Map& StructuredSDFGBuilder::add_map(
894
    Sequence& parent,
895
    const symbolic::Symbol indvar,
896
    const symbolic::Condition condition,
897
    const symbolic::Expression init,
898
    const symbolic::Expression update,
899
    const ScheduleType& schedule_type,
900
    const sdfg::control_flow::Assignments& assignments,
901
    const DebugInfo& debug_info
902
) {
2,284✔
903
    return insert_node_internal<
2,284✔
904
               Map>(parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition, schedule_type)
2,284✔
905
        .first;
2,284✔
906
}
2,284✔
907

908
Map& StructuredSDFGBuilder::add_map_before(
909
    Sequence& parent,
910
    ControlFlowNode& child,
911
    const symbolic::Symbol indvar,
912
    const symbolic::Condition condition,
913
    const symbolic::Expression init,
914
    const symbolic::Expression update,
915
    const ScheduleType& schedule_type,
916
    const sdfg::control_flow::Assignments& assignments,
917
    const DebugInfo& debug_info
918
) {
72✔
919
    int index = parent.index(child);
72✔
920
    if (index == -1) {
72✔
921
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
922
    }
×
923

924
    return insert_node_internal<
72✔
925
               Map>(parent, index, &assignments, debug_info, indvar, init, update, condition, schedule_type)
72✔
926
        .first;
72✔
927
}
72✔
928

929
Map& StructuredSDFGBuilder::add_map_after(
930
    Sequence& parent,
931
    ControlFlowNode& child,
932
    const symbolic::Symbol indvar,
933
    const symbolic::Condition condition,
934
    const symbolic::Expression init,
935
    const symbolic::Expression update,
936
    const ScheduleType& schedule_type,
937
    const sdfg::control_flow::Assignments& assignments,
938
    const DebugInfo& debug_info
939
) {
85✔
940
    int index = parent.index(child);
85✔
941
    if (index == -1) {
85✔
942
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
943
    }
×
944

945
    return insert_node_internal<
85✔
946
               Map>(parent, index + 1, &assignments, debug_info, indvar, init, update, condition, schedule_type)
85✔
947
        .first;
85✔
948
}
85✔
949

950
Map& StructuredSDFGBuilder::add_map_at(
951
    Sequence& parent,
952
    InsertionPoint insertion_point,
953
    const symbolic::Symbol indvar,
954
    const symbolic::Condition condition,
955
    const symbolic::Expression init,
956
    const symbolic::Expression update,
957
    const ScheduleType& schedule_type,
958
    const DebugInfo& debug_info,
959
    const sdfg::control_flow::Assignments* assignments
NEW
960
) {
×
NEW
961
    return insert_node_internal<
×
NEW
962
               Map>(parent, insertion_point, assignments, debug_info, indvar, init, update, condition, schedule_type)
×
963
        .first;
×
964
}
×
965

966
Reduce& StructuredSDFGBuilder::add_reduce(
967
    Sequence& parent,
968
    const symbolic::Symbol indvar,
969
    const symbolic::Condition condition,
970
    const symbolic::Expression init,
971
    const symbolic::Expression update,
972
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
973
    const ScheduleType& schedule_type,
974
    const sdfg::control_flow::Assignments& assignments,
975
    const DebugInfo& debug_info
976
) {
14✔
977
    return insert_node_internal<Reduce>(
14✔
978
               parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type
14✔
979
    )
14✔
980
        .first;
14✔
981
}
14✔
982

983
Reduce& StructuredSDFGBuilder::add_reduce_before(
984
    Sequence& parent,
985
    ControlFlowNode& child,
986
    const symbolic::Symbol indvar,
987
    const symbolic::Condition condition,
988
    const symbolic::Expression init,
989
    const symbolic::Expression update,
990
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
991
    const ScheduleType& schedule_type,
992
    const sdfg::control_flow::Assignments& assignments,
993
    const DebugInfo& debug_info
994
) {
×
995
    int index = parent.index(child);
×
996
    if (index == -1) {
×
997
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
998
    }
×
999

1000
    return insert_node_internal<
×
NEW
1001
               Reduce>(parent, index, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type)
×
1002
        .first;
×
1003
}
×
1004

1005
Reduce& StructuredSDFGBuilder::add_reduce_after(
1006
    Sequence& parent,
1007
    ControlFlowNode& child,
1008
    const symbolic::Symbol indvar,
1009
    const symbolic::Condition condition,
1010
    const symbolic::Expression init,
1011
    const symbolic::Expression update,
1012
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
1013
    const ScheduleType& schedule_type,
1014
    const sdfg::control_flow::Assignments& assignments,
1015
    const DebugInfo& debug_info
1016
) {
4✔
1017
    int index = parent.index(child);
4✔
1018
    if (index == -1) {
4✔
1019
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
1020
    }
×
1021

1022
    return insert_node_internal<Reduce>(
4✔
1023
               parent, index + 1, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type
4✔
1024
    )
4✔
1025
        .first;
4✔
1026
}
4✔
1027

1028
Continue& StructuredSDFGBuilder::
1029
    add_continue(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
27✔
1030
    return insert_node_internal<Continue>(parent, INSERT_AT_END, &assignments, debug_info).first;
27✔
1031
}
27✔
1032

1033
Break& StructuredSDFGBuilder::
1034
    add_break(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
28✔
1035
    return insert_node_internal<Break>(parent, INSERT_AT_END, &assignments, debug_info).first;
28✔
1036
}
28✔
1037

1038
Return& StructuredSDFGBuilder::add_return(
1039
    Sequence& parent,
1040
    const std::string& data,
1041
    const sdfg::control_flow::Assignments& assignments,
1042
    const DebugInfo& debug_info
1043
) {
56✔
1044
    return insert_node_internal<Return>(parent, INSERT_AT_END, &assignments, debug_info, data).first;
56✔
1045
}
56✔
1046

1047
Return& StructuredSDFGBuilder::add_constant_return(
1048
    Sequence& parent,
1049
    const std::string& data,
1050
    const types::IType& type,
1051
    const sdfg::control_flow::Assignments& assignments,
1052
    const DebugInfo& debug_info
1053
) {
×
NEW
1054
    return insert_node_internal<Return>(parent, INSERT_AT_END, &assignments, debug_info, data, type).first;
×
1055
}
×
1056

1057
For& StructuredSDFGBuilder::convert_while(
1058
    Sequence& parent,
1059
    While& loop,
1060
    const symbolic::Symbol indvar,
1061
    const symbolic::Condition condition,
1062
    const symbolic::Expression init,
1063
    const symbolic::Expression update
1064
) {
×
1065
    int index = parent.index(loop);
×
1066
    if (index == -1) {
×
1067
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
1068
    }
×
1069

1070
    auto iter = parent.children_.begin() + index;
×
1071
    auto& new_iter = *parent.children_.insert(
×
1072
        iter + 1,
×
1073
        std::unique_ptr<For>(new For(
×
1074
            this->new_element_id_batch(For::REQUIRED_ELEMENT_IDS),
×
1075
            loop.debug_info(),
×
1076
            &parent,
×
1077
            indvar,
×
1078
            init,
×
1079
            update,
×
1080
            condition
×
1081
        ))
×
1082
    );
×
1083

1084
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
1085
    this->move_children(loop.root(), for_loop.root());
×
1086

1087
    // Remove while loop
1088
    parent.children_.erase(parent.children_.begin() + index);
×
1089

1090
    return for_loop;
×
1091
};
×
1092

1093
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop) {
77✔
1094
    int index = parent.index(loop);
77✔
1095
    if (index == -1) {
77✔
1096
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
1097
    }
×
1098

1099
    auto iter = parent.children_.begin() + index;
77✔
1100
    auto& new_iter = *parent.children_.insert(
77✔
1101
        iter + 1,
77✔
1102
        std::unique_ptr<Map>(new Map(
77✔
1103
            this->new_element_id_batch(Map::REQUIRED_ELEMENT_IDS),
77✔
1104
            loop.debug_info(),
77✔
1105
            &parent,
77✔
1106
            loop.indvar(),
77✔
1107
            loop.init(),
77✔
1108
            loop.update(),
77✔
1109
            loop.condition(),
77✔
1110
            ScheduleType_Sequential::create()
77✔
1111
        ))
77✔
1112
    );
77✔
1113

1114
    auto& map = dynamic_cast<Map&>(*new_iter);
77✔
1115
    this->move_children(loop.root(), map.root());
77✔
1116

1117
    // Remove for loop
1118
    parent.children_.erase(parent.children_.begin() + index);
77✔
1119

1120
    return map;
77✔
1121
};
77✔
1122

1123
Reduce& StructuredSDFGBuilder::convert_for_to_reduce(
1124
    Sequence& parent, For& loop, const std::vector<structured_control_flow::ReductionInfo>& reductions
1125
) {
26✔
1126
    int index = parent.index(loop);
26✔
1127
    if (index == -1) {
26✔
1128
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
1129
    }
×
1130

1131
    auto iter = parent.children_.begin() + index;
26✔
1132
    auto& new_iter = *parent.children_.insert(
26✔
1133
        iter + 1,
26✔
1134
        std::unique_ptr<Reduce>(new Reduce(
26✔
1135
            this->new_element_id_batch(Reduce::REQUIRED_ELEMENT_IDS),
26✔
1136
            loop.debug_info(),
26✔
1137
            &parent,
26✔
1138
            loop.indvar(),
26✔
1139
            loop.init(),
26✔
1140
            loop.update(),
26✔
1141
            loop.condition(),
26✔
1142
            reductions,
26✔
1143
            ScheduleType_Sequential::create()
26✔
1144
        ))
26✔
1145
    );
26✔
1146

1147
    auto& reduce = dynamic_cast<Reduce&>(*new_iter);
26✔
1148
    this->move_children(loop.root(), reduce.root());
26✔
1149

1150
    // Remove for loop
1151
    parent.children_.erase(parent.children_.begin() + index);
26✔
1152

1153
    return reduce;
26✔
1154
};
26✔
1155

1156
void StructuredSDFGBuilder::update_if_else_condition(IfElse& if_else, size_t index, const symbolic::Condition condition) {
×
1157
    if (index >= if_else.conditions_.size()) {
×
1158
        throw InvalidSDFGException("StructuredSDFGBuilder: Index out of range");
×
1159
    }
×
1160
    if_else.conditions_.at(index) = condition;
×
1161
};
×
1162

1163
void StructuredSDFGBuilder::update_loop(
1164
    StructuredLoop& loop,
1165
    const symbolic::Symbol indvar,
1166
    const symbolic::Condition condition,
1167
    const symbolic::Expression init,
1168
    const symbolic::Expression update
1169
) {
119✔
1170
    loop.indvar_ = indvar;
119✔
1171
    loop.condition_ = condition;
119✔
1172
    loop.init_ = init;
119✔
1173
    loop.update_ = update;
119✔
1174
};
119✔
1175

1176
void StructuredSDFGBuilder::update_schedule_type(StructuredLoop& loop, const ScheduleType& schedule_type) {
36✔
1177
    loop.schedule_type_ = schedule_type;
36✔
1178
}
36✔
1179

1180
/***** Section: Dataflow Graph *****/
1181

1182
data_flow::AccessNode& StructuredSDFGBuilder::
1183
    add_access(structured_control_flow::Block& block, const std::string& data, const DebugInfo& debug_info) {
7,591✔
1184
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
7,591✔
1185
    auto res = block.dataflow_->nodes_.insert(
7,591✔
1186
        {vertex,
7,591✔
1187
         std::unique_ptr<data_flow::AccessNode>(
7,591✔
1188
             new data_flow::AccessNode(this->new_element_id(), debug_info, vertex, block.dataflow(), data)
7,591✔
1189
         )}
7,591✔
1190
    );
7,591✔
1191

1192
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
7,591✔
1193
};
7,591✔
1194

1195
data_flow::ConstantNode& StructuredSDFGBuilder::add_constant(
1196
    structured_control_flow::Block& block, const std::string& data, const types::IType& type, const DebugInfo& debug_info
1197
) {
435✔
1198
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
435✔
1199
    auto res = block.dataflow_->nodes_.insert(
435✔
1200
        {vertex,
435✔
1201
         std::unique_ptr<data_flow::ConstantNode>(
435✔
1202
             new data_flow::ConstantNode(this->new_element_id(), debug_info, vertex, block.dataflow(), data, type)
435✔
1203
         )}
435✔
1204
    );
435✔
1205

1206
    return dynamic_cast<data_flow::ConstantNode&>(*(res.first->second));
435✔
1207
};
435✔
1208

1209

1210
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
1211
    structured_control_flow::Block& block,
1212
    const data_flow::TaskletCode code,
1213
    const std::string& output,
1214
    const std::vector<std::string>& inputs,
1215
    const DebugInfo& debug_info
1216
) {
1,736✔
1217
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
1,736✔
1218
    auto res = block.dataflow_->nodes_.insert(
1,736✔
1219
        {vertex,
1,736✔
1220
         std::unique_ptr<data_flow::Tasklet>(
1,736✔
1221
             new data_flow::Tasklet(this->new_element_id(), debug_info, vertex, block.dataflow(), code, output, inputs)
1,736✔
1222
         )}
1,736✔
1223
    );
1,736✔
1224

1225
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
1,736✔
1226
};
1,736✔
1227

1228
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
1229
    structured_control_flow::Block& block,
1230
    data_flow::DataFlowNode& src,
1231
    const std::string& src_conn,
1232
    data_flow::DataFlowNode& dst,
1233
    const std::string& dst_conn,
1234
    const data_flow::Subset& subset,
1235
    const types::IType& base_type,
1236
    const DebugInfo& debug_info
1237
) {
8,324✔
1238
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
8,324✔
1239
    auto res = block.dataflow_->edges_.insert(
8,324✔
1240
        {edge.first,
8,324✔
1241
         std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
8,324✔
1242
             this->new_element_id(), debug_info, edge.first, block.dataflow(), src, src_conn, dst, dst_conn, subset, base_type
8,324✔
1243
         ))}
8,324✔
1244
    );
8,324✔
1245

1246
    auto& memlet = dynamic_cast<data_flow::Memlet&>(*(res.first->second));
8,324✔
1247
#ifndef NDEBUG
8,324✔
1248
    memlet.validate(*this->structured_sdfg_);
8,324✔
1249
#endif
8,324✔
1250

1251
    return memlet;
8,324✔
1252
};
8,324✔
1253

1254
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1255
    structured_control_flow::Block& block,
1256
    data_flow::AccessNode& src,
1257
    data_flow::CodeNode& dst,
1258
    const std::string& dst_conn,
1259
    const data_flow::Subset& subset,
1260
    const types::IType& base_type,
1261
    const DebugInfo& debug_info
1262
) {
4,350✔
1263
    return this->add_memlet(block, src, "void", dst, dst_conn, subset, base_type, debug_info);
4,350✔
1264
};
4,350✔
1265

1266
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1267
    structured_control_flow::Block& block,
1268
    data_flow::CodeNode& src,
1269
    const std::string& src_conn,
1270
    data_flow::AccessNode& dst,
1271
    const data_flow::Subset& subset,
1272
    const types::IType& base_type,
1273
    const DebugInfo& debug_info
1274
) {
1,573✔
1275
    return this->add_memlet(block, src, src_conn, dst, "void", subset, base_type, debug_info);
1,573✔
1276
};
1,573✔
1277

1278
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1279
    structured_control_flow::Block& block,
1280
    data_flow::AccessNode& src,
1281
    data_flow::Tasklet& dst,
1282
    const std::string& dst_conn,
1283
    const data_flow::Subset& subset,
1284
    const DebugInfo& debug_info
1285
) {
1,109✔
1286
    const types::IType* src_type = nullptr;
1,109✔
1287
    if (auto cnode = dynamic_cast<data_flow::ConstantNode*>(&src)) {
1,109✔
1288
        src_type = &cnode->type();
241✔
1289
    } else {
868✔
1290
        src_type = &this->structured_sdfg_->type(src.data());
868✔
1291
    }
868✔
1292
    auto base_type = types::infer_type(*this->structured_sdfg_, *src_type, subset);
1,109✔
1293
    if (base_type->type_id() != types::TypeID::Scalar) {
1,109✔
1294
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
1295
    }
×
1296
    return this->add_memlet(block, src, "void", dst, dst_conn, subset, *src_type, debug_info);
1,109✔
1297
};
1,109✔
1298

1299
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1300
    structured_control_flow::Block& block,
1301
    data_flow::Tasklet& src,
1302
    const std::string& src_conn,
1303
    data_flow::AccessNode& dst,
1304
    const data_flow::Subset& subset,
1305
    const DebugInfo& debug_info
1306
) {
720✔
1307
    auto& dst_type = this->structured_sdfg_->type(dst.data());
720✔
1308
    auto base_type = types::infer_type(*this->structured_sdfg_, dst_type, subset);
720✔
1309
    if (base_type->type_id() != types::TypeID::Scalar) {
720✔
1310
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
1311
    }
×
1312
    return this->add_memlet(block, src, src_conn, dst, "void", subset, dst_type, debug_info);
720✔
1313
};
720✔
1314

1315
data_flow::Memlet& StructuredSDFGBuilder::add_reference_memlet(
1316
    structured_control_flow::Block& block,
1317
    data_flow::AccessNode& src,
1318
    data_flow::AccessNode& dst,
1319
    const data_flow::Subset& subset,
1320
    const types::IType& base_type,
1321
    const DebugInfo& debug_info
1322
) {
120✔
1323
    return this->add_memlet(block, src, "void", dst, "ref", subset, base_type, debug_info);
120✔
1324
};
120✔
1325

1326
data_flow::Memlet& StructuredSDFGBuilder::add_dereference_memlet(
1327
    structured_control_flow::Block& block,
1328
    data_flow::AccessNode& src,
1329
    data_flow::AccessNode& dst,
1330
    bool derefs_src,
1331
    const types::IType& base_type,
1332
    const DebugInfo& debug_info
1333
) {
23✔
1334
    if (derefs_src) {
23✔
1335
        return this->add_memlet(block, src, "void", dst, "deref", {symbolic::zero()}, base_type, debug_info);
14✔
1336
    } else {
14✔
1337
        return this->add_memlet(block, src, "deref", dst, "void", {symbolic::zero()}, base_type, debug_info);
9✔
1338
    }
9✔
1339
};
23✔
1340

1341
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block, const data_flow::Memlet& edge) {
158✔
1342
    auto& graph = block.dataflow();
158✔
1343
    auto e = edge.edge();
158✔
1344
    boost::remove_edge(e, graph.graph_);
158✔
1345
    graph.edges_.erase(e);
158✔
1346
};
158✔
1347

1348
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block, const data_flow::DataFlowNode& node) {
91✔
1349
    auto& graph = block.dataflow();
91✔
1350
    auto v = node.vertex();
91✔
1351
    boost::remove_vertex(v, graph.graph_);
91✔
1352
    graph.nodes_.erase(v);
91✔
1353
};
91✔
1354

1355
void StructuredSDFGBuilder::clear_code_node_legacy(structured_control_flow::Block& block, const data_flow::CodeNode& node) {
646✔
1356
    auto& graph = block.dataflow();
646✔
1357

1358
    std::unordered_set<const data_flow::DataFlowNode*> to_delete = {&node};
646✔
1359

1360
    // Delete incoming
1361
    std::list<const data_flow::Memlet*> iedges;
646✔
1362
    for (auto& iedge : graph.in_edges(node)) {
1,626✔
1363
        iedges.push_back(&iedge);
1,626✔
1364
    }
1,626✔
1365
    for (auto iedge : iedges) {
1,626✔
1366
        auto& src = iedge->src();
1,626✔
1367
        to_delete.insert(&src);
1,626✔
1368

1369
        auto edge = iedge->edge();
1,626✔
1370
        graph.edges_.erase(edge);
1,626✔
1371
        boost::remove_edge(edge, graph.graph_);
1,626✔
1372
    }
1,626✔
1373

1374
    // Delete outgoing
1375
    std::list<const data_flow::Memlet*> oedges;
646✔
1376
    for (auto& oedge : graph.out_edges(node)) {
646✔
1377
        oedges.push_back(&oedge);
44✔
1378
    }
44✔
1379
    for (auto oedge : oedges) {
646✔
1380
        auto& dst = oedge->dst();
44✔
1381
        to_delete.insert(&dst);
44✔
1382

1383
        auto edge = oedge->edge();
44✔
1384
        graph.edges_.erase(edge);
44✔
1385
        boost::remove_edge(edge, graph.graph_);
44✔
1386
    }
44✔
1387

1388
    // Delete nodes
1389
    for (auto obsolete_node : to_delete) {
2,303✔
1390
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
2,303✔
1391
            auto vertex = obsolete_node->vertex();
2,303✔
1392
            graph.nodes_.erase(vertex);
2,303✔
1393
            boost::remove_vertex(vertex, graph.graph_);
2,303✔
1394
        }
2,303✔
1395
    }
2,303✔
1396
}
646✔
1397

1398
void StructuredSDFGBuilder::
1399
    clear_access_node_legacy(structured_control_flow::Block& block, const data_flow::AccessNode& node) {
3✔
1400
    auto& graph = block.dataflow();
3✔
1401

1402
    std::list<const data_flow::Memlet*> tmp;
3✔
1403
    std::list<const data_flow::DataFlowNode*> queue = {&node};
3✔
1404
    while (!queue.empty()) {
9✔
1405
        auto current = queue.front();
6✔
1406
        queue.pop_front();
6✔
1407
        if (current != &node) {
6✔
1408
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
3✔
1409
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
1410
                    continue;
×
1411
                }
×
1412
            }
×
1413
        }
3✔
1414

1415
        tmp.clear();
6✔
1416
        for (auto& iedge : graph.in_edges(*current)) {
6✔
1417
            tmp.push_back(&iedge);
3✔
1418
        }
3✔
1419
        for (auto iedge : tmp) {
6✔
1420
            auto& src = iedge->src();
3✔
1421
            queue.push_back(&src);
3✔
1422

1423
            auto edge = iedge->edge();
3✔
1424
            graph.edges_.erase(edge);
3✔
1425
            boost::remove_edge(edge, graph.graph_);
3✔
1426
        }
3✔
1427

1428
        if (current != &node || graph.out_degree(*current) == 0) {
6✔
1429
            auto vertex = current->vertex();
6✔
1430
            graph.nodes_.erase(vertex);
6✔
1431
            boost::remove_vertex(vertex, graph.graph_);
6✔
1432
        }
6✔
1433
    }
6✔
1434
}
3✔
1435

1436
int StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block, const data_flow::DataFlowNode& node) {
13✔
1437
    return clear_node(block, node, {&node});
13✔
1438
}
13✔
1439

1440
int StructuredSDFGBuilder::clear_node(
1441
    structured_control_flow::Block& block,
1442
    const data_flow::DataFlowNode& node,
1443
    const std::unordered_set<const data_flow::DataFlowNode*>& ignore_side_effects
1444
) {
13✔
1445
    auto& graph = block.dataflow();
13✔
1446

1447
    std::list<const data_flow::Memlet*> tmp;
13✔
1448
    std::list<const data_flow::DataFlowNode*> queue = {&node};
13✔
1449
    std::unordered_set<const data_flow::DataFlowNode*> remove_once_set;
13✔
1450
    std::unordered_set<const data_flow::DataFlowNode*> force_remove_set;
13✔
1451
    int removed_nodes = 0;
13✔
1452

1453
    do {
36✔
1454
        auto current = queue.front();
36✔
1455
        queue.pop_front();
36✔
1456

1457
        if (!remove_once_set.contains(current)) {
36✔
1458
            bool no_more_consumers = graph.out_degree(*current) == 0; // cannot remove nodes still in use
35✔
1459

1460
            auto* access_node = dynamic_cast<const data_flow::AccessNode*>(current);
35✔
1461

1462
            bool ignore_side_effects_on_current = ignore_side_effects.contains(current);
35✔
1463
            bool force_remove = force_remove_set.contains(current);
35✔
1464

1465
            // we can remove nodes without out-edges & side effects.
1466
            if ((no_more_consumers && !current->side_effect()) ||
35✔
1467
                ((ignore_side_effects_on_current || force_remove) && (no_more_consumers || access_node))) {
35✔
1468
                // Or for access-nodes on the ignore list, we can remove the write side (will not remove the node, only
1469
                // inputs) For any other node on the ignore list, we can ignore if it has side effects or not
1470
                tmp.clear();
28✔
1471
                for (auto& iedge : graph.in_edges(*current)) {
28✔
1472
                    tmp.push_back(&iedge);
25✔
1473
                }
25✔
1474
                bool no_in_edges = true;
28✔
1475
                for (auto iedge : tmp) {
28✔
1476
                    auto& src = iedge->src();
25✔
1477

1478
                    auto edge_rem = src.can_remove_out_edge(graph, iedge);
25✔
1479
                    if (edge_rem != data_flow::EdgeRemoveOption::NotRemovable) {
25✔
1480
                        auto src_conn = iedge->src_conn();
23✔
1481
                        queue.push_back(&src);
23✔
1482
                        auto edge = iedge->edge();
23✔
1483
                        graph.edges_.erase(edge);
23✔
1484
                        boost::remove_edge(edge, graph.graph_);
23✔
1485
                        if (edge_rem == data_flow::EdgeRemoveOption::RequiresUpdate) {
23✔
1486
                            const_cast<data_flow::DataFlowNode&>(src).update_edge_removed(src_conn);
×
1487
                        } else if (edge_rem == data_flow::EdgeRemoveOption::RemoveNodeAfter) {
23✔
1488
                            force_remove_set.insert(&src);
7✔
1489
                        }
7✔
1490
                    } else {
23✔
1491
                        no_in_edges = false;
2✔
1492
                    }
2✔
1493
                }
25✔
1494

1495
                if (no_more_consumers && no_in_edges) {
28✔
1496
                    remove_once_set.insert(current);
25✔
1497
                    auto vertex = current->vertex();
25✔
1498
                    graph.nodes_.erase(vertex);
25✔
1499
                    boost::remove_vertex(vertex, graph.graph_);
25✔
1500
                    ++removed_nodes;
25✔
1501
                } else if (!no_more_consumers && force_remove) {
25✔
1502
                    throw std::runtime_error(
×
1503
                        "Not yet supported: RemoveNodeAfter with more than 1 edge: #" +
×
1504
                        std::to_string(current->element_id())
×
1505
                    );
×
1506
                }
×
1507
            }
28✔
1508
        }
35✔
1509
    } while (!queue.empty());
36✔
1510

1511
    return removed_nodes;
13✔
1512
}
13✔
1513

1514
int StructuredSDFGBuilder::clear_ptr_borrow_edge(Block& block, const data_flow::Memlet& edge) {
1✔
1515
    auto& graph = block.dataflow();
1✔
1516
    auto& src = edge.src();
1✔
1517
    auto& dst = edge.dst();
1✔
1518

1519
    auto edge_removal = dst.can_remove_in_edge(graph, &edge);
1✔
1520
    int removed = 0;
1✔
1521
    if (edge_removal == data_flow::EdgeRemoveOption::Trivially) {
1✔
1522
        remove_memlet(block, edge);
×
1523
        ++removed;
×
1524
    } else if (edge_removal == data_flow::EdgeRemoveOption::RemoveNodeAfter) {
1✔
1525
        removed = 1 + clear_node(block, dst);
1✔
1526
    } else if (edge_removal == data_flow::EdgeRemoveOption::RequiresUpdate) {
1✔
1527
        remove_memlet(block, edge);
×
1528
        const_cast<data_flow::DataFlowNode&>(dst).update_edge_removed(edge.dst_conn());
×
1529
    } else if (edge_removal == data_flow::EdgeRemoveOption::NotRemovable) {
×
1530
        return 0;
×
1531
    }
×
1532

1533
    return removed;
1✔
1534
}
1✔
1535

1536
void StructuredSDFGBuilder::add_dataflow(const data_flow::DataFlowGraph& from, Block& to) {
19✔
1537
    auto& to_dataflow = to.dataflow();
19✔
1538

1539
    std::unordered_map<graph::Vertex, graph::Vertex> node_mapping;
19✔
1540
    for (auto& entry : from.nodes_) {
21✔
1541
        auto vertex = boost::add_vertex(to_dataflow.graph_);
21✔
1542
        to_dataflow.nodes_.insert({vertex, entry.second->clone(this->new_element_id(), vertex, to_dataflow)});
21✔
1543
        node_mapping.insert({entry.first, vertex});
21✔
1544
    }
21✔
1545

1546
    for (auto& entry : from.edges_) {
19✔
1547
        auto src = node_mapping[entry.second->src().vertex()];
14✔
1548
        auto dst = node_mapping[entry.second->dst().vertex()];
14✔
1549

1550
        auto edge = boost::add_edge(src, dst, to_dataflow.graph_);
14✔
1551

1552
        to_dataflow.edges_.insert(
14✔
1553
            {edge.first,
14✔
1554
             entry.second->clone(
14✔
1555
                 this->new_element_id(), edge.first, to_dataflow, *to_dataflow.nodes_[src], *to_dataflow.nodes_[dst]
14✔
1556
             )}
14✔
1557
        );
14✔
1558
    }
14✔
1559
};
19✔
1560

1561
void StructuredSDFGBuilder::merge_siblings(data_flow::AccessNode& source_node) {
21✔
1562
    auto& user_graph = source_node.get_parent();
21✔
1563
    auto* block = dynamic_cast<structured_control_flow::Block*>(user_graph.get_parent());
21✔
1564
    if (!block) {
21✔
1565
        throw InvalidSDFGException("Parent of user graph must be a block!");
×
1566
    }
×
1567

1568
    // Merge access nodes if they access the same container on a code node
1569
    for (auto& oedge : user_graph.out_edges(source_node)) {
21✔
1570
        if (auto* code_node = dynamic_cast<data_flow::CodeNode*>(&oedge.dst())) {
15✔
1571
            std::unordered_set<data_flow::Memlet*> iedges;
8✔
1572
            for (auto& iedge : user_graph.in_edges(*code_node)) {
10✔
1573
                iedges.insert(&iedge);
10✔
1574
            }
10✔
1575
            for (auto* iedge : iedges) {
10✔
1576
                if (dynamic_cast<data_flow::ConstantNode*>(&iedge->src())) {
10✔
1577
                    continue;
×
1578
                }
×
1579
                auto* access_node = static_cast<data_flow::AccessNode*>(&iedge->src());
10✔
1580
                if (access_node == &source_node || access_node->data() != source_node.data()) {
10✔
1581
                    continue;
9✔
1582
                }
9✔
1583
                this->add_memlet(
1✔
1584
                    *block,
1✔
1585
                    source_node,
1✔
1586
                    iedge->src_conn(),
1✔
1587
                    *code_node,
1✔
1588
                    iedge->dst_conn(),
1✔
1589
                    iedge->subset(),
1✔
1590
                    iedge->base_type(),
1✔
1591
                    iedge->debug_info()
1✔
1592
                );
1✔
1593
                this->remove_memlet(*block, *iedge);
1✔
1594
                source_node.set_debug_info(DebugInfo::merge(source_node.debug_info(), access_node->debug_info()));
1✔
1595
            }
1✔
1596
        }
8✔
1597
    }
15✔
1598

1599
    // Also merge "output" access nodes if they access the same container on a library node
1600
    for (auto& iedge : user_graph.in_edges(source_node)) {
21✔
1601
        if (auto* libnode = dynamic_cast<data_flow::LibraryNode*>(&iedge.src())) {
7✔
1602
            std::unordered_set<data_flow::Memlet*> oedges;
×
1603
            for (auto& oedge : user_graph.out_edges(*libnode)) {
×
1604
                oedges.insert(&oedge);
×
1605
            }
×
1606
            for (auto* oedge : oedges) {
×
1607
                auto* access_node = static_cast<data_flow::AccessNode*>(&oedge->dst());
×
1608
                if (access_node == &source_node || access_node->data() != source_node.data()) {
×
1609
                    continue;
×
1610
                }
×
1611
                this->add_memlet(
×
1612
                    *block,
×
1613
                    *libnode,
×
1614
                    oedge->src_conn(),
×
1615
                    source_node,
×
1616
                    oedge->dst_conn(),
×
1617
                    oedge->subset(),
×
1618
                    oedge->base_type(),
×
1619
                    oedge->debug_info()
×
1620
                );
×
1621
                this->remove_memlet(*block, *oedge);
×
1622
                source_node.set_debug_info(DebugInfo::merge(source_node.debug_info(), access_node->debug_info()));
×
1623
            }
×
1624
        }
×
1625
    }
7✔
1626
}
21✔
1627

1628
void StructuredSDFGBuilder::merge_sinks(structured_control_flow::Block& block) {
6✔
1629
    auto& graph = block.dataflow();
6✔
1630

1631
    // Collect sink access nodes (modifying the graph during iteration is not safe)
1632
    std::vector<data_flow::AccessNode*> sink_access_nodes;
6✔
1633
    for (auto node : graph.sinks()) {
9✔
1634
        if (auto* access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
9✔
1635
            sink_access_nodes.push_back(access_node);
9✔
1636
        }
9✔
1637
    }
9✔
1638

1639
    // Merge sink access nodes that refer to the same container
1640
    std::unordered_map<std::string, data_flow::AccessNode*> representatives;
6✔
1641
    for (auto* access_node : sink_access_nodes) {
9✔
1642
        auto it = representatives.find(access_node->data());
9✔
1643
        if (it == representatives.end()) {
9✔
1644
            representatives.insert({access_node->data(), access_node});
8✔
1645
            continue;
8✔
1646
        }
8✔
1647

1648
        auto& rep = *it->second;
1✔
1649

1650
        // Redirect all incoming memlets of this node onto the representative
1651
        std::unordered_set<data_flow::Memlet*> iedges;
1✔
1652
        for (auto& iedge : graph.in_edges(*access_node)) {
1✔
1653
            iedges.insert(&iedge);
1✔
1654
        }
1✔
1655
        for (auto* iedge : iedges) {
1✔
1656
            this->add_memlet(
1✔
1657
                block,
1✔
1658
                iedge->src(),
1✔
1659
                iedge->src_conn(),
1✔
1660
                rep,
1✔
1661
                iedge->dst_conn(),
1✔
1662
                iedge->subset(),
1✔
1663
                iedge->base_type(),
1✔
1664
                iedge->debug_info()
1✔
1665
            );
1✔
1666
            this->remove_memlet(block, *iedge);
1✔
1667
        }
1✔
1668

1669
        rep.set_debug_info(DebugInfo::merge(rep.debug_info(), access_node->debug_info()));
1✔
1670

1671
        // The node is now isolated and can be removed
1672
        this->remove_node(block, *access_node);
1✔
1673
    }
1✔
1674
}
6✔
1675

1676
} // namespace builder
1677
} // 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