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

daisytuner / sdfglib / 15973046991

30 Jun 2025 12:34PM UTC coverage: 64.64% (-0.2%) from 64.86%
15973046991

push

github

web-flow
Merge pull request #123 from daisytuner/simplify-tiling-transformation

Avoid deep copy in loop tiling

31 of 32 new or added lines in 2 files covered. (96.88%)

27 existing lines in 6 files now uncovered.

8643 of 13371 relevant lines covered (64.64%)

171.55 hits per line

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

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

3
#include <cstddef>
4

5
#include "sdfg/analysis/scope_analysis.h"
6
#include "sdfg/codegen/language_extensions/cpp_language_extension.h"
7
#include "sdfg/data_flow/library_node.h"
8
#include "sdfg/structured_control_flow/map.h"
9
#include "sdfg/structured_control_flow/sequence.h"
10
#include "sdfg/types/utils.h"
11

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

15
namespace sdfg {
16
namespace builder {
17

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

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

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

41
    return nodes;
1✔
42
};
1✔
43

44
bool post_dominates(
6✔
45
    const State* pdom, const State* node,
46
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree) {
47
    if (pdom == node) {
6✔
48
        return true;
1✔
49
    }
50

51
    auto current = pdom_tree.at(node);
5✔
52
    while (current != nullptr) {
9✔
53
        if (current == pdom) {
9✔
54
            return true;
5✔
55
        }
56
        current = pdom_tree.at(current);
4✔
57
    }
58

59
    return false;
×
60
}
6✔
61

62
const control_flow::State* StructuredSDFGBuilder::find_end_of_if_else(
3✔
63
    const SDFG& sdfg, const State* current, std::vector<const InterstateEdge*>& out_edges,
64
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree) {
65
    // Best-effort approach: Check if post-dominator of current dominates all out edges
66
    auto pdom = pdom_tree.at(current);
3✔
67
    for (auto& edge : out_edges) {
9✔
68
        if (!post_dominates(pdom, &edge->dst(), pdom_tree)) {
6✔
69
            return nullptr;
×
70
        }
71
    }
72

73
    return pdom;
3✔
74
}
3✔
75

76
void StructuredSDFGBuilder::traverse(const SDFG& sdfg) {
4✔
77
    // Start of SDFGS
78
    Sequence& root = *structured_sdfg_->root_;
4✔
79
    const State* start_state = &sdfg.start_state();
4✔
80

81
    auto pdom_tree = sdfg.post_dominator_tree();
4✔
82

83
    std::unordered_set<const InterstateEdge*> breaks;
4✔
84
    std::unordered_set<const InterstateEdge*> continues;
4✔
85
    for (auto& edge : sdfg.back_edges()) {
5✔
86
        continues.insert(edge);
1✔
87
    }
88

89
    std::unordered_set<const control_flow::State*> visited;
4✔
90
    this->traverse_with_loop_detection(sdfg, root, start_state, nullptr, continues, breaks,
4✔
91
                                       pdom_tree, visited);
92
};
4✔
93

94
void StructuredSDFGBuilder::traverse_with_loop_detection(
9✔
95
    const SDFG& sdfg, Sequence& scope, const State* current, const State* end,
96
    const std::unordered_set<const InterstateEdge*>& continues,
97
    const std::unordered_set<const InterstateEdge*>& breaks,
98
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree,
99
    std::unordered_set<const control_flow::State*>& visited) {
100
    if (current == end) {
9✔
101
        return;
1✔
102
    }
103

104
    auto in_edges = sdfg.in_edges(*current);
8✔
105

106
    // Loop detection
107
    std::unordered_set<const InterstateEdge*> loop_edges;
8✔
108
    for (auto& iedge : in_edges) {
13✔
109
        if (continues.find(&iedge) != continues.end()) {
5✔
110
            loop_edges.insert(&iedge);
1✔
111
        }
1✔
112
    }
113
    if (!loop_edges.empty()) {
8✔
114
        // 1. Determine nodes of loop body
115
        std::unordered_set<const control_flow::State*> body;
1✔
116
        for (auto back_edge : loop_edges) {
2✔
117
            auto loop_nodes = this->determine_loop_nodes(sdfg, back_edge->src(), back_edge->dst());
1✔
118
            body.insert(loop_nodes.begin(), loop_nodes.end());
1✔
119
        }
1✔
120

121
        // 2. Determine exit states and exit edges
122
        std::unordered_set<const control_flow::State*> exit_states;
1✔
123
        std::unordered_set<const control_flow::InterstateEdge*> exit_edges;
1✔
124
        for (auto node : body) {
4✔
125
            for (auto& edge : sdfg.out_edges(*node)) {
7✔
126
                if (body.find(&edge.dst()) == body.end()) {
4✔
127
                    exit_edges.insert(&edge);
1✔
128
                    exit_states.insert(&edge.dst());
1✔
129
                }
1✔
130
            }
131
        }
132
        if (exit_states.size() != 1) {
1✔
133
            throw UnstructuredControlFlowException();
×
134
        }
135
        const control_flow::State* exit_state = *exit_states.begin();
1✔
136

137
        for (auto& edge : breaks) {
1✔
138
            exit_edges.insert(edge);
×
139
        }
140

141
        // Collect debug information (could be removed when this is computed dynamically)
142
        DebugInfo dbg_info = current->debug_info();
1✔
143
        for (auto& edge : in_edges) {
3✔
144
            dbg_info = DebugInfo::merge(dbg_info, edge.debug_info());
2✔
145
        }
146
        for (auto node : body) {
4✔
147
            dbg_info = DebugInfo::merge(dbg_info, node->debug_info());
3✔
148
        }
149
        for (auto edge : exit_edges) {
2✔
150
            dbg_info = DebugInfo::merge(dbg_info, edge->debug_info());
1✔
151
        }
152

153
        // 3. Add while loop
154
        While& loop = this->add_while(scope, {}, dbg_info);
1✔
155

156
        std::unordered_set<const control_flow::State*> loop_visited(visited);
1✔
157
        this->traverse_without_loop_detection(sdfg, loop.root(), current, exit_state, continues,
1✔
158
                                              exit_edges, pdom_tree, loop_visited);
1✔
159

160
        this->traverse_with_loop_detection(sdfg, scope, exit_state, end, continues, breaks,
2✔
161
                                           pdom_tree, visited);
1✔
162
    } else {
1✔
163
        this->traverse_without_loop_detection(sdfg, scope, current, end, continues, breaks,
14✔
164
                                              pdom_tree, visited);
7✔
165
    }
166
};
9✔
167

168
void StructuredSDFGBuilder::traverse_without_loop_detection(
8✔
169
    const SDFG& sdfg, Sequence& scope, const State* current, const State* end,
170
    const std::unordered_set<const InterstateEdge*>& continues,
171
    const std::unordered_set<const InterstateEdge*>& breaks,
172
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree,
173
    std::unordered_set<const control_flow::State*>& visited) {
174
    std::list<const State*> queue = {current};
8✔
175
    while (!queue.empty()) {
26✔
176
        auto curr = queue.front();
18✔
177
        queue.pop_front();
18✔
178
        if (curr == end) {
18✔
179
            continue;
3✔
180
        }
181

182
        if (visited.find(curr) != visited.end()) {
15✔
183
            throw UnstructuredControlFlowException();
×
184
        }
185
        visited.insert(curr);
15✔
186

187
        auto out_edges = sdfg.out_edges(*curr);
15✔
188
        auto out_degree = sdfg.out_degree(*curr);
15✔
189

190
        // Case 1: Sink node
191
        if (out_degree == 0) {
15✔
192
            this->add_block(scope, curr->dataflow(), {}, curr->debug_info());
4✔
193
            this->add_return(scope, {}, curr->debug_info());
4✔
194
            continue;
4✔
195
        }
196

197
        // Case 2: Transition
198
        if (out_degree == 1) {
11✔
199
            auto& oedge = *out_edges.begin();
8✔
200
            if (!oedge.is_unconditional()) {
8✔
201
                throw UnstructuredControlFlowException();
×
202
            }
203
            this->add_block(scope, curr->dataflow(), oedge.assignments(), curr->debug_info());
8✔
204

205
            if (continues.find(&oedge) != continues.end()) {
8✔
206
                this->add_continue(scope, oedge.debug_info());
×
207
            } else if (breaks.find(&oedge) != breaks.end()) {
8✔
208
                this->add_break(scope, oedge.debug_info());
×
209
            } else {
×
210
                bool starts_loop = false;
8✔
211
                for (auto& iedge : sdfg.in_edges(oedge.dst())) {
19✔
212
                    if (continues.find(&iedge) != continues.end()) {
11✔
213
                        starts_loop = true;
×
214
                        break;
×
215
                    }
216
                }
217
                if (!starts_loop) {
8✔
218
                    queue.push_back(&oedge.dst());
8✔
219
                } else {
8✔
220
                    this->traverse_with_loop_detection(sdfg, scope, &oedge.dst(), end, continues,
×
221
                                                       breaks, pdom_tree, visited);
×
222
                }
223
            }
224
            continue;
8✔
225
        }
226

227
        // Case 3: Branches
228
        if (out_degree > 1) {
3✔
229
            this->add_block(scope, curr->dataflow(), {}, curr->debug_info());
3✔
230

231
            std::vector<const InterstateEdge*> out_edges_vec;
3✔
232
            for (auto& edge : out_edges) {
9✔
233
                out_edges_vec.push_back(&edge);
6✔
234
            }
235

236
            // Best-effort approach: Find end of if-else
237
            // If not found, the branches may repeat paths yielding a large SDFG
238
            const control_flow::State* local_end =
3✔
239
                this->find_end_of_if_else(sdfg, curr, out_edges_vec, pdom_tree);
3✔
240
            if (local_end == nullptr) {
3✔
241
                local_end = end;
×
242
            }
×
243

244
            auto& if_else = this->add_if_else(scope, curr->debug_info());
3✔
245
            for (size_t i = 0; i < out_degree; i++) {
9✔
246
                auto& out_edge = out_edges_vec[i];
6✔
247

248
                auto& branch =
6✔
249
                    this->add_case(if_else, out_edge->condition(), out_edge->debug_info());
6✔
250
                if (!out_edge->assignments().empty()) {
6✔
251
                    this->add_block(branch, out_edge->assignments(), out_edge->debug_info());
2✔
252
                }
2✔
253
                if (continues.find(out_edge) != continues.end()) {
6✔
254
                    this->add_continue(branch, out_edge->debug_info());
1✔
255
                } else if (breaks.find(out_edge) != breaks.end()) {
6✔
256
                    this->add_break(branch, out_edge->debug_info());
1✔
257
                } else {
1✔
258
                    std::unordered_set<const control_flow::State*> branch_visited(visited);
4✔
259
                    this->traverse_with_loop_detection(sdfg, branch, &out_edge->dst(), local_end,
4✔
260
                                                       continues, breaks, pdom_tree,
4✔
261
                                                       branch_visited);
262
                }
4✔
263
            }
6✔
264

265
            if (local_end != end) {
3✔
266
                bool starts_loop = false;
2✔
267
                for (auto& iedge : sdfg.in_edges(*local_end)) {
6✔
268
                    if (continues.find(&iedge) != continues.end()) {
4✔
269
                        starts_loop = true;
×
270
                        break;
×
271
                    }
272
                }
273
                if (!starts_loop) {
2✔
274
                    queue.push_back(local_end);
2✔
275
                } else {
2✔
276
                    this->traverse_with_loop_detection(sdfg, scope, local_end, end, continues,
×
277
                                                       breaks, pdom_tree, visited);
×
278
                }
279
            }
2✔
280
            continue;
281
        }
3✔
282
    }
283
}
8✔
284

285
Function& StructuredSDFGBuilder::function() const {
6,178✔
286
    return static_cast<Function&>(*this->structured_sdfg_);
6,178✔
287
};
288

289
StructuredSDFGBuilder::StructuredSDFGBuilder(std::unique_ptr<StructuredSDFG>& sdfg)
66✔
290
    : FunctionBuilder(), structured_sdfg_(std::move(sdfg)) {};
66✔
291

292
StructuredSDFGBuilder::StructuredSDFGBuilder(const std::string& name, FunctionType type)
501✔
293
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(name, type)) {};
501✔
294

295
StructuredSDFGBuilder::StructuredSDFGBuilder(const SDFG& sdfg)
4✔
296
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(sdfg.name(), sdfg.type())) {
4✔
297
    for (auto& entry : sdfg.structures_) {
4✔
298
        this->structured_sdfg_->structures_.insert({entry.first, entry.second->clone()});
×
299
    }
300

301
    for (auto& entry : sdfg.containers_) {
11✔
302
        this->structured_sdfg_->containers_.insert({entry.first, entry.second->clone()});
7✔
303
    }
304

305
    for (auto& arg : sdfg.arguments_) {
6✔
306
        this->structured_sdfg_->arguments_.push_back(arg);
2✔
307
    }
308

309
    for (auto& ext : sdfg.externals_) {
5✔
310
        this->structured_sdfg_->externals_.push_back(ext);
1✔
311
    }
312

313
    for (auto& entry : sdfg.assumptions_) {
9✔
314
        this->structured_sdfg_->assumptions_.insert({entry.first, entry.second});
5✔
315
    }
316

317
    for (auto& entry : sdfg.metadata_) {
6✔
318
        this->structured_sdfg_->metadata_[entry.first] = entry.second;
2✔
319
    }
320

321
    this->traverse(sdfg);
4✔
322
};
4✔
323

324
StructuredSDFG& StructuredSDFGBuilder::subject() const { return *this->structured_sdfg_; };
1,425✔
325

326
std::unique_ptr<StructuredSDFG> StructuredSDFGBuilder::move() {
392✔
327
    return std::move(this->structured_sdfg_);
392✔
328
};
329

330
Element* StructuredSDFGBuilder::find_element_by_id(const size_t& element_id) const {
9✔
331
    auto& sdfg = this->subject();
9✔
332
    std::list<Element*> queue = {&sdfg.root()};
9✔
333
    while (!queue.empty()) {
32✔
334
        auto current = queue.front();
32✔
335
        queue.pop_front();
32✔
336

337
        if (current->element_id() == element_id) {
32✔
338
            return current;
9✔
339
        }
340

341
        if (auto block_stmt = dynamic_cast<structured_control_flow::Block*>(current)) {
23✔
342
            auto& dataflow = block_stmt->dataflow();
×
343
            for (auto& node : dataflow.nodes()) {
×
344
                queue.push_back(&node);
×
345
            }
346
            for (auto& edge : dataflow.edges()) {
×
347
                queue.push_back(&edge);
×
348
            }
349
        } else if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
23✔
350
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
26✔
351
                queue.push_back(&sequence_stmt->at(i).first);
13✔
352
                queue.push_back(&sequence_stmt->at(i).second);
13✔
353
            }
13✔
354
        } else if (dynamic_cast<structured_control_flow::Return*>(current)) {
23✔
355
            // Do nothing
356
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
10✔
357
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
358
                queue.push_back(&if_else_stmt->at(i).first);
×
359
            }
×
360
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
10✔
361
            queue.push_back(&for_stmt->root());
×
362
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
10✔
363
            queue.push_back(&while_stmt->root());
×
364
        } else if (dynamic_cast<structured_control_flow::Continue*>(current)) {
10✔
365
            // Do nothing
366
        } else if (dynamic_cast<structured_control_flow::Break*>(current)) {
10✔
367
            // Do nothing
368
        } else if (auto map_stmt = dynamic_cast<structured_control_flow::Map*>(current)) {
10✔
369
            queue.push_back(&map_stmt->root());
5✔
370
        }
5✔
371
    }
372

373
    return nullptr;
×
374
};
9✔
375

376
Sequence& StructuredSDFGBuilder::add_sequence(Sequence& parent,
39✔
377
                                              const sdfg::control_flow::Assignments& assignments,
378
                                              const DebugInfo& debug_info) {
379
    parent.children_.push_back(
78✔
380
        std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info)));
39✔
381

382
    parent.transitions_.push_back(std::unique_ptr<Transition>(
78✔
383
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
39✔
384

385
    return static_cast<Sequence&>(*parent.children_.back().get());
39✔
386
};
×
387

388
std::pair<Sequence&, Transition&> StructuredSDFGBuilder::add_sequence_before(
8✔
389
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
390
    // Insert block before current block
391
    int index = -1;
8✔
392
    for (size_t i = 0; i < parent.children_.size(); i++) {
8✔
393
        if (parent.children_.at(i).get() == &block) {
8✔
394
            index = i;
8✔
395
            break;
8✔
396
        }
UNCOV
397
    }
×
398
    if (index == -1) {
8✔
399
        throw InvalidSDFGException("StructuredSDFGBuilder: Block not found");
×
400
    }
401

402
    parent.children_.insert(
16✔
403
        parent.children_.begin() + index,
8✔
404
        std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info)));
8✔
405

406
    parent.transitions_.insert(
16✔
407
        parent.transitions_.begin() + index,
8✔
408
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
8✔
409

410
    auto new_entry = parent.at(index);
8✔
411
    auto& new_block = dynamic_cast<structured_control_flow::Sequence&>(new_entry.first);
8✔
412

413
    return {new_block, new_entry.second};
8✔
414
};
×
415

416
void StructuredSDFGBuilder::remove_child(Sequence& parent, size_t i) {
9✔
417
    parent.children_.erase(parent.children_.begin() + i);
9✔
418
    parent.transitions_.erase(parent.transitions_.begin() + i);
9✔
419
};
9✔
420

421
void StructuredSDFGBuilder::remove_child(Sequence& parent, ControlFlowNode& child) {
12✔
422
    int index = -1;
12✔
423
    for (size_t i = 0; i < parent.children_.size(); i++) {
16✔
424
        if (parent.children_.at(i).get() == &child) {
16✔
425
            index = i;
12✔
426
            break;
12✔
427
        }
428
    }
4✔
429

430
    parent.children_.erase(parent.children_.begin() + index);
12✔
431
    parent.transitions_.erase(parent.transitions_.begin() + index);
12✔
432
};
12✔
433

434
void StructuredSDFGBuilder::insert_children(Sequence& parent, Sequence& other, size_t i) {
20✔
435
    parent.children_.insert(parent.children_.begin() + i,
40✔
436
                            std::make_move_iterator(other.children_.begin()),
20✔
437
                            std::make_move_iterator(other.children_.end()));
20✔
438
    parent.transitions_.insert(parent.transitions_.begin() + i,
40✔
439
                               std::make_move_iterator(other.transitions_.begin()),
20✔
440
                               std::make_move_iterator(other.transitions_.end()));
20✔
441
    for (auto& trans : parent.transitions_) {
44✔
442
        trans->parent_ = &parent;
24✔
443
    }
444
    other.children_.clear();
20✔
445
    other.transitions_.clear();
20✔
446
};
20✔
447

448
void StructuredSDFGBuilder::insert(ControlFlowNode& node, Sequence& source, Sequence& target,
8✔
449
                                   const DebugInfo& debug_info) {
450
    // Insert node into target sequence
451
    int index = -1;
8✔
452
    for (size_t i = 0; i < source.children_.size(); i++) {
16✔
453
        if (source.children_.at(i).get() == &node) {
16✔
454
            index = i;
8✔
455
            break;
8✔
456
        }
457
    }
8✔
458
    if (index == -1) {
8✔
NEW
459
        throw InvalidSDFGException("StructuredSDFGBuilder: Node not found in source sequence");
×
460
    }
461

462
    auto node_ptr = std::move(source.children_.at(index));
8✔
463
    source.children_.erase(source.children_.begin() + index);
8✔
464
    source.transitions_.erase(source.transitions_.begin() + index);
8✔
465

466
    target.children_.push_back(std::move(node_ptr));
8✔
467
    target.transitions_.push_back(
16✔
468
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, target)));
8✔
469
};
8✔
470

471
Block& StructuredSDFGBuilder::add_block(Sequence& parent,
496✔
472
                                        const sdfg::control_flow::Assignments& assignments,
473
                                        const DebugInfo& debug_info) {
474
    parent.children_.push_back(
992✔
475
        std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
496✔
476

477
    parent.transitions_.push_back(std::unique_ptr<Transition>(
992✔
478
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
496✔
479

480
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
496✔
481
    (*new_block.dataflow_).parent_ = &new_block;
496✔
482

483
    return new_block;
496✔
484
};
×
485

486
Block& StructuredSDFGBuilder::add_block(Sequence& parent,
19✔
487
                                        const data_flow::DataFlowGraph& data_flow_graph,
488
                                        const sdfg::control_flow::Assignments& assignments,
489
                                        const DebugInfo& debug_info) {
490
    parent.children_.push_back(
38✔
491
        std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
19✔
492

493
    parent.transitions_.push_back(std::unique_ptr<Transition>(
38✔
494
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
19✔
495

496
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
19✔
497
    (*new_block.dataflow_).parent_ = &new_block;
19✔
498

499
    this->add_dataflow(data_flow_graph, new_block);
19✔
500

501
    return new_block;
19✔
502
};
×
503

504
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
10✔
505
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
506
    // Insert block before current block
507
    int index = -1;
10✔
508
    for (size_t i = 0; i < parent.children_.size(); i++) {
10✔
509
        if (parent.children_.at(i).get() == &block) {
10✔
510
            index = i;
10✔
511
            break;
10✔
512
        }
513
    }
×
514
    assert(index > -1);
10✔
515

516
    parent.children_.insert(parent.children_.begin() + index,
20✔
517
                            std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
10✔
518

519
    parent.transitions_.insert(
20✔
520
        parent.transitions_.begin() + index,
10✔
521
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
10✔
522

523
    auto new_entry = parent.at(index);
10✔
524
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
10✔
525
    (*new_block.dataflow_).parent_ = &new_block;
10✔
526

527
    return {new_block, new_entry.second};
10✔
528
};
×
529

530
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
×
531
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph,
532
    const DebugInfo& debug_info) {
533
    // Insert block before current block
534
    int index = -1;
×
535
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
536
        if (parent.children_.at(i).get() == &block) {
×
537
            index = i;
×
538
            break;
×
539
        }
540
    }
×
541
    assert(index > -1);
×
542

543
    parent.children_.insert(parent.children_.begin() + index,
×
544
                            std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
×
545

546
    parent.transitions_.insert(
×
547
        parent.transitions_.begin() + index,
×
548
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
×
549

550
    auto new_entry = parent.at(index);
×
551
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
552
    (*new_block.dataflow_).parent_ = &new_block;
×
553

554
    this->add_dataflow(data_flow_graph, new_block);
×
555

556
    return {new_block, new_entry.second};
×
557
};
×
558

559
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(Sequence& parent,
2✔
560
                                                                      ControlFlowNode& block,
561
                                                                      const DebugInfo& debug_info) {
562
    // Insert block before current block
563
    int index = -1;
2✔
564
    for (size_t i = 0; i < parent.children_.size(); i++) {
3✔
565
        if (parent.children_.at(i).get() == &block) {
3✔
566
            index = i;
2✔
567
            break;
2✔
568
        }
569
    }
1✔
570
    assert(index > -1);
2✔
571

572
    parent.children_.insert(parent.children_.begin() + index + 1,
4✔
573
                            std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
2✔
574

575
    parent.transitions_.insert(
4✔
576
        parent.transitions_.begin() + index + 1,
2✔
577
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
2✔
578

579
    auto new_entry = parent.at(index + 1);
2✔
580
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
2✔
581
    (*new_block.dataflow_).parent_ = &new_block;
2✔
582

583
    return {new_block, new_entry.second};
2✔
584
};
×
585

586
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(
×
587
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph,
588
    const DebugInfo& debug_info) {
589
    int index = -1;
×
590
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
591
        if (parent.children_.at(i).get() == &block) {
×
592
            index = i;
×
593
            break;
×
594
        }
595
    }
×
596
    assert(index > -1);
×
597

598
    parent.children_.insert(parent.children_.begin() + index + 1,
×
599
                            std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
×
600

601
    parent.transitions_.insert(
×
602
        parent.transitions_.begin() + index + 1,
×
603
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
×
604

605
    auto new_entry = parent.at(index + 1);
×
606
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
607
    (*new_block.dataflow_).parent_ = &new_block;
×
608

609
    this->add_dataflow(data_flow_graph, new_block);
×
610

611
    return {new_block, new_entry.second};
×
612
};
×
613

614
For& StructuredSDFGBuilder::add_for(Sequence& parent, const symbolic::Symbol& indvar,
140✔
615
                                    const symbolic::Condition& condition,
616
                                    const symbolic::Expression& init,
617
                                    const symbolic::Expression& update,
618
                                    const sdfg::control_flow::Assignments& assignments,
619
                                    const DebugInfo& debug_info) {
620
    parent.children_.push_back(std::unique_ptr<For>(
280✔
621
        new For(this->new_element_id(), debug_info, indvar, init, update, condition)));
140✔
622

623
    // Increment element id for body node
624
    this->new_element_id();
140✔
625

626
    parent.transitions_.push_back(std::unique_ptr<Transition>(
280✔
627
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
140✔
628

629
    return static_cast<For&>(*parent.children_.back().get());
140✔
630
};
×
631

632
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_before(
10✔
633
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
634
    const symbolic::Condition& condition, const symbolic::Expression& init,
635
    const symbolic::Expression& update, const DebugInfo& debug_info) {
636
    // Insert block before current block
637
    int index = -1;
10✔
638
    for (size_t i = 0; i < parent.children_.size(); i++) {
10✔
639
        if (parent.children_.at(i).get() == &block) {
10✔
640
            index = i;
10✔
641
            break;
10✔
642
        }
643
    }
×
644
    assert(index > -1);
10✔
645

646
    parent.children_.insert(parent.children_.begin() + index,
20✔
647
                            std::unique_ptr<For>(new For(this->new_element_id(), debug_info, indvar,
20✔
648
                                                         init, update, condition)));
10✔
649

650
    // Increment element id for body node
651
    this->new_element_id();
10✔
652

653
    parent.transitions_.insert(
20✔
654
        parent.transitions_.begin() + index,
10✔
655
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
10✔
656

657
    auto new_entry = parent.at(index);
10✔
658
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
10✔
659

660
    return {new_block, new_entry.second};
10✔
661
};
×
662

663
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_after(
6✔
664
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
665
    const symbolic::Condition& condition, const symbolic::Expression& init,
666
    const symbolic::Expression& update, const DebugInfo& debug_info) {
667
    // Insert block before current block
668
    int index = -1;
6✔
669
    for (size_t i = 0; i < parent.children_.size(); i++) {
11✔
670
        if (parent.children_.at(i).get() == &block) {
11✔
671
            index = i;
6✔
672
            break;
6✔
673
        }
674
    }
5✔
675
    assert(index > -1);
6✔
676

677
    parent.children_.insert(parent.children_.begin() + index + 1,
12✔
678
                            std::unique_ptr<For>(new For(this->new_element_id(), debug_info, indvar,
12✔
679
                                                         init, update, condition)));
6✔
680

681
    // Increment element id for body node
682
    this->new_element_id();
6✔
683

684
    parent.transitions_.insert(
12✔
685
        parent.transitions_.begin() + index + 1,
6✔
686
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
6✔
687

688
    auto new_entry = parent.at(index + 1);
6✔
689
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
6✔
690

691
    return {new_block, new_entry.second};
6✔
692
};
×
693

694
IfElse& StructuredSDFGBuilder::add_if_else(Sequence& parent, const DebugInfo& debug_info) {
39✔
695
    return this->add_if_else(parent, control_flow::Assignments{}, debug_info);
39✔
696
};
×
697

698
IfElse& StructuredSDFGBuilder::add_if_else(Sequence& parent,
44✔
699
                                           const sdfg::control_flow::Assignments& assignments,
700
                                           const DebugInfo& debug_info) {
701
    parent.children_.push_back(
88✔
702
        std::unique_ptr<IfElse>(new IfElse(this->new_element_id(), debug_info)));
44✔
703

704
    parent.transitions_.push_back(std::unique_ptr<Transition>(
88✔
705
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
44✔
706

707
    return static_cast<IfElse&>(*parent.children_.back().get());
44✔
708
};
×
709

710
std::pair<IfElse&, Transition&> StructuredSDFGBuilder::add_if_else_before(
1✔
711
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
712
    // Insert block before current block
713
    int index = -1;
1✔
714
    for (size_t i = 0; i < parent.children_.size(); i++) {
1✔
715
        if (parent.children_.at(i).get() == &block) {
1✔
716
            index = i;
1✔
717
            break;
1✔
718
        }
719
    }
×
720
    assert(index > -1);
1✔
721

722
    parent.children_.insert(
2✔
723
        parent.children_.begin() + index,
1✔
724
        std::unique_ptr<IfElse>(new IfElse(this->new_element_id(), debug_info)));
1✔
725

726
    parent.transitions_.insert(
2✔
727
        parent.transitions_.begin() + index,
1✔
728
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
1✔
729

730
    auto new_entry = parent.at(index);
1✔
731
    auto& new_block = dynamic_cast<structured_control_flow::IfElse&>(new_entry.first);
1✔
732

733
    return {new_block, new_entry.second};
1✔
734
};
×
735

736
Sequence& StructuredSDFGBuilder::add_case(IfElse& scope, const sdfg::symbolic::Condition cond,
76✔
737
                                          const DebugInfo& debug_info) {
738
    scope.cases_.push_back(
152✔
739
        std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info)));
76✔
740

741
    scope.conditions_.push_back(cond);
76✔
742
    return *scope.cases_.back();
76✔
743
};
×
744

745
void StructuredSDFGBuilder::remove_case(IfElse& scope, size_t i, const DebugInfo& debug_info) {
4✔
746
    scope.cases_.erase(scope.cases_.begin() + i);
4✔
747
    scope.conditions_.erase(scope.conditions_.begin() + i);
4✔
748
};
4✔
749

750
While& StructuredSDFGBuilder::add_while(Sequence& parent,
33✔
751
                                        const sdfg::control_flow::Assignments& assignments,
752
                                        const DebugInfo& debug_info) {
753
    parent.children_.push_back(
66✔
754
        std::unique_ptr<While>(new While(this->new_element_id(), debug_info)));
33✔
755

756
    // Increment element id for body node
757
    this->new_element_id();
33✔
758

759
    parent.transitions_.push_back(std::unique_ptr<Transition>(
66✔
760
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
33✔
761

762
    return static_cast<While&>(*parent.children_.back().get());
33✔
763
};
×
764

765
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent, const DebugInfo& debug_info) {
12✔
766
    return this->add_continue(parent, control_flow::Assignments{}, debug_info);
12✔
767
};
×
768

769
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent,
14✔
770
                                              const sdfg::control_flow::Assignments& assignments,
771
                                              const DebugInfo& debug_info) {
772
    // Check if continue is in a loop
773
    analysis::AnalysisManager analysis_manager(this->subject());
14✔
774
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
14✔
775
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
14✔
776
    bool in_loop = false;
14✔
777
    while (current_scope != nullptr) {
24✔
778
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
24✔
779
            in_loop = true;
14✔
780
            break;
14✔
781
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
10✔
782
            throw UnstructuredControlFlowException();
×
783
        }
784
        current_scope = scope_tree_analysis.parent_scope(current_scope);
10✔
785
    }
786
    if (!in_loop) {
14✔
787
        throw UnstructuredControlFlowException();
×
788
    }
789

790
    parent.children_.push_back(
28✔
791
        std::unique_ptr<Continue>(new Continue(this->new_element_id(), debug_info)));
14✔
792

793
    parent.transitions_.push_back(std::unique_ptr<Transition>(
28✔
794
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
14✔
795

796
    return static_cast<Continue&>(*parent.children_.back().get());
14✔
797
};
14✔
798

799
Break& StructuredSDFGBuilder::add_break(Sequence& parent, const DebugInfo& debug_info) {
13✔
800
    return this->add_break(parent, control_flow::Assignments{}, debug_info);
13✔
801
};
×
802

803
Break& StructuredSDFGBuilder::add_break(Sequence& parent,
15✔
804
                                        const sdfg::control_flow::Assignments& assignments,
805
                                        const DebugInfo& debug_info) {
806
    // Check if break is in a loop
807
    analysis::AnalysisManager analysis_manager(this->subject());
15✔
808
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
15✔
809
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
15✔
810
    bool in_loop = false;
15✔
811
    while (current_scope != nullptr) {
25✔
812
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
25✔
813
            in_loop = true;
15✔
814
            break;
15✔
815
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
10✔
816
            throw UnstructuredControlFlowException();
×
817
        }
818
        current_scope = scope_tree_analysis.parent_scope(current_scope);
10✔
819
    }
820
    if (!in_loop) {
15✔
821
        throw UnstructuredControlFlowException();
×
822
    }
823

824
    parent.children_.push_back(
30✔
825
        std::unique_ptr<Break>(new Break(this->new_element_id(), debug_info)));
15✔
826

827
    parent.transitions_.push_back(std::unique_ptr<Transition>(
30✔
828
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
15✔
829

830
    return static_cast<Break&>(*parent.children_.back().get());
15✔
831
};
15✔
832

833
Return& StructuredSDFGBuilder::add_return(Sequence& parent,
14✔
834
                                          const sdfg::control_flow::Assignments& assignments,
835
                                          const DebugInfo& debug_info) {
836
    parent.children_.push_back(
28✔
837
        std::unique_ptr<Return>(new Return(this->new_element_id(), debug_info)));
14✔
838

839
    parent.transitions_.push_back(std::unique_ptr<Transition>(
28✔
840
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
14✔
841

842
    return static_cast<Return&>(*parent.children_.back().get());
14✔
843
};
×
844

845
Map& StructuredSDFGBuilder::add_map(Sequence& parent, const symbolic::Symbol& indvar,
25✔
846
                                    const symbolic::Condition& condition,
847
                                    const symbolic::Expression& init,
848
                                    const symbolic::Expression& update,
849
                                    const ScheduleType& schedule_type,
850
                                    const sdfg::control_flow::Assignments& assignments,
851
                                    const DebugInfo& debug_info) {
852
    parent.children_.push_back(std::unique_ptr<Map>(new Map(
50✔
853
        this->new_element_id(), debug_info, indvar, init, update, condition, schedule_type)));
25✔
854

855
    // Increment element id for body node
856
    this->new_element_id();
25✔
857

858
    parent.transitions_.push_back(std::unique_ptr<Transition>(
50✔
859
        new Transition(this->new_element_id(), debug_info, parent, assignments)));
25✔
860

861
    return static_cast<Map&>(*parent.children_.back().get());
25✔
862
};
×
863

864
std::pair<Map&, Transition&> StructuredSDFGBuilder::add_map_before(
4✔
865
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
866
    const symbolic::Condition& condition, const symbolic::Expression& init,
867
    const symbolic::Expression& update, const ScheduleType& schedule_type,
868
    const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
869
    // Insert block before current block
870
    int index = -1;
4✔
871
    for (size_t i = 0; i < parent.children_.size(); i++) {
4✔
872
        if (parent.children_.at(i).get() == &block) {
4✔
873
            index = i;
4✔
874
            break;
4✔
875
        }
876
    }
×
877
    assert(index > -1);
4✔
878

879
    parent.children_.insert(parent.children_.begin() + index,
8✔
880
                            std::unique_ptr<Map>(new Map(this->new_element_id(), debug_info, indvar,
8✔
881
                                                         init, update, condition, schedule_type)));
4✔
882

883
    // Increment element id for body node
884
    this->new_element_id();
4✔
885

886
    parent.transitions_.insert(
8✔
887
        parent.transitions_.begin() + index,
4✔
888
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
4✔
889

890
    auto new_entry = parent.at(index);
4✔
891
    auto& new_block = dynamic_cast<structured_control_flow::Map&>(new_entry.first);
4✔
892

893
    return {new_block, new_entry.second};
4✔
894
};
×
895

896
std::pair<Map&, Transition&> StructuredSDFGBuilder::add_map_after(
8✔
897
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
898
    const symbolic::Condition& condition, const symbolic::Expression& init,
899
    const symbolic::Expression& update, const ScheduleType& schedule_type,
900
    const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
901
    // Insert block before current block
902
    int index = -1;
8✔
903
    for (size_t i = 0; i < parent.children_.size(); i++) {
8✔
904
        if (parent.children_.at(i).get() == &block) {
8✔
905
            index = i;
8✔
906
            break;
8✔
907
        }
908
    }
×
909
    assert(index > -1);
8✔
910

911
    parent.children_.insert(parent.children_.begin() + index + 1,
16✔
912
                            std::unique_ptr<Map>(new Map(this->new_element_id(), debug_info, indvar,
16✔
913
                                                         init, update, condition, schedule_type)));
8✔
914

915
    // Increment element id for body node
916
    this->new_element_id();
8✔
917

918
    parent.transitions_.insert(
16✔
919
        parent.transitions_.begin() + index + 1,
8✔
920
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent)));
8✔
921

922
    auto new_entry = parent.at(index + 1);
8✔
923
    auto& new_block = dynamic_cast<structured_control_flow::Map&>(new_entry.first);
8✔
924

925
    return {new_block, new_entry.second};
8✔
926
};
×
927

928
For& StructuredSDFGBuilder::convert_while(Sequence& parent, While& loop,
×
929
                                          const symbolic::Symbol& indvar,
930
                                          const symbolic::Condition& condition,
931
                                          const symbolic::Expression& init,
932
                                          const symbolic::Expression& update) {
933
    // Insert for loop
934
    size_t index = 0;
×
935
    for (auto& entry : parent.children_) {
×
936
        if (entry.get() == &loop) {
×
937
            break;
×
938
        }
939
        index++;
×
940
    }
941
    auto iter = parent.children_.begin() + index;
×
942
    auto& new_iter = *parent.children_.insert(
×
943
        iter + 1, std::unique_ptr<For>(new For(this->new_element_id(), loop.debug_info(), indvar,
×
944
                                               init, update, condition)));
×
945

946
    // Increment element id for body node
947
    this->new_element_id();
×
948

949
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
950
    this->insert_children(for_loop.root(), loop.root(), 0);
×
951

952
    // Remove while loop
953
    parent.children_.erase(parent.children_.begin() + index);
×
954

955
    return for_loop;
×
956
};
×
957

958
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop) {
8✔
959
    // Insert for loop
960
    size_t index = 0;
8✔
961
    for (auto& entry : parent.children_) {
8✔
962
        if (entry.get() == &loop) {
8✔
963
            break;
8✔
964
        }
965
        index++;
×
966
    }
967
    auto iter = parent.children_.begin() + index;
8✔
968
    auto& new_iter = *parent.children_.insert(
16✔
969
        iter + 1, std::unique_ptr<Map>(new Map(this->new_element_id(), loop.debug_info(),
16✔
970
                                               loop.indvar(), loop.init(), loop.update(),
8✔
971
                                               loop.condition(), ScheduleType_Sequential)));
8✔
972

973
    // Increment element id for body node
974
    this->new_element_id();
8✔
975

976
    auto& map = dynamic_cast<Map&>(*new_iter);
8✔
977
    this->insert_children(map.root(), loop.root(), 0);
8✔
978

979
    // Remove for loop
980
    parent.children_.erase(parent.children_.begin() + index);
8✔
981

982
    return map;
8✔
983
};
×
984

UNCOV
985
void StructuredSDFGBuilder::clear_sequence(Sequence& parent) {
×
UNCOV
986
    parent.children_.clear();
×
UNCOV
987
    parent.transitions_.clear();
×
UNCOV
988
};
×
989

990
Sequence& StructuredSDFGBuilder::parent(const ControlFlowNode& node) {
2✔
991
    std::list<structured_control_flow::ControlFlowNode*> queue = {&this->structured_sdfg_->root()};
2✔
992
    while (!queue.empty()) {
2✔
993
        auto current = queue.front();
2✔
994
        queue.pop_front();
2✔
995

996
        if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
2✔
997
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
2✔
998
                if (&sequence_stmt->at(i).first == &node) {
2✔
999
                    return *sequence_stmt;
2✔
1000
                }
1001
                queue.push_back(&sequence_stmt->at(i).first);
×
1002
            }
×
1003
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
×
1004
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
1005
                queue.push_back(&if_else_stmt->at(i).first);
×
1006
            }
×
1007
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
×
1008
            queue.push_back(&while_stmt->root());
×
1009
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
×
1010
            queue.push_back(&for_stmt->root());
×
1011
        }
×
1012
    }
1013

1014
    return this->structured_sdfg_->root();
×
1015
};
2✔
1016

1017
/***** Section: Dataflow Graph *****/
1018

1019
data_flow::AccessNode& StructuredSDFGBuilder::add_access(structured_control_flow::Block& block,
595✔
1020
                                                         const std::string& data,
1021
                                                         const DebugInfo& debug_info) {
1022
    // Check: Data exists
1023
    if (!this->subject().exists(data)) {
595✔
1024
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
1025
    }
1026

1027
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
595✔
1028
    auto res = block.dataflow_->nodes_.insert(
1,190✔
1029
        {vertex, std::unique_ptr<data_flow::AccessNode>(new data_flow::AccessNode(
1,190✔
1030
                     this->new_element_id(), debug_info, vertex, block.dataflow(), data))});
595✔
1031

1032
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
595✔
1033
};
×
1034

1035
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
375✔
1036
    structured_control_flow::Block& block, const data_flow::TaskletCode code,
1037
    const std::pair<std::string, sdfg::types::Scalar>& output,
1038
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
1039
    const DebugInfo& debug_info) {
1040
    // Check: Duplicate inputs
1041
    std::unordered_set<std::string> input_names;
375✔
1042
    for (auto& input : inputs) {
877✔
1043
        if (!input.first.starts_with("_in")) {
502✔
1044
            continue;
281✔
1045
        }
1046
        if (input_names.find(input.first) != input_names.end()) {
221✔
1047
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
1048
        }
1049
        input_names.insert(input.first);
221✔
1050
    }
1051

1052
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
375✔
1053
    auto res = block.dataflow_->nodes_.insert(
750✔
1054
        {vertex, std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
750✔
1055
                     this->new_element_id(), debug_info, vertex, block.dataflow(), code, output,
375✔
1056
                     inputs, symbolic::__true__()))});
375✔
1057

1058
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
375✔
1059
};
375✔
1060

1061
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
604✔
1062
    structured_control_flow::Block& block, data_flow::DataFlowNode& src,
1063
    const std::string& src_conn, data_flow::DataFlowNode& dst, const std::string& dst_conn,
1064
    const data_flow::Subset& subset, const DebugInfo& debug_info) {
1065
    auto& function_ = this->function();
604✔
1066

1067
    // Check - Case 1: Access Node -> Access Node
1068
    // - src_conn or dst_conn must be refs. The other must be void.
1069
    // - The side of the memlet that is void, is dereferenced.
1070
    // - The dst type must always be a pointer after potential dereferencing.
1071
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
1072
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
604✔
1073
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
1✔
1074
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
1✔
1075
        if (src_conn == "refs") {
1✔
1076
            if (dst_conn != "void") {
×
1077
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
1078
            }
1079

1080
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
1081
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
×
1082
                throw InvalidSDFGException("dst type must be a pointer");
×
1083
            }
1084

1085
            auto& src_type = function_.type(src_node.data());
×
1086
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
×
1087
                throw InvalidSDFGException("src type must be a pointer");
×
1088
            }
1089
        } else if (src_conn == "void") {
1✔
1090
            if (dst_conn != "refs") {
1✔
1091
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
1092
            }
1093

1094
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
1✔
1095
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
1096
            }
1097

1098
            // Trivially correct but checks inference
1099
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
1✔
1100
            types::Pointer ref_type(src_type);
1✔
1101
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
1102
                throw InvalidSDFGException("src type must be a pointer");
1103
            }
1104

1105
            auto& dst_type = function_.type(dst_node.data());
1✔
1106
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
1✔
1107
                throw InvalidSDFGException("dst type must be a pointer");
×
1108
            }
1109
        } else {
1✔
1110
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
1111
        }
1112
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
831✔
1113
               dynamic_cast<data_flow::Tasklet*>(&dst)) {
227✔
1114
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
227✔
1115
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
227✔
1116
        if (src_conn != "void") {
227✔
1117
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1118
        }
1119
        bool found = false;
227✔
1120
        for (auto& input : dst_node.inputs()) {
304✔
1121
            if (input.first == dst_conn) {
304✔
1122
                found = true;
227✔
1123
                break;
227✔
1124
            }
1125
        }
1126
        if (!found) {
227✔
1127
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
1128
        }
1129
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
227✔
1130
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
227✔
1131
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
1132
        }
1133
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) &&
979✔
1134
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
376✔
1135
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
376✔
1136
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
376✔
1137
        if (src_conn != src_node.output().first) {
376✔
1138
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
1139
        }
1140
        if (dst_conn != "void") {
376✔
1141
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1142
        }
1143

1144
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
376✔
1145
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
376✔
1146
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
1147
        }
1148
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
376✔
1149
               dynamic_cast<data_flow::LibraryNode*>(&dst)) {
×
1150
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
1151
        if (src_conn != "void") {
×
1152
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1153
        }
1154
        bool found = false;
×
1155
        for (auto& input : dst_node.inputs()) {
×
1156
            if (input == dst_conn) {
×
1157
                found = true;
×
1158
                break;
×
1159
            }
1160
        }
1161
        if (!found) {
×
1162
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
1163
        }
1164
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) &&
×
1165
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
1166
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
1167
        if (dst_conn != "void") {
×
1168
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1169
        }
1170
        bool found = false;
×
1171
        for (auto& output : src_node.outputs()) {
×
1172
            if (output == src_conn) {
×
1173
                found = true;
×
1174
                break;
×
1175
            }
1176
        }
1177
        if (!found) {
×
1178
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
1179
        }
1180
    } else {
×
1181
        throw InvalidSDFGException("Invalid src or dst node type");
×
1182
    }
1183

1184
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
604✔
1185
    auto res = block.dataflow_->edges_.insert(
1,208✔
1186
        {edge.first, std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
1,208✔
1187
                         this->new_element_id(), debug_info, edge.first, block.dataflow(), src,
604✔
1188
                         src_conn, dst, dst_conn, subset))});
604✔
1189

1190
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
604✔
1191
};
×
1192

1193
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block,
×
1194
                                          const data_flow::Memlet& edge) {
1195
    auto& graph = block.dataflow();
×
1196
    auto e = edge.edge();
×
1197
    boost::remove_edge(e, graph.graph_);
×
1198
    graph.edges_.erase(e);
×
1199
};
×
1200

1201
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block,
×
1202
                                        const data_flow::DataFlowNode& node) {
1203
    auto& graph = block.dataflow();
×
1204
    auto v = node.vertex();
×
1205
    boost::remove_vertex(v, graph.graph_);
×
1206
    graph.nodes_.erase(v);
×
1207
};
×
1208

1209
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
8✔
1210
                                       const data_flow::Tasklet& node) {
1211
    auto& graph = block.dataflow();
8✔
1212

1213
    std::unordered_set<const data_flow::DataFlowNode*> to_delete = {&node};
8✔
1214

1215
    // Delete incoming
1216
    std::list<const data_flow::Memlet*> iedges;
8✔
1217
    for (auto& iedge : graph.in_edges(node)) {
15✔
1218
        iedges.push_back(&iedge);
7✔
1219
    }
1220
    for (auto iedge : iedges) {
15✔
1221
        auto& src = iedge->src();
7✔
1222
        to_delete.insert(&src);
7✔
1223

1224
        auto edge = iedge->edge();
7✔
1225
        graph.edges_.erase(edge);
7✔
1226
        boost::remove_edge(edge, graph.graph_);
7✔
1227
    }
1228

1229
    // Delete outgoing
1230
    std::list<const data_flow::Memlet*> oedges;
8✔
1231
    for (auto& oedge : graph.out_edges(node)) {
16✔
1232
        oedges.push_back(&oedge);
8✔
1233
    }
1234
    for (auto oedge : oedges) {
16✔
1235
        auto& dst = oedge->dst();
8✔
1236
        to_delete.insert(&dst);
8✔
1237

1238
        auto edge = oedge->edge();
8✔
1239
        graph.edges_.erase(edge);
8✔
1240
        boost::remove_edge(edge, graph.graph_);
8✔
1241
    }
1242

1243
    // Delete nodes
1244
    for (auto obsolete_node : to_delete) {
31✔
1245
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
23✔
1246
            auto vertex = obsolete_node->vertex();
23✔
1247
            graph.nodes_.erase(vertex);
23✔
1248
            boost::remove_vertex(vertex, graph.graph_);
23✔
1249
        }
23✔
1250
    }
1251
};
8✔
1252

1253
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
1✔
1254
                                       const data_flow::AccessNode& node) {
1255
    auto& graph = block.dataflow();
1✔
1256
    if (graph.out_degree(node) != 0) {
1✔
1257
        throw InvalidSDFGException("StructuredSDFGBuilder: Access node has outgoing edges");
×
1258
    }
1259

1260
    std::list<const data_flow::Memlet*> tmp;
1✔
1261
    std::list<const data_flow::DataFlowNode*> queue = {&node};
1✔
1262
    while (!queue.empty()) {
3✔
1263
        auto current = queue.front();
2✔
1264
        queue.pop_front();
2✔
1265
        if (current != &node) {
2✔
1266
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
1✔
1267
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
1268
                    continue;
×
1269
                }
1270
            }
×
1271
        }
1✔
1272

1273
        tmp.clear();
2✔
1274
        for (auto& iedge : graph.in_edges(*current)) {
3✔
1275
            tmp.push_back(&iedge);
1✔
1276
        }
1277
        for (auto iedge : tmp) {
3✔
1278
            auto& src = iedge->src();
1✔
1279
            queue.push_back(&src);
1✔
1280

1281
            auto edge = iedge->edge();
1✔
1282
            graph.edges_.erase(edge);
1✔
1283
            boost::remove_edge(edge, graph.graph_);
1✔
1284
        }
1285

1286
        auto vertex = current->vertex();
2✔
1287
        graph.nodes_.erase(vertex);
2✔
1288
        boost::remove_vertex(vertex, graph.graph_);
2✔
1289
    }
1290
};
1✔
1291

1292
data_flow::AccessNode& StructuredSDFGBuilder::symbolic_expression_to_dataflow(
×
1293
    structured_control_flow::Block& parent, const symbolic::Expression& expr) {
1294
    auto& sdfg = this->subject();
×
1295

1296
    codegen::CPPLanguageExtension language_extension;
×
1297

1298
    // Base cases
1299
    if (SymEngine::is_a<SymEngine::Symbol>(*expr)) {
×
1300
        auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(expr);
×
1301

1302
        // Determine type
1303
        types::Scalar sym_type = types::Scalar(types::PrimitiveType::Void);
×
1304
        if (symbolic::is_nv(sym)) {
×
1305
            sym_type = types::Scalar(types::PrimitiveType::Int32);
×
1306
        } else {
×
1307
            sym_type = static_cast<const types::Scalar&>(sdfg.type(sym->get_name()));
×
1308
        }
1309

1310
        // Add new container for intermediate result
1311
        auto tmp = this->find_new_name();
×
1312
        this->add_container(tmp, sym_type);
×
1313

1314
        // Create dataflow graph
1315
        auto& input_node = this->add_access(parent, sym->get_name());
×
1316
        auto& output_node = this->add_access(parent, tmp);
×
1317
        auto& tasklet = this->add_tasklet(parent, data_flow::TaskletCode::assign,
×
1318
                                          {"_out", sym_type}, {{"_in", sym_type}});
×
1319
        this->add_memlet(parent, input_node, "void", tasklet, "_in", {});
×
1320
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1321

1322
        return output_node;
×
1323
    } else if (SymEngine::is_a<SymEngine::Integer>(*expr)) {
×
1324
        auto tmp = this->find_new_name();
×
1325
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Int64));
×
1326

1327
        auto& output_node = this->add_access(parent, tmp);
×
1328
        auto& tasklet = this->add_tasklet(
×
1329
            parent, data_flow::TaskletCode::assign,
×
1330
            {"_out", types::Scalar(types::PrimitiveType::Int64)},
×
1331
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Int64)}});
×
1332
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1333
        return output_node;
×
1334
    } else if (SymEngine::is_a<SymEngine::BooleanAtom>(*expr)) {
×
1335
        auto tmp = this->find_new_name();
×
1336
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1337

1338
        auto& output_node = this->add_access(parent, tmp);
×
1339
        auto& tasklet = this->add_tasklet(
×
1340
            parent, data_flow::TaskletCode::assign,
×
1341
            {"_out", types::Scalar(types::PrimitiveType::Bool)},
×
1342
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Bool)}});
×
1343
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1344
        return output_node;
×
1345
    } else if (SymEngine::is_a<SymEngine::Or>(*expr)) {
×
1346
        auto or_expr = SymEngine::rcp_static_cast<const SymEngine::Or>(expr);
×
1347
        if (or_expr->get_container().size() != 2) {
×
1348
            throw InvalidSDFGException(
×
1349
                "StructuredSDFGBuilder: Or expression must have exactly two arguments");
×
1350
        }
1351

1352
        std::vector<data_flow::AccessNode*> input_nodes;
×
1353
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1354
        for (auto& arg : or_expr->get_container()) {
×
1355
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1356
            input_nodes.push_back(&input_node);
×
1357
            input_types.push_back(
×
1358
                {"_in" + std::to_string(input_types.size() + 1),
×
1359
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1360
        }
1361

1362
        // Add new container for intermediate result
1363
        auto tmp = this->find_new_name();
×
1364
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1365

1366
        auto& output_node = this->add_access(parent, tmp);
×
1367
        auto& tasklet =
×
1368
            this->add_tasklet(parent, data_flow::TaskletCode::logical_or,
×
1369
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1370
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1371
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1372
                             {});
×
1373
        }
×
1374
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1375
        return output_node;
×
1376
    } else if (SymEngine::is_a<SymEngine::And>(*expr)) {
×
1377
        auto and_expr = SymEngine::rcp_static_cast<const SymEngine::And>(expr);
×
1378
        if (and_expr->get_container().size() != 2) {
×
1379
            throw InvalidSDFGException(
×
1380
                "StructuredSDFGBuilder: And expression must have exactly two arguments");
×
1381
        }
1382

1383
        std::vector<data_flow::AccessNode*> input_nodes;
×
1384
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1385
        for (auto& arg : and_expr->get_container()) {
×
1386
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1387
            input_nodes.push_back(&input_node);
×
1388
            input_types.push_back(
×
1389
                {"_in" + std::to_string(input_types.size() + 1),
×
1390
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1391
        }
1392

1393
        // Add new container for intermediate result
1394
        auto tmp = this->find_new_name();
×
1395
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1396

1397
        auto& output_node = this->add_access(parent, tmp);
×
1398
        auto& tasklet =
×
1399
            this->add_tasklet(parent, data_flow::TaskletCode::logical_and,
×
1400
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1401
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1402
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1403
                             {});
×
1404
        }
×
1405
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1406
        return output_node;
×
1407
    } else {
×
1408
        throw std::runtime_error("Unsupported expression type");
×
1409
    }
1410
};
×
1411

1412
void StructuredSDFGBuilder::add_dataflow(const data_flow::DataFlowGraph& from, Block& to) {
19✔
1413
    auto& to_dataflow = to.dataflow();
19✔
1414

1415
    std::unordered_map<graph::Vertex, graph::Vertex> node_mapping;
19✔
1416
    for (auto& entry : from.nodes_) {
20✔
1417
        auto vertex = boost::add_vertex(to_dataflow.graph_);
1✔
1418
        to_dataflow.nodes_.insert(
2✔
1419
            {vertex, entry.second->clone(this->new_element_id(), vertex, to_dataflow)});
1✔
1420
        node_mapping.insert({entry.first, vertex});
1✔
1421
    }
1422

1423
    for (auto& entry : from.edges_) {
19✔
UNCOV
1424
        auto src = node_mapping[entry.second->src().vertex()];
×
UNCOV
1425
        auto dst = node_mapping[entry.second->dst().vertex()];
×
1426

UNCOV
1427
        auto edge = boost::add_edge(src, dst, to_dataflow.graph_);
×
1428

UNCOV
1429
        to_dataflow.edges_.insert(
×
UNCOV
1430
            {edge.first, entry.second->clone(this->new_element_id(), edge.first, to_dataflow,
×
UNCOV
1431
                                             *to_dataflow.nodes_[src], *to_dataflow.nodes_[dst])});
×
1432
    }
1433
};
19✔
1434

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

© 2025 Coveralls, Inc