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

daisytuner / docc / 28793626242

06 Jul 2026 01:04PM UTC coverage: 62.962% (+0.2%) from 62.801%
28793626242

Pull #740

github

web-flow
Merge b7e03d389 into 23e67a4ab
Pull Request #740: Expand pass

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

36 existing lines in 10 files now uncovered.

40557 of 64415 relevant lines covered (62.96%)

972.09 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_; };
7,451✔
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,592✔
451
        auto current = queue.front();
1,588✔
452
        queue.pop_front();
1,588✔
453

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

458
        if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(current)) {
1,410✔
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,355✔
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)) {
864✔
472
            // Do nothing
473
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
864✔
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)) {
864✔
478
            queue.push_back(&for_stmt->root());
215✔
479
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
649✔
480
            queue.push_back(&while_stmt->root());
×
481
        } else if (dynamic_cast<structured_control_flow::Continue*>(current)) {
649✔
482
            // Do nothing
483
        } else if (dynamic_cast<structured_control_flow::Break*>(current)) {
649✔
484
            // Do nothing
485
        } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(current)) {
649✔
486
            queue.push_back(&map_stmt->root());
116✔
487
        } else if (auto reduce_stmt = dynamic_cast<structured_control_flow::Reduce*>(current)) {
533✔
488
            queue.push_back(&reduce_stmt->root());
×
489
        }
×
490
    }
1,410✔
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_) {
716✔
607
        child->parent_ = &target;
716✔
608
    }
716✔
609
    for (auto& trans : target.transitions_) {
716✔
610
        trans->parent_ = &target;
716✔
611
    }
716✔
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;
×
UNCOV
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;
×
UNCOV
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)
×
UNCOV
963
        .first;
×
UNCOV
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