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

daisytuner / docc / 29992851043

23 Jul 2026 08:52AM UTC coverage: 64.1% (+0.3%) from 63.787%
29992851043

Pull #866

github

web-flow
Merge f4354fb28 into 2e568810b
Pull Request #866: Add Support for Complex Types

188 of 296 new or added lines in 12 files covered. (63.51%)

272 existing lines in 10 files now uncovered.

42854 of 66855 relevant lines covered (64.1%)

737.97 hits per line

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

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

3
#include <cstddef>
4

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

12
#define TRAVERSE_CUTOFF 30
100✔
13

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

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

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

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

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

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

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

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

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

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

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

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

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

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

128
    return false;
6✔
129
}
9✔
130

131

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

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

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

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

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

167

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

293
            break;
17✔
294
        }
17✔
295

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

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

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

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

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

337

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

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

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

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

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

383
Function& StructuredSDFGBuilder::function() const { return static_cast<Function&>(*this->structured_sdfg_); };
60,698✔
384

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

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

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

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

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

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

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

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

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

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

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

428
StructuredSDFG& StructuredSDFGBuilder::subject() const { return *this->structured_sdfg_; };
7,011✔
429

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

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

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

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

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

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

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

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

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

496
void StructuredSDFGBuilder::
497
    replace_symbols(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
1✔
498
    FunctionBuilder::replace_symbols(old_expression, new_expression);
1✔
499
    this->structured_sdfg_->root_->replace(old_expression, new_expression);
1✔
500
}
1✔
501

502
void StructuredSDFGBuilder::replace_symbols(const symbolic::ExpressionMapping& replacements) {
1✔
503
    FunctionBuilder::replace_symbols(replacements);
1✔
504
    this->structured_sdfg_->root_->replace(replacements);
1✔
505
}
1✔
506

507
Sequence& StructuredSDFGBuilder::
508
    add_sequence(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
29✔
509
    return insert_node_internal<Sequence>(parent, INSERT_AT_END, &assignments, debug_info).first;
29✔
510
}
29✔
511

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

523
    return insert_node_internal<Sequence>(parent, index, &assignments, debug_info).first;
58✔
524
}
58✔
525

526
Sequence& StructuredSDFGBuilder::add_sequence_after(
527
    Sequence& parent,
528
    ControlFlowNode& child,
529
    const sdfg::control_flow::Assignments& assignments,
530
    const DebugInfo& debug_info
531
) {
24✔
532
    int index = parent.index(child);
24✔
533
    if (index == -1) {
24✔
UNCOV
534
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
535
    }
×
536

537
    return insert_node_internal<Sequence>(parent, index + 1, &assignments, debug_info).first;
24✔
538
}
24✔
539

540
Sequence& StructuredSDFGBuilder::add_sequence_at(
541
    Sequence& parent,
542
    InsertionPoint insertion_point,
543
    const DebugInfo& debug_info,
544
    const sdfg::control_flow::Assignments* assignments
545
) {
607✔
546
    return insert_node_internal<Sequence>(parent, insertion_point, assignments, debug_info).first;
607✔
547
}
607✔
548

549
std::pair<Sequence&, Transition&> StructuredSDFGBuilder::
550
    add_sequence_before(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
551
    int index = parent.index(child);
×
552
    if (index == -1) {
×
553
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
554
    }
×
555

556
    return insert_node_internal<Sequence>(parent, index, {}, debug_info);
×
557
}
×
558

559
void StructuredSDFGBuilder::remove_from_parent(ControlFlowNode& child) {
59✔
560
    auto* parent = dyn_cast<structured_control_flow::Sequence*>(child.get_parent());
59✔
561
    if (parent == nullptr) {
59✔
UNCOV
562
        throw InvalidSDFGException(
×
UNCOV
563
            "StructuredSDFGBuilder: Child has no sequence parent: #" + std::to_string(child.element_id())
×
UNCOV
564
        );
×
UNCOV
565
    }
×
566
    auto idx = parent->index(child);
59✔
567
    if (idx < 0) {
59✔
568
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found in parent");
×
569
    }
×
570
    remove_child(*parent, idx);
59✔
571
}
59✔
572

573
void StructuredSDFGBuilder::remove_child(Sequence& parent, size_t index) {
1,014✔
574
    parent.children_.erase(parent.children_.begin() + index);
1,014✔
575
    parent.transitions_.erase(parent.transitions_.begin() + index);
1,014✔
576
};
1,014✔
577

UNCOV
578
void StructuredSDFGBuilder::remove_children(Sequence& parent) {
×
UNCOV
579
    parent.children_.clear();
×
UNCOV
580
    parent.transitions_.clear();
×
UNCOV
581
};
×
582

583
void StructuredSDFGBuilder::move_child(Sequence& source, size_t source_index, Sequence& target) {
128✔
584
    size_t target_index = target.size();
128✔
585
    if (&source == &target) {
128✔
UNCOV
586
        target_index--;
×
UNCOV
587
    }
×
588
    this->move_child(source, source_index, target, target_index);
128✔
589
};
128✔
590

591
void StructuredSDFGBuilder::move_child(Sequence& source, size_t source_index, Sequence& target, size_t target_index) {
157✔
592
    auto node_ptr = std::move(source.children_.at(source_index));
157✔
593
    auto trans_ptr = std::move(source.transitions_.at(source_index));
157✔
594
    source.children_.erase(source.children_.begin() + source_index);
157✔
595
    source.transitions_.erase(source.transitions_.begin() + source_index);
157✔
596

597
    node_ptr->parent_ = &target;
157✔
598
    trans_ptr->parent_ = &target;
157✔
599
    target.children_.insert(target.children_.begin() + target_index, std::move(node_ptr));
157✔
600
    target.transitions_.insert(target.transitions_.begin() + target_index, std::move(trans_ptr));
157✔
601
};
157✔
602

603
void StructuredSDFGBuilder::move_children(Sequence& source, Sequence& target) {
279✔
604
    this->move_children(source, target, target.size());
279✔
605
};
279✔
606

607
void StructuredSDFGBuilder::move_children(Sequence& source, Sequence& target, size_t target_index) {
347✔
608
    target.children_.insert(
347✔
609
        target.children_.begin() + target_index,
347✔
610
        std::make_move_iterator(source.children_.begin()),
347✔
611
        std::make_move_iterator(source.children_.end())
347✔
612
    );
347✔
613
    target.transitions_.insert(
347✔
614
        target.transitions_.begin() + target_index,
347✔
615
        std::make_move_iterator(source.transitions_.begin()),
347✔
616
        std::make_move_iterator(source.transitions_.end())
347✔
617
    );
347✔
618
    for (auto& child : target.children_) {
715✔
619
        child->parent_ = &target;
715✔
620
    }
715✔
621
    for (auto& trans : target.transitions_) {
715✔
622
        trans->parent_ = &target;
715✔
623
    }
715✔
624
    source.children_.clear();
347✔
625
    source.transitions_.clear();
347✔
626
};
347✔
627

628
Sequence& StructuredSDFGBuilder::hoist_root() {
×
UNCOV
629
    auto current_root = std::move(this->structured_sdfg_->root_);
×
630

UNCOV
631
    this->structured_sdfg_->root_ =
×
UNCOV
632
        std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), current_root->debug_info(), nullptr));
×
633

UNCOV
634
    current_root->parent_ = this->structured_sdfg_->root_.get();
×
UNCOV
635
    this->structured_sdfg_->root_->children_.push_back(std::move(current_root));
×
UNCOV
636
    this->structured_sdfg_->root_->transitions_.push_back(std::unique_ptr<Transition>(
×
UNCOV
637
        new Transition(this->new_element_id(), current_root->debug_info(), *this->structured_sdfg_->root_)
×
UNCOV
638
    ));
×
UNCOV
639
    return *this->structured_sdfg_->root_;
×
UNCOV
640
};
×
641

642
Block& StructuredSDFGBuilder::
643
    add_block(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
3,685✔
644
    return insert_block_internal(parent, INSERT_AT_END, nullptr, &assignments, debug_info).first;
3,685✔
645
}
3,685✔
646

647
Block& StructuredSDFGBuilder::add_block(
648
    Sequence& parent,
649
    const data_flow::DataFlowGraph& data_flow_graph,
650
    const sdfg::control_flow::Assignments& assignments,
651
    const DebugInfo& debug_info
652
) {
19✔
653
    return insert_block_internal(parent, INSERT_AT_END, &data_flow_graph, &assignments, debug_info).first;
19✔
654
}
19✔
655

656
std::pair<Block&, Transition&> StructuredSDFGBuilder::insert_block_internal(
657
    Sequence& parent,
658
    int32_t insert_idx,
659
    const data_flow::DataFlowGraph* import_from,
660
    const sdfg::control_flow::Assignments* assignments,
661
    const DebugInfo& debug_info
662
) {
4,044✔
663
    auto new_pair = insert_node_internal<Block>(parent, insert_idx, assignments, debug_info);
4,044✔
664

665
    if (import_from) {
4,044✔
666
        this->add_dataflow(*import_from, new_pair.first);
19✔
667
    }
19✔
668

669
    return new_pair;
4,044✔
670
}
4,044✔
671

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

683
    return insert_block_internal(parent, index, nullptr, &assignments, debug_info).first;
164✔
684
};
164✔
685

686
Block& StructuredSDFGBuilder::add_block_before(
687
    Sequence& parent,
688
    ControlFlowNode& child,
689
    data_flow::DataFlowGraph& data_flow_graph,
690
    const sdfg::control_flow::Assignments& assignments,
691
    const DebugInfo& debug_info
UNCOV
692
) {
×
UNCOV
693
    int index = parent.index(child);
×
UNCOV
694
    if (index == -1) {
×
UNCOV
695
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
696
    }
×
697

698
    return insert_block_internal(parent, index, &data_flow_graph, &assignments, debug_info).first;
×
UNCOV
699
};
×
700

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

712
    return insert_block_internal(parent, index + 1, nullptr, &assignments, debug_info).first;
176✔
713
}
176✔
714

715
Block& StructuredSDFGBuilder::add_block_after(
716
    Sequence& parent,
717
    ControlFlowNode& child,
718
    data_flow::DataFlowGraph& data_flow_graph,
719
    const sdfg::control_flow::Assignments& assignments,
720
    const DebugInfo& debug_info
UNCOV
721
) {
×
UNCOV
722
    int index = parent.index(child);
×
723
    if (index == -1) {
×
724
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
725
    }
×
726

UNCOV
727
    return insert_block_internal(parent, index + 1, &data_flow_graph, &assignments, debug_info).first;
×
728
}
×
729

730
Block& StructuredSDFGBuilder::add_block_at(
731
    Sequence& parent,
732
    InsertionPoint insertion_point,
733
    const DebugInfo& debug_info,
734
    const sdfg::control_flow::Assignments* assignments
735
) {
×
UNCOV
736
    return insert_block_internal(parent, insertion_point, nullptr, assignments, debug_info).first;
×
UNCOV
737
}
×
738

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

746
    return insert_block_internal(parent, index, nullptr, {}, debug_info);
×
UNCOV
747
}
×
748

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

UNCOV
757
    return insert_block_internal(parent, index, &data_flow_graph, {}, debug_info);
×
UNCOV
758
}
×
759

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

767
    return insert_block_internal(parent, index + 1, nullptr, {}, debug_info);
×
UNCOV
768
}
×
769

770
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(
771
    Sequence& parent, ControlFlowNode& child, data_flow::DataFlowGraph& data_flow_graph, const DebugInfo& debug_info
UNCOV
772
) {
×
UNCOV
773
    int index = parent.index(child);
×
UNCOV
774
    if (index == -1) {
×
UNCOV
775
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
776
    }
×
777

UNCOV
778
    return insert_block_internal(parent, index + 1, &data_flow_graph, {}, debug_info);
×
UNCOV
779
}
×
780

781
IfElse& StructuredSDFGBuilder::
782
    add_if_else(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
150✔
783
    return insert_node_internal<IfElse>(parent, INSERT_AT_END, &assignments, debug_info).first;
150✔
784
}
150✔
785

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

797
    return insert_node_internal<IfElse>(parent, index, &assignments, debug_info).first;
35✔
798
}
35✔
799

800
IfElse& StructuredSDFGBuilder::add_if_else_after(
801
    Sequence& parent,
802
    ControlFlowNode& child,
803
    const sdfg::control_flow::Assignments& assignments,
804
    const DebugInfo& debug_info
805
) {
×
806
    int index = parent.index(child);
×
807
    if (index == -1) {
×
UNCOV
808
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
809
    }
×
810

UNCOV
811
    return insert_node_internal<IfElse>(parent, index + 1, &assignments, debug_info).first;
×
UNCOV
812
}
×
813

814
std::pair<IfElse&, Transition&> StructuredSDFGBuilder::
UNCOV
815
    add_if_else_before(Sequence& parent, ControlFlowNode& child, const DebugInfo& debug_info) {
×
UNCOV
816
    int index = parent.index(child);
×
UNCOV
817
    if (index == -1) {
×
UNCOV
818
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
819
    }
×
820

821
    return insert_node_internal<IfElse>(parent, INSERT_AT_END, {}, debug_info);
×
822
};
×
823

824
Sequence& StructuredSDFGBuilder::add_case(IfElse& scope, const sdfg::symbolic::Condition cond, const DebugInfo& debug_info) {
288✔
825
    scope.cases_.push_back(std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info, &scope)));
288✔
826

827
    scope.conditions_.push_back(cond);
288✔
828
    return *scope.cases_.back();
288✔
829
};
288✔
830

UNCOV
831
void StructuredSDFGBuilder::remove_case(IfElse& scope, size_t index, const DebugInfo& debug_info) {
×
UNCOV
832
    scope.cases_.erase(scope.cases_.begin() + index);
×
UNCOV
833
    scope.conditions_.erase(scope.conditions_.begin() + index);
×
UNCOV
834
};
×
835

836
While& StructuredSDFGBuilder::
837
    add_while(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
53✔
838
    return insert_node_internal<While>(parent, INSERT_AT_END, &assignments, debug_info).first;
53✔
839
};
53✔
840

841
For& StructuredSDFGBuilder::add_for(
842
    Sequence& parent,
843
    const symbolic::Symbol indvar,
844
    const symbolic::Condition condition,
845
    const symbolic::Expression init,
846
    const symbolic::Expression update,
847
    const sdfg::control_flow::Assignments& assignments,
848
    const DebugInfo& debug_info
849
) {
808✔
850
    return insert_node_internal<For>(parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition)
808✔
851
        .first;
808✔
852
}
808✔
853

854
For& StructuredSDFGBuilder::add_for_before(
855
    Sequence& parent,
856
    ControlFlowNode& child,
857
    const symbolic::Symbol indvar,
858
    const symbolic::Condition condition,
859
    const symbolic::Expression init,
860
    const symbolic::Expression update,
861
    const sdfg::control_flow::Assignments& assignments,
862
    const DebugInfo& debug_info
863
) {
40✔
864
    int index = parent.index(child);
40✔
865
    if (index == -1) {
40✔
UNCOV
866
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
867
    }
×
868

869
    return insert_node_internal<For>(parent, index, &assignments, debug_info, indvar, init, update, condition).first;
40✔
870
}
40✔
871

872
For& StructuredSDFGBuilder::add_for_after(
873
    Sequence& parent,
874
    ControlFlowNode& child,
875
    const symbolic::Symbol indvar,
876
    const symbolic::Condition condition,
877
    const symbolic::Expression init,
878
    const symbolic::Expression update,
879
    const sdfg::control_flow::Assignments& assignments,
880
    const DebugInfo& debug_info
881
) {
62✔
882
    int index = parent.index(child);
62✔
883
    if (index == -1) {
62✔
UNCOV
884
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
885
    }
×
886

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

890
For& StructuredSDFGBuilder::add_for_at(
891
    Sequence& parent,
892
    InsertionPoint insertion_point,
893
    const symbolic::Symbol indvar,
894
    const symbolic::Condition condition,
895
    const symbolic::Expression init,
896
    const symbolic::Expression update,
897
    const ScheduleType& schedule_type,
898
    const DebugInfo& debug_info,
899
    const sdfg::control_flow::Assignments* assignments
UNCOV
900
) {
×
UNCOV
901
    return insert_node_internal<For>(parent, insertion_point, assignments, debug_info, indvar, init, update, condition)
×
UNCOV
902
        .first;
×
UNCOV
903
}
×
904

905
Map& StructuredSDFGBuilder::add_map(
906
    Sequence& parent,
907
    const symbolic::Symbol indvar,
908
    const symbolic::Condition condition,
909
    const symbolic::Expression init,
910
    const symbolic::Expression update,
911
    const ScheduleType& schedule_type,
912
    const sdfg::control_flow::Assignments& assignments,
913
    const DebugInfo& debug_info
914
) {
2,337✔
915
    return insert_node_internal<
2,337✔
916
               Map>(parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition, schedule_type)
2,337✔
917
        .first;
2,337✔
918
}
2,337✔
919

920
Map& StructuredSDFGBuilder::add_map_before(
921
    Sequence& parent,
922
    ControlFlowNode& child,
923
    const symbolic::Symbol indvar,
924
    const symbolic::Condition condition,
925
    const symbolic::Expression init,
926
    const symbolic::Expression update,
927
    const ScheduleType& schedule_type,
928
    const sdfg::control_flow::Assignments& assignments,
929
    const DebugInfo& debug_info
930
) {
75✔
931
    int index = parent.index(child);
75✔
932
    if (index == -1) {
75✔
UNCOV
933
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
934
    }
×
935

936
    return insert_node_internal<
75✔
937
               Map>(parent, index, &assignments, debug_info, indvar, init, update, condition, schedule_type)
75✔
938
        .first;
75✔
939
}
75✔
940

941
Map& StructuredSDFGBuilder::add_map_after(
942
    Sequence& parent,
943
    ControlFlowNode& child,
944
    const symbolic::Symbol indvar,
945
    const symbolic::Condition condition,
946
    const symbolic::Expression init,
947
    const symbolic::Expression update,
948
    const ScheduleType& schedule_type,
949
    const sdfg::control_flow::Assignments& assignments,
950
    const DebugInfo& debug_info
951
) {
85✔
952
    int index = parent.index(child);
85✔
953
    if (index == -1) {
85✔
UNCOV
954
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
955
    }
×
956

957
    return insert_node_internal<
85✔
958
               Map>(parent, index + 1, &assignments, debug_info, indvar, init, update, condition, schedule_type)
85✔
959
        .first;
85✔
960
}
85✔
961

962
Map& StructuredSDFGBuilder::add_map_at(
963
    Sequence& parent,
964
    InsertionPoint insertion_point,
965
    const symbolic::Symbol indvar,
966
    const symbolic::Condition condition,
967
    const symbolic::Expression init,
968
    const symbolic::Expression update,
969
    const ScheduleType& schedule_type,
970
    const DebugInfo& debug_info,
971
    const sdfg::control_flow::Assignments* assignments
UNCOV
972
) {
×
UNCOV
973
    return insert_node_internal<
×
UNCOV
974
               Map>(parent, insertion_point, assignments, debug_info, indvar, init, update, condition, schedule_type)
×
UNCOV
975
        .first;
×
UNCOV
976
}
×
977

978
Reduce& StructuredSDFGBuilder::add_reduce(
979
    Sequence& parent,
980
    const symbolic::Symbol indvar,
981
    const symbolic::Condition condition,
982
    const symbolic::Expression init,
983
    const symbolic::Expression update,
984
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
985
    const ScheduleType& schedule_type,
986
    const sdfg::control_flow::Assignments& assignments,
987
    const DebugInfo& debug_info
988
) {
43✔
989
    return insert_node_internal<Reduce>(
43✔
990
               parent, INSERT_AT_END, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type
43✔
991
    )
43✔
992
        .first;
43✔
993
}
43✔
994

995
Reduce& StructuredSDFGBuilder::add_reduce_before(
996
    Sequence& parent,
997
    ControlFlowNode& child,
998
    const symbolic::Symbol indvar,
999
    const symbolic::Condition condition,
1000
    const symbolic::Expression init,
1001
    const symbolic::Expression update,
1002
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
1003
    const ScheduleType& schedule_type,
1004
    const sdfg::control_flow::Assignments& assignments,
1005
    const DebugInfo& debug_info
UNCOV
1006
) {
×
UNCOV
1007
    int index = parent.index(child);
×
UNCOV
1008
    if (index == -1) {
×
UNCOV
1009
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
1010
    }
×
1011

UNCOV
1012
    return insert_node_internal<
×
UNCOV
1013
               Reduce>(parent, index, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type)
×
UNCOV
1014
        .first;
×
UNCOV
1015
}
×
1016

1017
Reduce& StructuredSDFGBuilder::add_reduce_after(
1018
    Sequence& parent,
1019
    ControlFlowNode& child,
1020
    const symbolic::Symbol indvar,
1021
    const symbolic::Condition condition,
1022
    const symbolic::Expression init,
1023
    const symbolic::Expression update,
1024
    const std::vector<structured_control_flow::ReductionInfo>& reductions,
1025
    const ScheduleType& schedule_type,
1026
    const sdfg::control_flow::Assignments& assignments,
1027
    const DebugInfo& debug_info
1028
) {
4✔
1029
    int index = parent.index(child);
4✔
1030
    if (index == -1) {
4✔
UNCOV
1031
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
1032
    }
×
1033

1034
    return insert_node_internal<Reduce>(
4✔
1035
               parent, index + 1, &assignments, debug_info, indvar, init, update, condition, reductions, schedule_type
4✔
1036
    )
4✔
1037
        .first;
4✔
1038
}
4✔
1039

1040
Continue& StructuredSDFGBuilder::
1041
    add_continue(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
27✔
1042
    return insert_node_internal<Continue>(parent, INSERT_AT_END, &assignments, debug_info).first;
27✔
1043
}
27✔
1044

1045
Break& StructuredSDFGBuilder::
1046
    add_break(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
28✔
1047
    return insert_node_internal<Break>(parent, INSERT_AT_END, &assignments, debug_info).first;
28✔
1048
}
28✔
1049

1050
Return& StructuredSDFGBuilder::add_return(
1051
    Sequence& parent,
1052
    const std::string& data,
1053
    const sdfg::control_flow::Assignments& assignments,
1054
    const DebugInfo& debug_info
1055
) {
56✔
1056
    return insert_node_internal<Return>(parent, INSERT_AT_END, &assignments, debug_info, data).first;
56✔
1057
}
56✔
1058

1059
Return& StructuredSDFGBuilder::add_constant_return(
1060
    Sequence& parent,
1061
    const std::string& data,
1062
    const types::IType& type,
1063
    const sdfg::control_flow::Assignments& assignments,
1064
    const DebugInfo& debug_info
1065
) {
×
1066
    return insert_node_internal<Return>(parent, INSERT_AT_END, &assignments, debug_info, data, type).first;
×
1067
}
×
1068

1069
For& StructuredSDFGBuilder::convert_while(
1070
    Sequence& parent,
1071
    While& loop,
1072
    const symbolic::Symbol indvar,
1073
    const symbolic::Condition condition,
1074
    const symbolic::Expression init,
1075
    const symbolic::Expression update
1076
) {
×
1077
    int index = parent.index(loop);
×
1078
    if (index == -1) {
×
1079
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
1080
    }
×
1081

1082
    auto iter = parent.children_.begin() + index;
×
UNCOV
1083
    auto& new_iter = *parent.children_.insert(
×
1084
        iter + 1,
×
1085
        std::unique_ptr<For>(new For(
×
UNCOV
1086
            this->new_element_id_batch(For::REQUIRED_ELEMENT_IDS),
×
UNCOV
1087
            loop.debug_info(),
×
1088
            &parent,
×
UNCOV
1089
            indvar,
×
1090
            init,
×
1091
            update,
×
UNCOV
1092
            condition
×
UNCOV
1093
        ))
×
UNCOV
1094
    );
×
1095

1096
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
1097
    this->move_children(loop.root(), for_loop.root());
×
1098

1099
    // Remove while loop
UNCOV
1100
    parent.children_.erase(parent.children_.begin() + index);
×
1101

UNCOV
1102
    return for_loop;
×
UNCOV
1103
};
×
1104

1105
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop) {
77✔
1106
    int index = parent.index(loop);
77✔
1107
    if (index == -1) {
77✔
UNCOV
1108
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
1109
    }
×
1110

1111
    auto iter = parent.children_.begin() + index;
77✔
1112
    auto& new_iter = *parent.children_.insert(
77✔
1113
        iter + 1,
77✔
1114
        std::unique_ptr<Map>(new Map(
77✔
1115
            this->new_element_id_batch(Map::REQUIRED_ELEMENT_IDS),
77✔
1116
            loop.debug_info(),
77✔
1117
            &parent,
77✔
1118
            loop.indvar(),
77✔
1119
            loop.init(),
77✔
1120
            loop.update(),
77✔
1121
            loop.condition(),
77✔
1122
            ScheduleType_Sequential::create()
77✔
1123
        ))
77✔
1124
    );
77✔
1125

1126
    auto& map = dynamic_cast<Map&>(*new_iter);
77✔
1127
    this->move_children(loop.root(), map.root());
77✔
1128

1129
    // Remove for loop
1130
    parent.children_.erase(parent.children_.begin() + index);
77✔
1131

1132
    return map;
77✔
1133
};
77✔
1134

1135
Reduce& StructuredSDFGBuilder::convert_for_to_reduce(
1136
    Sequence& parent, For& loop, const std::vector<structured_control_flow::ReductionInfo>& reductions
1137
) {
26✔
1138
    int index = parent.index(loop);
26✔
1139
    if (index == -1) {
26✔
UNCOV
1140
        throw InvalidSDFGException("StructuredSDFGBuilder: Child not found");
×
UNCOV
1141
    }
×
1142

1143
    auto iter = parent.children_.begin() + index;
26✔
1144
    auto& new_iter = *parent.children_.insert(
26✔
1145
        iter + 1,
26✔
1146
        std::unique_ptr<Reduce>(new Reduce(
26✔
1147
            this->new_element_id_batch(Reduce::REQUIRED_ELEMENT_IDS),
26✔
1148
            loop.debug_info(),
26✔
1149
            &parent,
26✔
1150
            loop.indvar(),
26✔
1151
            loop.init(),
26✔
1152
            loop.update(),
26✔
1153
            loop.condition(),
26✔
1154
            reductions,
26✔
1155
            ScheduleType_Sequential::create()
26✔
1156
        ))
26✔
1157
    );
26✔
1158

1159
    auto& reduce = dynamic_cast<Reduce&>(*new_iter);
26✔
1160
    this->move_children(loop.root(), reduce.root());
26✔
1161

1162
    // Remove for loop
1163
    parent.children_.erase(parent.children_.begin() + index);
26✔
1164

1165
    return reduce;
26✔
1166
};
26✔
1167

UNCOV
1168
void StructuredSDFGBuilder::update_if_else_condition(IfElse& if_else, size_t index, const symbolic::Condition condition) {
×
UNCOV
1169
    if (index >= if_else.conditions_.size()) {
×
UNCOV
1170
        throw InvalidSDFGException("StructuredSDFGBuilder: Index out of range");
×
UNCOV
1171
    }
×
UNCOV
1172
    if_else.conditions_.at(index) = condition;
×
UNCOV
1173
};
×
1174

1175
void StructuredSDFGBuilder::update_loop(
1176
    StructuredLoop& loop,
1177
    const symbolic::Symbol indvar,
1178
    const symbolic::Condition condition,
1179
    const symbolic::Expression init,
1180
    const symbolic::Expression update
1181
) {
119✔
1182
    loop.indvar_ = indvar;
119✔
1183
    loop.condition_ = condition;
119✔
1184
    loop.init_ = init;
119✔
1185
    loop.update_ = update;
119✔
1186
};
119✔
1187

1188
void StructuredSDFGBuilder::update_schedule_type(StructuredLoop& loop, const ScheduleType& schedule_type) {
36✔
1189
    loop.schedule_type_ = schedule_type;
36✔
1190
}
36✔
1191

1192
/***** Section: Dataflow Graph *****/
1193

1194
data_flow::AccessNode& StructuredSDFGBuilder::
1195
    add_access(structured_control_flow::Block& block, const std::string& data, const DebugInfo& debug_info) {
7,732✔
1196
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
7,732✔
1197
    auto res = block.dataflow_->nodes_.insert(
7,732✔
1198
        {vertex,
7,732✔
1199
         std::unique_ptr<data_flow::AccessNode>(
7,732✔
1200
             new data_flow::AccessNode(this->new_element_id(), debug_info, vertex, block.dataflow(), data)
7,732✔
1201
         )}
7,732✔
1202
    );
7,732✔
1203

1204
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
7,732✔
1205
};
7,732✔
1206

1207
data_flow::ConstantNode& StructuredSDFGBuilder::add_constant(
1208
    structured_control_flow::Block& block, const std::string& data, const types::IType& type, const DebugInfo& debug_info
1209
) {
435✔
1210
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
435✔
1211
    auto res = block.dataflow_->nodes_.insert(
435✔
1212
        {vertex,
435✔
1213
         std::unique_ptr<data_flow::ConstantNode>(
435✔
1214
             new data_flow::ConstantNode(this->new_element_id(), debug_info, vertex, block.dataflow(), data, type)
435✔
1215
         )}
435✔
1216
    );
435✔
1217

1218
    return dynamic_cast<data_flow::ConstantNode&>(*(res.first->second));
435✔
1219
};
435✔
1220

1221

1222
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
1223
    structured_control_flow::Block& block,
1224
    const data_flow::TaskletCode code,
1225
    const std::string& output,
1226
    const std::vector<std::string>& inputs,
1227
    const DebugInfo& debug_info
1228
) {
1,766✔
1229
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
1,766✔
1230
    auto res = block.dataflow_->nodes_.insert(
1,766✔
1231
        {vertex,
1,766✔
1232
         std::unique_ptr<data_flow::Tasklet>(
1,766✔
1233
             new data_flow::Tasklet(this->new_element_id(), debug_info, vertex, block.dataflow(), code, output, inputs)
1,766✔
1234
         )}
1,766✔
1235
    );
1,766✔
1236

1237
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
1,766✔
1238
};
1,766✔
1239

1240
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
1241
    structured_control_flow::Block& block,
1242
    data_flow::DataFlowNode& src,
1243
    const std::string& src_conn,
1244
    data_flow::DataFlowNode& dst,
1245
    const std::string& dst_conn,
1246
    const data_flow::Subset& subset,
1247
    const types::IType& base_type,
1248
    const DebugInfo& debug_info
1249
) {
8,465✔
1250
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
8,465✔
1251
    auto res = block.dataflow_->edges_.insert(
8,465✔
1252
        {edge.first,
8,465✔
1253
         std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
8,465✔
1254
             this->new_element_id(), debug_info, edge.first, block.dataflow(), src, src_conn, dst, dst_conn, subset, base_type
8,465✔
1255
         ))}
8,465✔
1256
    );
8,465✔
1257

1258
    auto& memlet = dynamic_cast<data_flow::Memlet&>(*(res.first->second));
8,465✔
1259
#ifndef NDEBUG
8,465✔
1260
    memlet.validate(*this->structured_sdfg_);
8,465✔
1261
#endif
8,465✔
1262

1263
    return memlet;
8,465✔
1264
};
8,465✔
1265

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

1278
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1279
    structured_control_flow::Block& block,
1280
    data_flow::CodeNode& src,
1281
    const std::string& src_conn,
1282
    data_flow::AccessNode& dst,
1283
    const data_flow::Subset& subset,
1284
    const types::IType& base_type,
1285
    const DebugInfo& debug_info
1286
) {
1,602✔
1287
    return this->add_memlet(block, src, src_conn, dst, "void", subset, base_type, debug_info);
1,602✔
1288
};
1,602✔
1289

1290
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1291
    structured_control_flow::Block& block,
1292
    data_flow::AccessNode& src,
1293
    data_flow::Tasklet& dst,
1294
    const std::string& dst_conn,
1295
    const data_flow::Subset& subset,
1296
    const DebugInfo& debug_info
1297
) {
1,111✔
1298
    const types::IType* src_type = nullptr;
1,111✔
1299
    if (auto cnode = dynamic_cast<data_flow::ConstantNode*>(&src)) {
1,111✔
1300
        src_type = &cnode->type();
241✔
1301
    } else {
870✔
1302
        src_type = &this->structured_sdfg_->type(src.data());
870✔
1303
    }
870✔
1304
    auto base_type = types::infer_type(*this->structured_sdfg_, *src_type, subset);
1,111✔
1305
    if (base_type->type_id() != types::TypeID::Scalar) {
1,111✔
UNCOV
1306
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
UNCOV
1307
    }
×
1308
    return this->add_memlet(block, src, "void", dst, dst_conn, subset, *src_type, debug_info);
1,111✔
1309
};
1,111✔
1310

1311
data_flow::Memlet& StructuredSDFGBuilder::add_computational_memlet(
1312
    structured_control_flow::Block& block,
1313
    data_flow::Tasklet& src,
1314
    const std::string& src_conn,
1315
    data_flow::AccessNode& dst,
1316
    const data_flow::Subset& subset,
1317
    const DebugInfo& debug_info
1318
) {
721✔
1319
    auto& dst_type = this->structured_sdfg_->type(dst.data());
721✔
1320
    auto base_type = types::infer_type(*this->structured_sdfg_, dst_type, subset);
721✔
1321
    if (base_type->type_id() != types::TypeID::Scalar) {
721✔
UNCOV
1322
        throw InvalidSDFGException("Computational memlet must have a scalar type");
×
UNCOV
1323
    }
×
1324
    return this->add_memlet(block, src, src_conn, dst, "void", subset, dst_type, debug_info);
721✔
1325
};
721✔
1326

1327
data_flow::Memlet& StructuredSDFGBuilder::add_reference_memlet(
1328
    structured_control_flow::Block& block,
1329
    data_flow::AccessNode& src,
1330
    data_flow::AccessNode& dst,
1331
    const data_flow::Subset& subset,
1332
    const types::IType& base_type,
1333
    const DebugInfo& debug_info
1334
) {
120✔
1335
    return this->add_memlet(block, src, "void", dst, "ref", subset, base_type, debug_info);
120✔
1336
};
120✔
1337

1338
data_flow::Memlet& StructuredSDFGBuilder::add_dereference_memlet(
1339
    structured_control_flow::Block& block,
1340
    data_flow::AccessNode& src,
1341
    data_flow::AccessNode& dst,
1342
    bool derefs_src,
1343
    const types::IType& base_type,
1344
    const DebugInfo& debug_info
1345
) {
23✔
1346
    if (derefs_src) {
23✔
1347
        return this->add_memlet(block, src, "void", dst, "deref", {symbolic::zero()}, base_type, debug_info);
14✔
1348
    } else {
14✔
1349
        return this->add_memlet(block, src, "deref", dst, "void", {symbolic::zero()}, base_type, debug_info);
9✔
1350
    }
9✔
1351
};
23✔
1352

1353
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block, const data_flow::Memlet& edge) {
158✔
1354
    auto& graph = block.dataflow();
158✔
1355
    auto e = edge.edge();
158✔
1356
    boost::remove_edge(e, graph.graph_);
158✔
1357
    graph.edges_.erase(e);
158✔
1358
};
158✔
1359

1360
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block, const data_flow::DataFlowNode& node) {
91✔
1361
    auto& graph = block.dataflow();
91✔
1362
    auto v = node.vertex();
91✔
1363
    boost::remove_vertex(v, graph.graph_);
91✔
1364
    graph.nodes_.erase(v);
91✔
1365
};
91✔
1366

1367
void StructuredSDFGBuilder::clear_code_node_legacy(structured_control_flow::Block& block, const data_flow::CodeNode& node) {
666✔
1368
    auto& graph = block.dataflow();
666✔
1369

1370
    std::unordered_set<const data_flow::DataFlowNode*> to_delete = {&node};
666✔
1371

1372
    // Delete incoming
1373
    std::list<const data_flow::Memlet*> iedges;
666✔
1374
    for (auto& iedge : graph.in_edges(node)) {
1,669✔
1375
        iedges.push_back(&iedge);
1,669✔
1376
    }
1,669✔
1377
    for (auto iedge : iedges) {
1,669✔
1378
        auto& src = iedge->src();
1,669✔
1379
        to_delete.insert(&src);
1,669✔
1380

1381
        auto edge = iedge->edge();
1,669✔
1382
        graph.edges_.erase(edge);
1,669✔
1383
        boost::remove_edge(edge, graph.graph_);
1,669✔
1384
    }
1,669✔
1385

1386
    // Delete outgoing
1387
    std::list<const data_flow::Memlet*> oedges;
666✔
1388
    for (auto& oedge : graph.out_edges(node)) {
666✔
1389
        oedges.push_back(&oedge);
44✔
1390
    }
44✔
1391
    for (auto oedge : oedges) {
666✔
1392
        auto& dst = oedge->dst();
44✔
1393
        to_delete.insert(&dst);
44✔
1394

1395
        auto edge = oedge->edge();
44✔
1396
        graph.edges_.erase(edge);
44✔
1397
        boost::remove_edge(edge, graph.graph_);
44✔
1398
    }
44✔
1399

1400
    // Delete nodes
1401
    for (auto obsolete_node : to_delete) {
2,366✔
1402
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
2,366✔
1403
            auto vertex = obsolete_node->vertex();
2,366✔
1404
            graph.nodes_.erase(vertex);
2,366✔
1405
            boost::remove_vertex(vertex, graph.graph_);
2,366✔
1406
        }
2,366✔
1407
    }
2,366✔
1408
}
666✔
1409

1410
void StructuredSDFGBuilder::
1411
    clear_access_node_legacy(structured_control_flow::Block& block, const data_flow::AccessNode& node) {
3✔
1412
    auto& graph = block.dataflow();
3✔
1413

1414
    std::list<const data_flow::Memlet*> tmp;
3✔
1415
    std::list<const data_flow::DataFlowNode*> queue = {&node};
3✔
1416
    while (!queue.empty()) {
9✔
1417
        auto current = queue.front();
6✔
1418
        queue.pop_front();
6✔
1419
        if (current != &node) {
6✔
1420
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
3✔
UNCOV
1421
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
UNCOV
1422
                    continue;
×
UNCOV
1423
                }
×
UNCOV
1424
            }
×
1425
        }
3✔
1426

1427
        tmp.clear();
6✔
1428
        for (auto& iedge : graph.in_edges(*current)) {
6✔
1429
            tmp.push_back(&iedge);
3✔
1430
        }
3✔
1431
        for (auto iedge : tmp) {
6✔
1432
            auto& src = iedge->src();
3✔
1433
            queue.push_back(&src);
3✔
1434

1435
            auto edge = iedge->edge();
3✔
1436
            graph.edges_.erase(edge);
3✔
1437
            boost::remove_edge(edge, graph.graph_);
3✔
1438
        }
3✔
1439

1440
        if (current != &node || graph.out_degree(*current) == 0) {
6✔
1441
            auto vertex = current->vertex();
6✔
1442
            graph.nodes_.erase(vertex);
6✔
1443
            boost::remove_vertex(vertex, graph.graph_);
6✔
1444
        }
6✔
1445
    }
6✔
1446
}
3✔
1447

1448
int StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block, const data_flow::DataFlowNode& node) {
13✔
1449
    return clear_node(block, node, {&node});
13✔
1450
}
13✔
1451

1452
int StructuredSDFGBuilder::clear_node(
1453
    structured_control_flow::Block& block,
1454
    const data_flow::DataFlowNode& node,
1455
    const std::unordered_set<const data_flow::DataFlowNode*>& ignore_side_effects
1456
) {
13✔
1457
    auto& graph = block.dataflow();
13✔
1458

1459
    std::list<const data_flow::Memlet*> tmp;
13✔
1460
    std::list<const data_flow::DataFlowNode*> queue = {&node};
13✔
1461
    std::unordered_set<const data_flow::DataFlowNode*> remove_once_set;
13✔
1462
    std::unordered_set<const data_flow::DataFlowNode*> force_remove_set;
13✔
1463
    int removed_nodes = 0;
13✔
1464

1465
    do {
36✔
1466
        auto current = queue.front();
36✔
1467
        queue.pop_front();
36✔
1468

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

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

1474
            bool ignore_side_effects_on_current = ignore_side_effects.contains(current);
35✔
1475
            bool force_remove = force_remove_set.contains(current);
35✔
1476

1477
            // we can remove nodes without out-edges & side effects.
1478
            if ((no_more_consumers && !current->side_effect()) ||
35✔
1479
                ((ignore_side_effects_on_current || force_remove) && (no_more_consumers || access_node))) {
35✔
1480
                // Or for access-nodes on the ignore list, we can remove the write side (will not remove the node, only
1481
                // inputs) For any other node on the ignore list, we can ignore if it has side effects or not
1482
                tmp.clear();
28✔
1483
                for (auto& iedge : graph.in_edges(*current)) {
28✔
1484
                    tmp.push_back(&iedge);
25✔
1485
                }
25✔
1486
                bool no_in_edges = true;
28✔
1487
                for (auto iedge : tmp) {
28✔
1488
                    auto& src = iedge->src();
25✔
1489

1490
                    auto edge_rem = src.can_remove_out_edge(graph, iedge);
25✔
1491
                    if (edge_rem != data_flow::EdgeRemoveOption::NotRemovable) {
25✔
1492
                        auto src_conn = iedge->src_conn();
23✔
1493
                        queue.push_back(&src);
23✔
1494
                        auto edge = iedge->edge();
23✔
1495
                        graph.edges_.erase(edge);
23✔
1496
                        boost::remove_edge(edge, graph.graph_);
23✔
1497
                        if (edge_rem == data_flow::EdgeRemoveOption::RequiresUpdate) {
23✔
UNCOV
1498
                            const_cast<data_flow::DataFlowNode&>(src).update_edge_removed(src_conn);
×
1499
                        } else if (edge_rem == data_flow::EdgeRemoveOption::RemoveNodeAfter) {
23✔
1500
                            force_remove_set.insert(&src);
7✔
1501
                        }
7✔
1502
                    } else {
23✔
1503
                        no_in_edges = false;
2✔
1504
                    }
2✔
1505
                }
25✔
1506

1507
                if (no_more_consumers && no_in_edges) {
28✔
1508
                    remove_once_set.insert(current);
25✔
1509
                    auto vertex = current->vertex();
25✔
1510
                    graph.nodes_.erase(vertex);
25✔
1511
                    boost::remove_vertex(vertex, graph.graph_);
25✔
1512
                    ++removed_nodes;
25✔
1513
                } else if (!no_more_consumers && force_remove) {
25✔
UNCOV
1514
                    throw std::runtime_error(
×
UNCOV
1515
                        "Not yet supported: RemoveNodeAfter with more than 1 edge: #" +
×
UNCOV
1516
                        std::to_string(current->element_id())
×
UNCOV
1517
                    );
×
UNCOV
1518
                }
×
1519
            }
28✔
1520
        }
35✔
1521
    } while (!queue.empty());
36✔
1522

1523
    return removed_nodes;
13✔
1524
}
13✔
1525

1526
int StructuredSDFGBuilder::clear_ptr_borrow_edge(Block& block, const data_flow::Memlet& edge) {
1✔
1527
    auto& graph = block.dataflow();
1✔
1528
    auto& src = edge.src();
1✔
1529
    auto& dst = edge.dst();
1✔
1530

1531
    auto edge_removal = dst.can_remove_in_edge(graph, &edge);
1✔
1532
    int removed = 0;
1✔
1533
    if (edge_removal == data_flow::EdgeRemoveOption::Trivially) {
1✔
UNCOV
1534
        remove_memlet(block, edge);
×
UNCOV
1535
        ++removed;
×
1536
    } else if (edge_removal == data_flow::EdgeRemoveOption::RemoveNodeAfter) {
1✔
1537
        removed = 1 + clear_node(block, dst);
1✔
1538
    } else if (edge_removal == data_flow::EdgeRemoveOption::RequiresUpdate) {
1✔
UNCOV
1539
        remove_memlet(block, edge);
×
UNCOV
1540
        const_cast<data_flow::DataFlowNode&>(dst).update_edge_removed(edge.dst_conn());
×
UNCOV
1541
    } else if (edge_removal == data_flow::EdgeRemoveOption::NotRemovable) {
×
UNCOV
1542
        return 0;
×
UNCOV
1543
    }
×
1544

1545
    return removed;
1✔
1546
}
1✔
1547

1548
void StructuredSDFGBuilder::add_dataflow(const data_flow::DataFlowGraph& from, Block& to) {
19✔
1549
    auto& to_dataflow = to.dataflow();
19✔
1550

1551
    std::unordered_map<graph::Vertex, graph::Vertex> node_mapping;
19✔
1552
    for (auto& entry : from.nodes_) {
21✔
1553
        auto vertex = boost::add_vertex(to_dataflow.graph_);
21✔
1554
        to_dataflow.nodes_.insert({vertex, entry.second->clone(this->new_element_id(), vertex, to_dataflow)});
21✔
1555
        node_mapping.insert({entry.first, vertex});
21✔
1556
    }
21✔
1557

1558
    for (auto& entry : from.edges_) {
19✔
1559
        auto src = node_mapping[entry.second->src().vertex()];
14✔
1560
        auto dst = node_mapping[entry.second->dst().vertex()];
14✔
1561

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

1564
        to_dataflow.edges_.insert(
14✔
1565
            {edge.first,
14✔
1566
             entry.second->clone(
14✔
1567
                 this->new_element_id(), edge.first, to_dataflow, *to_dataflow.nodes_[src], *to_dataflow.nodes_[dst]
14✔
1568
             )}
14✔
1569
        );
14✔
1570
    }
14✔
1571
};
19✔
1572

1573
void StructuredSDFGBuilder::merge_siblings(data_flow::AccessNode& source_node) {
21✔
1574
    auto& user_graph = source_node.get_parent();
21✔
1575
    auto* block = dyn_cast<structured_control_flow::Block*>(user_graph.get_parent());
21✔
1576
    if (!block) {
21✔
1577
        throw InvalidSDFGException("Parent of user graph must be a block!");
×
1578
    }
×
1579

1580
    // Merge access nodes if they access the same container on a code node
1581
    for (auto& oedge : user_graph.out_edges(source_node)) {
21✔
1582
        if (auto* code_node = dynamic_cast<data_flow::CodeNode*>(&oedge.dst())) {
15✔
1583
            std::unordered_set<data_flow::Memlet*> iedges;
8✔
1584
            for (auto& iedge : user_graph.in_edges(*code_node)) {
10✔
1585
                iedges.insert(&iedge);
10✔
1586
            }
10✔
1587
            for (auto* iedge : iedges) {
10✔
1588
                if (dynamic_cast<data_flow::ConstantNode*>(&iedge->src())) {
10✔
UNCOV
1589
                    continue;
×
UNCOV
1590
                }
×
1591
                auto* access_node = static_cast<data_flow::AccessNode*>(&iedge->src());
10✔
1592
                if (access_node == &source_node || access_node->data() != source_node.data()) {
10✔
1593
                    continue;
9✔
1594
                }
9✔
1595
                this->add_memlet(
1✔
1596
                    *block,
1✔
1597
                    source_node,
1✔
1598
                    iedge->src_conn(),
1✔
1599
                    *code_node,
1✔
1600
                    iedge->dst_conn(),
1✔
1601
                    iedge->subset(),
1✔
1602
                    iedge->base_type(),
1✔
1603
                    iedge->debug_info()
1✔
1604
                );
1✔
1605
                this->remove_memlet(*block, *iedge);
1✔
1606
                source_node.set_debug_info(DebugInfo::merge(source_node.debug_info(), access_node->debug_info()));
1✔
1607
            }
1✔
1608
        }
8✔
1609
    }
15✔
1610

1611
    // Also merge "output" access nodes if they access the same container on a library node
1612
    for (auto& iedge : user_graph.in_edges(source_node)) {
21✔
1613
        if (auto* libnode = dynamic_cast<data_flow::LibraryNode*>(&iedge.src())) {
7✔
1614
            std::unordered_set<data_flow::Memlet*> oedges;
×
1615
            for (auto& oedge : user_graph.out_edges(*libnode)) {
×
1616
                oedges.insert(&oedge);
×
1617
            }
×
1618
            for (auto* oedge : oedges) {
×
1619
                auto* access_node = static_cast<data_flow::AccessNode*>(&oedge->dst());
×
1620
                if (access_node == &source_node || access_node->data() != source_node.data()) {
×
1621
                    continue;
×
1622
                }
×
1623
                this->add_memlet(
×
1624
                    *block,
×
UNCOV
1625
                    *libnode,
×
UNCOV
1626
                    oedge->src_conn(),
×
UNCOV
1627
                    source_node,
×
UNCOV
1628
                    oedge->dst_conn(),
×
UNCOV
1629
                    oedge->subset(),
×
UNCOV
1630
                    oedge->base_type(),
×
UNCOV
1631
                    oedge->debug_info()
×
UNCOV
1632
                );
×
UNCOV
1633
                this->remove_memlet(*block, *oedge);
×
UNCOV
1634
                source_node.set_debug_info(DebugInfo::merge(source_node.debug_info(), access_node->debug_info()));
×
UNCOV
1635
            }
×
UNCOV
1636
        }
×
1637
    }
7✔
1638
}
21✔
1639

1640
void StructuredSDFGBuilder::merge_sinks(structured_control_flow::Block& block) {
6✔
1641
    auto& graph = block.dataflow();
6✔
1642

1643
    // Collect sink access nodes (modifying the graph during iteration is not safe)
1644
    std::vector<data_flow::AccessNode*> sink_access_nodes;
6✔
1645
    for (auto node : graph.sinks()) {
9✔
1646
        if (auto* access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
9✔
1647
            sink_access_nodes.push_back(access_node);
9✔
1648
        }
9✔
1649
    }
9✔
1650

1651
    // Merge sink access nodes that refer to the same container
1652
    std::unordered_map<std::string, data_flow::AccessNode*> representatives;
6✔
1653
    for (auto* access_node : sink_access_nodes) {
9✔
1654
        auto it = representatives.find(access_node->data());
9✔
1655
        if (it == representatives.end()) {
9✔
1656
            representatives.insert({access_node->data(), access_node});
8✔
1657
            continue;
8✔
1658
        }
8✔
1659

1660
        auto& rep = *it->second;
1✔
1661

1662
        // Redirect all incoming memlets of this node onto the representative
1663
        std::unordered_set<data_flow::Memlet*> iedges;
1✔
1664
        for (auto& iedge : graph.in_edges(*access_node)) {
1✔
1665
            iedges.insert(&iedge);
1✔
1666
        }
1✔
1667
        for (auto* iedge : iedges) {
1✔
1668
            this->add_memlet(
1✔
1669
                block,
1✔
1670
                iedge->src(),
1✔
1671
                iedge->src_conn(),
1✔
1672
                rep,
1✔
1673
                iedge->dst_conn(),
1✔
1674
                iedge->subset(),
1✔
1675
                iedge->base_type(),
1✔
1676
                iedge->debug_info()
1✔
1677
            );
1✔
1678
            this->remove_memlet(block, *iedge);
1✔
1679
        }
1✔
1680

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

1683
        // The node is now isolated and can be removed
1684
        this->remove_node(block, *access_node);
1✔
1685
    }
1✔
1686
}
6✔
1687

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

© 2026 Coveralls, Inc