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

daisytuner / sdfglib / 16069945621

04 Jul 2025 08:56AM UTC coverage: 64.375% (-0.2%) from 64.606%
16069945621

push

github

web-flow
Merge pull request #137 from daisytuner/clang-format

runs clang-format on codebase

609 of 827 new or added lines in 63 files covered. (73.64%)

46 existing lines in 30 files now uncovered.

8578 of 13325 relevant lines covered (64.38%)

177.24 hits per line

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

68.1
/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::
19
    determine_loop_nodes(const SDFG& sdfg, const control_flow::State& start, const control_flow::State& end) const {
1✔
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,
46
    const State* node,
47
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree
48
) {
49
    if (pdom == node) {
6✔
50
        return true;
1✔
51
    }
52

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

61
    return false;
×
62
}
6✔
63

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

78
    return pdom;
3✔
79
}
3✔
80

81
void StructuredSDFGBuilder::traverse(const SDFG& sdfg) {
4✔
82
    // Start of SDFGS
83
    Sequence& root = *structured_sdfg_->root_;
4✔
84
    const State* start_state = &sdfg.start_state();
4✔
85

86
    auto pdom_tree = sdfg.post_dominator_tree();
4✔
87

88
    std::unordered_set<const InterstateEdge*> breaks;
4✔
89
    std::unordered_set<const InterstateEdge*> continues;
4✔
90
    for (auto& edge : sdfg.back_edges()) {
5✔
91
        continues.insert(edge);
1✔
92
    }
93

94
    std::unordered_set<const control_flow::State*> visited;
4✔
95
    this->traverse_with_loop_detection(sdfg, root, start_state, nullptr, continues, breaks, pdom_tree, visited);
4✔
96
};
4✔
97

98
void StructuredSDFGBuilder::traverse_with_loop_detection(
9✔
99
    const SDFG& sdfg,
100
    Sequence& scope,
101
    const State* current,
102
    const State* end,
103
    const std::unordered_set<const InterstateEdge*>& continues,
104
    const std::unordered_set<const InterstateEdge*>& breaks,
105
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree,
106
    std::unordered_set<const control_flow::State*>& visited
107
) {
108
    if (current == end) {
9✔
109
        return;
1✔
110
    }
111

112
    auto in_edges = sdfg.in_edges(*current);
8✔
113

114
    // Loop detection
115
    std::unordered_set<const InterstateEdge*> loop_edges;
8✔
116
    for (auto& iedge : in_edges) {
13✔
117
        if (continues.find(&iedge) != continues.end()) {
5✔
118
            loop_edges.insert(&iedge);
1✔
119
        }
1✔
120
    }
121
    if (!loop_edges.empty()) {
8✔
122
        // 1. Determine nodes of loop body
123
        std::unordered_set<const control_flow::State*> body;
1✔
124
        for (auto back_edge : loop_edges) {
2✔
125
            auto loop_nodes = this->determine_loop_nodes(sdfg, back_edge->src(), back_edge->dst());
1✔
126
            body.insert(loop_nodes.begin(), loop_nodes.end());
1✔
127
        }
1✔
128

129
        // 2. Determine exit states and exit edges
130
        std::unordered_set<const control_flow::State*> exit_states;
1✔
131
        std::unordered_set<const control_flow::InterstateEdge*> exit_edges;
1✔
132
        for (auto node : body) {
4✔
133
            for (auto& edge : sdfg.out_edges(*node)) {
7✔
134
                if (body.find(&edge.dst()) == body.end()) {
4✔
135
                    exit_edges.insert(&edge);
1✔
136
                    exit_states.insert(&edge.dst());
1✔
137
                }
1✔
138
            }
139
        }
140
        if (exit_states.size() != 1) {
1✔
141
            throw UnstructuredControlFlowException();
×
142
        }
143
        const control_flow::State* exit_state = *exit_states.begin();
1✔
144

145
        for (auto& edge : breaks) {
1✔
146
            exit_edges.insert(edge);
×
147
        }
148

149
        // Collect debug information (could be removed when this is computed dynamically)
150
        DebugInfo dbg_info = current->debug_info();
1✔
151
        for (auto& edge : in_edges) {
3✔
152
            dbg_info = DebugInfo::merge(dbg_info, edge.debug_info());
2✔
153
        }
154
        for (auto node : body) {
4✔
155
            dbg_info = DebugInfo::merge(dbg_info, node->debug_info());
3✔
156
        }
157
        for (auto edge : exit_edges) {
2✔
158
            dbg_info = DebugInfo::merge(dbg_info, edge->debug_info());
1✔
159
        }
160

161
        // 3. Add while loop
162
        While& loop = this->add_while(scope, {}, dbg_info);
1✔
163

164
        std::unordered_set<const control_flow::State*> loop_visited(visited);
1✔
165
        this->traverse_without_loop_detection(
1✔
166
            sdfg, loop.root(), current, exit_state, continues, exit_edges, pdom_tree, loop_visited
1✔
167
        );
168

169
        this->traverse_with_loop_detection(sdfg, scope, exit_state, end, continues, breaks, pdom_tree, visited);
1✔
170
    } else {
1✔
171
        this->traverse_without_loop_detection(sdfg, scope, current, end, continues, breaks, pdom_tree, visited);
7✔
172
    }
173
};
9✔
174

175
void StructuredSDFGBuilder::traverse_without_loop_detection(
8✔
176
    const SDFG& sdfg,
177
    Sequence& scope,
178
    const State* current,
179
    const State* end,
180
    const std::unordered_set<const InterstateEdge*>& continues,
181
    const std::unordered_set<const InterstateEdge*>& breaks,
182
    const std::unordered_map<const control_flow::State*, const control_flow::State*>& pdom_tree,
183
    std::unordered_set<const control_flow::State*>& visited
184
) {
185
    std::list<const State*> queue = {current};
8✔
186
    while (!queue.empty()) {
26✔
187
        auto curr = queue.front();
18✔
188
        queue.pop_front();
18✔
189
        if (curr == end) {
18✔
190
            continue;
3✔
191
        }
192

193
        if (visited.find(curr) != visited.end()) {
15✔
194
            throw UnstructuredControlFlowException();
×
195
        }
196
        visited.insert(curr);
15✔
197

198
        auto out_edges = sdfg.out_edges(*curr);
15✔
199
        auto out_degree = sdfg.out_degree(*curr);
15✔
200

201
        // Case 1: Sink node
202
        if (out_degree == 0) {
15✔
203
            this->add_block(scope, curr->dataflow(), {}, curr->debug_info());
4✔
204
            this->add_return(scope, {}, curr->debug_info());
4✔
205
            continue;
4✔
206
        }
207

208
        // Case 2: Transition
209
        if (out_degree == 1) {
11✔
210
            auto& oedge = *out_edges.begin();
8✔
211
            if (!oedge.is_unconditional()) {
8✔
212
                throw UnstructuredControlFlowException();
×
213
            }
214
            this->add_block(scope, curr->dataflow(), oedge.assignments(), curr->debug_info());
8✔
215

216
            if (continues.find(&oedge) != continues.end()) {
8✔
217
                this->add_continue(scope, oedge.debug_info());
×
218
            } else if (breaks.find(&oedge) != breaks.end()) {
8✔
219
                this->add_break(scope, oedge.debug_info());
×
220
            } else {
×
221
                bool starts_loop = false;
8✔
222
                for (auto& iedge : sdfg.in_edges(oedge.dst())) {
19✔
223
                    if (continues.find(&iedge) != continues.end()) {
11✔
224
                        starts_loop = true;
×
225
                        break;
×
226
                    }
227
                }
228
                if (!starts_loop) {
8✔
229
                    queue.push_back(&oedge.dst());
8✔
230
                } else {
8✔
NEW
231
                    this->traverse_with_loop_detection(
×
NEW
232
                        sdfg, scope, &oedge.dst(), end, continues, breaks, pdom_tree, visited
×
233
                    );
234
                }
235
            }
236
            continue;
8✔
237
        }
238

239
        // Case 3: Branches
240
        if (out_degree > 1) {
3✔
241
            this->add_block(scope, curr->dataflow(), {}, curr->debug_info());
3✔
242

243
            std::vector<const InterstateEdge*> out_edges_vec;
3✔
244
            for (auto& edge : out_edges) {
9✔
245
                out_edges_vec.push_back(&edge);
6✔
246
            }
247

248
            // Best-effort approach: Find end of if-else
249
            // If not found, the branches may repeat paths yielding a large SDFG
250
            const control_flow::State* local_end = this->find_end_of_if_else(sdfg, curr, out_edges_vec, pdom_tree);
3✔
251
            if (local_end == nullptr) {
3✔
252
                local_end = end;
×
253
            }
×
254

255
            auto& if_else = this->add_if_else(scope, curr->debug_info());
3✔
256
            for (size_t i = 0; i < out_degree; i++) {
9✔
257
                auto& out_edge = out_edges_vec[i];
6✔
258

259
                auto& branch = this->add_case(if_else, out_edge->condition(), out_edge->debug_info());
6✔
260
                if (!out_edge->assignments().empty()) {
6✔
261
                    this->add_block(branch, out_edge->assignments(), out_edge->debug_info());
2✔
262
                }
2✔
263
                if (continues.find(out_edge) != continues.end()) {
6✔
264
                    this->add_continue(branch, out_edge->debug_info());
1✔
265
                } else if (breaks.find(out_edge) != breaks.end()) {
6✔
266
                    this->add_break(branch, out_edge->debug_info());
1✔
267
                } else {
1✔
268
                    std::unordered_set<const control_flow::State*> branch_visited(visited);
4✔
269
                    this->traverse_with_loop_detection(
4✔
270
                        sdfg, branch, &out_edge->dst(), local_end, continues, breaks, pdom_tree, branch_visited
4✔
271
                    );
272
                }
4✔
273
            }
6✔
274

275
            if (local_end != end) {
3✔
276
                bool starts_loop = false;
2✔
277
                for (auto& iedge : sdfg.in_edges(*local_end)) {
6✔
278
                    if (continues.find(&iedge) != continues.end()) {
4✔
279
                        starts_loop = true;
×
280
                        break;
×
281
                    }
282
                }
283
                if (!starts_loop) {
2✔
284
                    queue.push_back(local_end);
2✔
285
                } else {
2✔
NEW
286
                    this->traverse_with_loop_detection(sdfg, scope, local_end, end, continues, breaks, pdom_tree, visited);
×
287
                }
288
            }
2✔
289
            continue;
290
        }
3✔
291
    }
292
}
8✔
293

294
Function& StructuredSDFGBuilder::function() const { return static_cast<Function&>(*this->structured_sdfg_); };
6,216✔
295

296
StructuredSDFGBuilder::StructuredSDFGBuilder(std::unique_ptr<StructuredSDFG>& sdfg)
66✔
297
    : FunctionBuilder(), structured_sdfg_(std::move(sdfg)) {};
66✔
298

299
StructuredSDFGBuilder::StructuredSDFGBuilder(const std::string& name, FunctionType type)
503✔
300
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(name, type)) {};
503✔
301

302
StructuredSDFGBuilder::StructuredSDFGBuilder(const SDFG& sdfg)
4✔
303
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(sdfg.name(), sdfg.type())) {
4✔
304
    for (auto& entry : sdfg.structures_) {
4✔
305
        this->structured_sdfg_->structures_.insert({entry.first, entry.second->clone()});
×
306
    }
307

308
    for (auto& entry : sdfg.containers_) {
11✔
309
        this->structured_sdfg_->containers_.insert({entry.first, entry.second->clone()});
7✔
310
    }
311

312
    for (auto& arg : sdfg.arguments_) {
6✔
313
        this->structured_sdfg_->arguments_.push_back(arg);
2✔
314
    }
315

316
    for (auto& ext : sdfg.externals_) {
5✔
317
        this->structured_sdfg_->externals_.push_back(ext);
1✔
318
    }
319

320
    for (auto& entry : sdfg.assumptions_) {
9✔
321
        this->structured_sdfg_->assumptions_.insert({entry.first, entry.second});
5✔
322
    }
323

324
    for (auto& entry : sdfg.metadata_) {
6✔
325
        this->structured_sdfg_->metadata_[entry.first] = entry.second;
2✔
326
    }
327

328
    this->traverse(sdfg);
4✔
329
};
4✔
330

331
StructuredSDFG& StructuredSDFGBuilder::subject() const { return *this->structured_sdfg_; };
1,431✔
332

333
std::unique_ptr<StructuredSDFG> StructuredSDFGBuilder::move() { return std::move(this->structured_sdfg_); };
392✔
334

335
Element* StructuredSDFGBuilder::find_element_by_id(const size_t& element_id) const {
9✔
336
    auto& sdfg = this->subject();
9✔
337
    std::list<Element*> queue = {&sdfg.root()};
9✔
338
    while (!queue.empty()) {
32✔
339
        auto current = queue.front();
32✔
340
        queue.pop_front();
32✔
341

342
        if (current->element_id() == element_id) {
32✔
343
            return current;
9✔
344
        }
345

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

378
    return nullptr;
×
379
};
9✔
380

381
Sequence& StructuredSDFGBuilder::
382
    add_sequence(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
39✔
383
    parent.children_.push_back(std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info)));
39✔
384

385
    parent.transitions_
78✔
386
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
39✔
387
        );
388

389
    return static_cast<Sequence&>(*parent.children_.back().get());
39✔
390
};
×
391

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

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

410
    parent.transitions_.insert(
16✔
411
        parent.transitions_.begin() + index,
8✔
412
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
8✔
413
    );
414

415
    auto new_entry = parent.at(index);
8✔
416
    auto& new_block = dynamic_cast<structured_control_flow::Sequence&>(new_entry.first);
8✔
417

418
    return {new_block, new_entry.second};
8✔
419
};
×
420

421
void StructuredSDFGBuilder::remove_child(Sequence& parent, size_t i) {
9✔
422
    parent.children_.erase(parent.children_.begin() + i);
9✔
423
    parent.transitions_.erase(parent.transitions_.begin() + i);
9✔
424
};
9✔
425

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

435
    parent.children_.erase(parent.children_.begin() + index);
12✔
436
    parent.transitions_.erase(parent.transitions_.begin() + index);
12✔
437
};
12✔
438

439
void StructuredSDFGBuilder::insert_children(Sequence& parent, Sequence& other, size_t i) {
20✔
440
    parent.children_.insert(
40✔
441
        parent.children_.begin() + i,
20✔
442
        std::make_move_iterator(other.children_.begin()),
20✔
443
        std::make_move_iterator(other.children_.end())
20✔
444
    );
445
    parent.transitions_.insert(
40✔
446
        parent.transitions_.begin() + i,
20✔
447
        std::make_move_iterator(other.transitions_.begin()),
20✔
448
        std::make_move_iterator(other.transitions_.end())
20✔
449
    );
450
    for (auto& trans : parent.transitions_) {
44✔
451
        trans->parent_ = &parent;
24✔
452
    }
453
    other.children_.clear();
20✔
454
    other.transitions_.clear();
20✔
455
};
20✔
456

457
void StructuredSDFGBuilder::insert(ControlFlowNode& node, Sequence& source, Sequence& target, const DebugInfo& debug_info) {
8✔
458
    // Insert node into target sequence
459
    int index = -1;
8✔
460
    for (size_t i = 0; i < source.children_.size(); i++) {
16✔
461
        if (source.children_.at(i).get() == &node) {
16✔
462
            index = i;
8✔
463
            break;
8✔
464
        }
465
    }
8✔
466
    if (index == -1) {
8✔
467
        throw InvalidSDFGException("StructuredSDFGBuilder: Node not found in source sequence");
×
468
    }
469

470
    auto node_ptr = std::move(source.children_.at(index));
8✔
471
    source.children_.erase(source.children_.begin() + index);
8✔
472
    source.transitions_.erase(source.transitions_.begin() + index);
8✔
473

474
    target.children_.push_back(std::move(node_ptr));
8✔
475
    target.transitions_.push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, target)
8✔
476
    ));
477
};
8✔
478

479
Block& StructuredSDFGBuilder::
480
    add_block(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
498✔
481
    parent.children_.push_back(std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
498✔
482

483
    parent.transitions_
996✔
484
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
498✔
485
        );
486

487
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
498✔
488
    (*new_block.dataflow_).parent_ = &new_block;
498✔
489

490
    return new_block;
498✔
491
};
×
492

493
Block& StructuredSDFGBuilder::add_block(
19✔
494
    Sequence& parent,
495
    const data_flow::DataFlowGraph& data_flow_graph,
496
    const sdfg::control_flow::Assignments& assignments,
497
    const DebugInfo& debug_info
498
) {
499
    parent.children_.push_back(std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
19✔
500

501
    parent.transitions_
38✔
502
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
19✔
503
        );
504

505
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
19✔
506
    (*new_block.dataflow_).parent_ = &new_block;
19✔
507

508
    this->add_dataflow(data_flow_graph, new_block);
19✔
509

510
    return new_block;
19✔
511
};
×
512

513
std::pair<Block&, Transition&> StructuredSDFGBuilder::
514
    add_block_before(Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
10✔
515
    // Insert block before current block
516
    int index = -1;
10✔
517
    for (size_t i = 0; i < parent.children_.size(); i++) {
10✔
518
        if (parent.children_.at(i).get() == &block) {
10✔
519
            index = i;
10✔
520
            break;
10✔
521
        }
522
    }
×
523
    assert(index > -1);
10✔
524

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

528
    parent.transitions_.insert(
20✔
529
        parent.transitions_.begin() + index,
10✔
530
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
10✔
531
    );
532

533
    auto new_entry = parent.at(index);
10✔
534
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
10✔
535
    (*new_block.dataflow_).parent_ = &new_block;
10✔
536

537
    return {new_block, new_entry.second};
10✔
538
};
×
539

540
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
×
541
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph, const DebugInfo& debug_info
542
) {
543
    // Insert block before current block
544
    int index = -1;
×
545
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
546
        if (parent.children_.at(i).get() == &block) {
×
547
            index = i;
×
548
            break;
×
549
        }
550
    }
×
551
    assert(index > -1);
×
552

NEW
553
    parent.children_
×
NEW
554
        .insert(parent.children_.begin() + index, std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info)));
×
555

556
    parent.transitions_.insert(
×
557
        parent.transitions_.begin() + index,
×
NEW
558
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
×
559
    );
560

561
    auto new_entry = parent.at(index);
×
562
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
563
    (*new_block.dataflow_).parent_ = &new_block;
×
564

565
    this->add_dataflow(data_flow_graph, new_block);
×
566

567
    return {new_block, new_entry.second};
×
568
};
×
569

570
std::pair<Block&, Transition&> StructuredSDFGBuilder::
571
    add_block_after(Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
2✔
572
    // Insert block before current block
573
    int index = -1;
2✔
574
    for (size_t i = 0; i < parent.children_.size(); i++) {
3✔
575
        if (parent.children_.at(i).get() == &block) {
3✔
576
            index = i;
2✔
577
            break;
2✔
578
        }
579
    }
1✔
580
    assert(index > -1);
2✔
581

582
    parent.children_.insert(
4✔
583
        parent.children_.begin() + index + 1, std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info))
2✔
584
    );
585

586
    parent.transitions_.insert(
4✔
587
        parent.transitions_.begin() + index + 1,
2✔
588
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
2✔
589
    );
590

591
    auto new_entry = parent.at(index + 1);
2✔
592
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
2✔
593
    (*new_block.dataflow_).parent_ = &new_block;
2✔
594

595
    return {new_block, new_entry.second};
2✔
596
};
×
597

598
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(
×
599
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph, const DebugInfo& debug_info
600
) {
601
    int index = -1;
×
602
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
603
        if (parent.children_.at(i).get() == &block) {
×
604
            index = i;
×
605
            break;
×
606
        }
607
    }
×
608
    assert(index > -1);
×
609

NEW
610
    parent.children_.insert(
×
NEW
611
        parent.children_.begin() + index + 1, std::unique_ptr<Block>(new Block(this->new_element_id(), debug_info))
×
612
    );
613

614
    parent.transitions_.insert(
×
615
        parent.transitions_.begin() + index + 1,
×
NEW
616
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
×
617
    );
618

619
    auto new_entry = parent.at(index + 1);
×
620
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
621
    (*new_block.dataflow_).parent_ = &new_block;
×
622

623
    this->add_dataflow(data_flow_graph, new_block);
×
624

625
    return {new_block, new_entry.second};
×
626
};
×
627

628
For& StructuredSDFGBuilder::add_for(
142✔
629
    Sequence& parent,
630
    const symbolic::Symbol& indvar,
631
    const symbolic::Condition& condition,
632
    const symbolic::Expression& init,
633
    const symbolic::Expression& update,
634
    const sdfg::control_flow::Assignments& assignments,
635
    const DebugInfo& debug_info
636
) {
637
    parent.children_
284✔
638
        .push_back(std::unique_ptr<For>(new For(this->new_element_id(), debug_info, indvar, init, update, condition)));
142✔
639

640
    // Increment element id for body node
641
    this->new_element_id();
142✔
642

643
    parent.transitions_
284✔
644
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
142✔
645
        );
646

647
    return static_cast<For&>(*parent.children_.back().get());
142✔
648
};
×
649

650
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_before(
10✔
651
    Sequence& parent,
652
    ControlFlowNode& block,
653
    const symbolic::Symbol& indvar,
654
    const symbolic::Condition& condition,
655
    const symbolic::Expression& init,
656
    const symbolic::Expression& update,
657
    const DebugInfo& debug_info
658
) {
659
    // Insert block before current block
660
    int index = -1;
10✔
661
    for (size_t i = 0; i < parent.children_.size(); i++) {
10✔
662
        if (parent.children_.at(i).get() == &block) {
10✔
663
            index = i;
10✔
664
            break;
10✔
665
        }
666
    }
×
667
    assert(index > -1);
10✔
668

669
    parent.children_.insert(
20✔
670
        parent.children_.begin() + index,
10✔
671
        std::unique_ptr<For>(new For(this->new_element_id(), debug_info, indvar, init, update, condition))
10✔
672
    );
673

674
    // Increment element id for body node
675
    this->new_element_id();
10✔
676

677
    parent.transitions_.insert(
20✔
678
        parent.transitions_.begin() + index,
10✔
679
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
10✔
680
    );
681

682
    auto new_entry = parent.at(index);
10✔
683
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
10✔
684

685
    return {new_block, new_entry.second};
10✔
686
};
×
687

688
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_after(
6✔
689
    Sequence& parent,
690
    ControlFlowNode& block,
691
    const symbolic::Symbol& indvar,
692
    const symbolic::Condition& condition,
693
    const symbolic::Expression& init,
694
    const symbolic::Expression& update,
695
    const DebugInfo& debug_info
696
) {
697
    // Insert block before current block
698
    int index = -1;
6✔
699
    for (size_t i = 0; i < parent.children_.size(); i++) {
11✔
700
        if (parent.children_.at(i).get() == &block) {
11✔
701
            index = i;
6✔
702
            break;
6✔
703
        }
704
    }
5✔
705
    assert(index > -1);
6✔
706

707
    parent.children_.insert(
12✔
708
        parent.children_.begin() + index + 1,
6✔
709
        std::unique_ptr<For>(new For(this->new_element_id(), debug_info, indvar, init, update, condition))
6✔
710
    );
711

712
    // Increment element id for body node
713
    this->new_element_id();
6✔
714

715
    parent.transitions_.insert(
12✔
716
        parent.transitions_.begin() + index + 1,
6✔
717
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
6✔
718
    );
719

720
    auto new_entry = parent.at(index + 1);
6✔
721
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
6✔
722

723
    return {new_block, new_entry.second};
6✔
724
};
×
725

726
IfElse& StructuredSDFGBuilder::add_if_else(Sequence& parent, const DebugInfo& debug_info) {
39✔
727
    return this->add_if_else(parent, control_flow::Assignments{}, debug_info);
39✔
728
};
×
729

730
IfElse& StructuredSDFGBuilder::
731
    add_if_else(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
44✔
732
    parent.children_.push_back(std::unique_ptr<IfElse>(new IfElse(this->new_element_id(), debug_info)));
44✔
733

734
    parent.transitions_
88✔
735
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
44✔
736
        );
737

738
    return static_cast<IfElse&>(*parent.children_.back().get());
44✔
739
};
×
740

741
std::pair<IfElse&, Transition&> StructuredSDFGBuilder::
742
    add_if_else_before(Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
1✔
743
    // Insert block before current block
744
    int index = -1;
1✔
745
    for (size_t i = 0; i < parent.children_.size(); i++) {
1✔
746
        if (parent.children_.at(i).get() == &block) {
1✔
747
            index = i;
1✔
748
            break;
1✔
749
        }
750
    }
×
751
    assert(index > -1);
1✔
752

753
    parent.children_
2✔
754
        .insert(parent.children_.begin() + index, std::unique_ptr<IfElse>(new IfElse(this->new_element_id(), debug_info)));
1✔
755

756
    parent.transitions_.insert(
2✔
757
        parent.transitions_.begin() + index,
1✔
758
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
1✔
759
    );
760

761
    auto new_entry = parent.at(index);
1✔
762
    auto& new_block = dynamic_cast<structured_control_flow::IfElse&>(new_entry.first);
1✔
763

764
    return {new_block, new_entry.second};
1✔
765
};
×
766

767
Sequence& StructuredSDFGBuilder::add_case(IfElse& scope, const sdfg::symbolic::Condition cond, const DebugInfo& debug_info) {
76✔
768
    scope.cases_.push_back(std::unique_ptr<Sequence>(new Sequence(this->new_element_id(), debug_info)));
76✔
769

770
    scope.conditions_.push_back(cond);
76✔
771
    return *scope.cases_.back();
76✔
772
};
×
773

774
void StructuredSDFGBuilder::remove_case(IfElse& scope, size_t i, const DebugInfo& debug_info) {
4✔
775
    scope.cases_.erase(scope.cases_.begin() + i);
4✔
776
    scope.conditions_.erase(scope.conditions_.begin() + i);
4✔
777
};
4✔
778

779
While& StructuredSDFGBuilder::
780
    add_while(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
33✔
781
    parent.children_.push_back(std::unique_ptr<While>(new While(this->new_element_id(), debug_info)));
33✔
782

783
    // Increment element id for body node
784
    this->new_element_id();
33✔
785

786
    parent.transitions_
66✔
787
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
33✔
788
        );
789

790
    return static_cast<While&>(*parent.children_.back().get());
33✔
791
};
×
792

793
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent, const DebugInfo& debug_info) {
12✔
794
    return this->add_continue(parent, control_flow::Assignments{}, debug_info);
12✔
795
};
×
796

797
Continue& StructuredSDFGBuilder::
798
    add_continue(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
14✔
799
    // Check if continue is in a loop
800
    analysis::AnalysisManager analysis_manager(this->subject());
14✔
801
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
14✔
802
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
14✔
803
    bool in_loop = false;
14✔
804
    while (current_scope != nullptr) {
24✔
805
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
24✔
806
            in_loop = true;
14✔
807
            break;
14✔
808
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
10✔
809
            throw UnstructuredControlFlowException();
×
810
        }
811
        current_scope = scope_tree_analysis.parent_scope(current_scope);
10✔
812
    }
813
    if (!in_loop) {
14✔
814
        throw UnstructuredControlFlowException();
×
815
    }
816

817
    parent.children_.push_back(std::unique_ptr<Continue>(new Continue(this->new_element_id(), debug_info)));
14✔
818

819
    parent.transitions_
28✔
820
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
14✔
821
        );
822

823
    return static_cast<Continue&>(*parent.children_.back().get());
14✔
824
};
14✔
825

826
Break& StructuredSDFGBuilder::add_break(Sequence& parent, const DebugInfo& debug_info) {
13✔
827
    return this->add_break(parent, control_flow::Assignments{}, debug_info);
13✔
828
};
×
829

830
Break& StructuredSDFGBuilder::
831
    add_break(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
15✔
832
    // Check if break is in a loop
833
    analysis::AnalysisManager analysis_manager(this->subject());
15✔
834
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeAnalysis>();
15✔
835
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
15✔
836
    bool in_loop = false;
15✔
837
    while (current_scope != nullptr) {
25✔
838
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
25✔
839
            in_loop = true;
15✔
840
            break;
15✔
841
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
10✔
842
            throw UnstructuredControlFlowException();
×
843
        }
844
        current_scope = scope_tree_analysis.parent_scope(current_scope);
10✔
845
    }
846
    if (!in_loop) {
15✔
847
        throw UnstructuredControlFlowException();
×
848
    }
849

850
    parent.children_.push_back(std::unique_ptr<Break>(new Break(this->new_element_id(), debug_info)));
15✔
851

852
    parent.transitions_
30✔
853
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
15✔
854
        );
855

856
    return static_cast<Break&>(*parent.children_.back().get());
15✔
857
};
15✔
858

859
Return& StructuredSDFGBuilder::
860
    add_return(Sequence& parent, const sdfg::control_flow::Assignments& assignments, const DebugInfo& debug_info) {
14✔
861
    parent.children_.push_back(std::unique_ptr<Return>(new Return(this->new_element_id(), debug_info)));
14✔
862

863
    parent.transitions_
28✔
864
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
14✔
865
        );
866

867
    return static_cast<Return&>(*parent.children_.back().get());
14✔
868
};
×
869

870
Map& StructuredSDFGBuilder::add_map(
25✔
871
    Sequence& parent,
872
    const symbolic::Symbol& indvar,
873
    const symbolic::Condition& condition,
874
    const symbolic::Expression& init,
875
    const symbolic::Expression& update,
876
    const ScheduleType& schedule_type,
877
    const sdfg::control_flow::Assignments& assignments,
878
    const DebugInfo& debug_info
879
) {
880
    parent.children_
50✔
881
        .push_back(std::unique_ptr<
25✔
882
                   Map>(new Map(this->new_element_id(), debug_info, indvar, init, update, condition, schedule_type)));
25✔
883

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

887
    parent.transitions_
50✔
888
        .push_back(std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent, assignments))
25✔
889
        );
890

891
    return static_cast<Map&>(*parent.children_.back().get());
25✔
892
};
×
893

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

915
    parent.children_.insert(
8✔
916
        parent.children_.begin() + index,
4✔
917
        std::unique_ptr<Map>(new Map(this->new_element_id(), debug_info, indvar, init, update, condition, schedule_type)
4✔
918
        )
919
    );
920

921
    // Increment element id for body node
922
    this->new_element_id();
4✔
923

924
    parent.transitions_.insert(
8✔
925
        parent.transitions_.begin() + index,
4✔
926
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
4✔
927
    );
928

929
    auto new_entry = parent.at(index);
4✔
930
    auto& new_block = dynamic_cast<structured_control_flow::Map&>(new_entry.first);
4✔
931

932
    return {new_block, new_entry.second};
4✔
933
};
×
934

935
std::pair<Map&, Transition&> StructuredSDFGBuilder::add_map_after(
8✔
936
    Sequence& parent,
937
    ControlFlowNode& block,
938
    const symbolic::Symbol& indvar,
939
    const symbolic::Condition& condition,
940
    const symbolic::Expression& init,
941
    const symbolic::Expression& update,
942
    const ScheduleType& schedule_type,
943
    const sdfg::control_flow::Assignments& assignments,
944
    const DebugInfo& debug_info
945
) {
946
    // Insert block before current block
947
    int index = -1;
8✔
948
    for (size_t i = 0; i < parent.children_.size(); i++) {
8✔
949
        if (parent.children_.at(i).get() == &block) {
8✔
950
            index = i;
8✔
951
            break;
8✔
952
        }
953
    }
×
954
    assert(index > -1);
8✔
955

956
    parent.children_.insert(
16✔
957
        parent.children_.begin() + index + 1,
8✔
958
        std::unique_ptr<Map>(new Map(this->new_element_id(), debug_info, indvar, init, update, condition, schedule_type)
8✔
959
        )
960
    );
961

962
    // Increment element id for body node
963
    this->new_element_id();
8✔
964

965
    parent.transitions_.insert(
16✔
966
        parent.transitions_.begin() + index + 1,
8✔
967
        std::unique_ptr<Transition>(new Transition(this->new_element_id(), debug_info, parent))
8✔
968
    );
969

970
    auto new_entry = parent.at(index + 1);
8✔
971
    auto& new_block = dynamic_cast<structured_control_flow::Map&>(new_entry.first);
8✔
972

973
    return {new_block, new_entry.second};
8✔
974
};
×
975

NEW
976
For& StructuredSDFGBuilder::convert_while(
×
977
    Sequence& parent,
978
    While& loop,
979
    const symbolic::Symbol& indvar,
980
    const symbolic::Condition& condition,
981
    const symbolic::Expression& init,
982
    const symbolic::Expression& update
983
) {
984
    // Insert for loop
985
    size_t index = 0;
×
986
    for (auto& entry : parent.children_) {
×
987
        if (entry.get() == &loop) {
×
988
            break;
×
989
        }
990
        index++;
×
991
    }
992
    auto iter = parent.children_.begin() + index;
×
993
    auto& new_iter = *parent.children_.insert(
×
NEW
994
        iter + 1,
×
NEW
995
        std::unique_ptr<For>(new For(this->new_element_id(), loop.debug_info(), indvar, init, update, condition))
×
996
    );
997

998
    // Increment element id for body node
999
    this->new_element_id();
×
1000

1001
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
1002
    this->insert_children(for_loop.root(), loop.root(), 0);
×
1003

1004
    // Remove while loop
1005
    parent.children_.erase(parent.children_.begin() + index);
×
1006

1007
    return for_loop;
×
1008
};
×
1009

1010
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop) {
8✔
1011
    // Insert for loop
1012
    size_t index = 0;
8✔
1013
    for (auto& entry : parent.children_) {
8✔
1014
        if (entry.get() == &loop) {
8✔
1015
            break;
8✔
1016
        }
1017
        index++;
×
1018
    }
1019
    auto iter = parent.children_.begin() + index;
8✔
1020
    auto& new_iter = *parent.children_.insert(
16✔
1021
        iter + 1,
8✔
1022
        std::unique_ptr<Map>(new Map(
16✔
1023
            this->new_element_id(),
8✔
1024
            loop.debug_info(),
8✔
1025
            loop.indvar(),
8✔
1026
            loop.init(),
8✔
1027
            loop.update(),
8✔
1028
            loop.condition(),
8✔
1029
            ScheduleType_Sequential
1030
        ))
1031
    );
1032

1033
    // Increment element id for body node
1034
    this->new_element_id();
8✔
1035

1036
    auto& map = dynamic_cast<Map&>(*new_iter);
8✔
1037
    this->insert_children(map.root(), loop.root(), 0);
8✔
1038

1039
    // Remove for loop
1040
    parent.children_.erase(parent.children_.begin() + index);
8✔
1041

1042
    return map;
8✔
1043
};
×
1044

1045
void StructuredSDFGBuilder::clear_sequence(Sequence& parent) {
×
1046
    parent.children_.clear();
×
1047
    parent.transitions_.clear();
×
1048
};
×
1049

1050
Sequence& StructuredSDFGBuilder::parent(const ControlFlowNode& node) {
2✔
1051
    std::list<structured_control_flow::ControlFlowNode*> queue = {&this->structured_sdfg_->root()};
2✔
1052
    while (!queue.empty()) {
2✔
1053
        auto current = queue.front();
2✔
1054
        queue.pop_front();
2✔
1055

1056
        if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
2✔
1057
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
2✔
1058
                if (&sequence_stmt->at(i).first == &node) {
2✔
1059
                    return *sequence_stmt;
2✔
1060
                }
1061
                queue.push_back(&sequence_stmt->at(i).first);
×
1062
            }
×
1063
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
×
1064
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
1065
                queue.push_back(&if_else_stmt->at(i).first);
×
1066
            }
×
1067
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
×
1068
            queue.push_back(&while_stmt->root());
×
1069
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
×
1070
            queue.push_back(&for_stmt->root());
×
1071
        }
×
1072
    }
1073

1074
    return this->structured_sdfg_->root();
×
1075
};
2✔
1076

1077
/***** Section: Dataflow Graph *****/
1078

1079
data_flow::AccessNode& StructuredSDFGBuilder::
1080
    add_access(structured_control_flow::Block& block, const std::string& data, const DebugInfo& debug_info) {
599✔
1081
    // Check: Data exists
1082
    if (!this->subject().exists(data)) {
599✔
1083
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
1084
    }
1085

1086
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
599✔
1087
    auto res = block.dataflow_->nodes_.insert(
1,198✔
1088
        {vertex,
599✔
1089
         std::unique_ptr<data_flow::AccessNode>(
599✔
1090
             new data_flow::AccessNode(this->new_element_id(), debug_info, vertex, block.dataflow(), data)
599✔
1091
         )}
1092
    );
1093

1094
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
599✔
1095
};
×
1096

1097
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
377✔
1098
    structured_control_flow::Block& block,
1099
    const data_flow::TaskletCode code,
1100
    const std::pair<std::string, sdfg::types::Scalar>& output,
1101
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
1102
    const DebugInfo& debug_info
1103
) {
1104
    // Check: Duplicate inputs
1105
    std::unordered_set<std::string> input_names;
377✔
1106
    for (auto& input : inputs) {
881✔
1107
        if (!input.first.starts_with("_in")) {
504✔
1108
            continue;
281✔
1109
        }
1110
        if (input_names.find(input.first) != input_names.end()) {
223✔
1111
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
1112
        }
1113
        input_names.insert(input.first);
223✔
1114
    }
1115

1116
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
377✔
1117
    auto res = block.dataflow_->nodes_.insert(
754✔
1118
        {vertex,
377✔
1119
         std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
754✔
1120
             this->new_element_id(), debug_info, vertex, block.dataflow(), code, output, inputs, symbolic::__true__()
377✔
1121
         ))}
1122
    );
1123

1124
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
377✔
1125
};
377✔
1126

1127
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
608✔
1128
    structured_control_flow::Block& block,
1129
    data_flow::DataFlowNode& src,
1130
    const std::string& src_conn,
1131
    data_flow::DataFlowNode& dst,
1132
    const std::string& dst_conn,
1133
    const data_flow::Subset& subset,
1134
    const DebugInfo& debug_info
1135
) {
1136
    auto& function_ = this->function();
608✔
1137

1138
    // Check - Case 1: Access Node -> Access Node
1139
    // - src_conn or dst_conn must be refs. The other must be void.
1140
    // - The side of the memlet that is void, is dereferenced.
1141
    // - The dst type must always be a pointer after potential dereferencing.
1142
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
1143
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
608✔
1144
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
1✔
1145
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
1✔
1146
        if (src_conn == "refs") {
1✔
1147
            if (dst_conn != "void") {
×
1148
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
1149
            }
1150

1151
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
1152
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
×
1153
                throw InvalidSDFGException("dst type must be a pointer");
×
1154
            }
1155

1156
            auto& src_type = function_.type(src_node.data());
×
1157
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
×
1158
                throw InvalidSDFGException("src type must be a pointer");
×
1159
            }
1160
        } else if (src_conn == "void") {
1✔
1161
            if (dst_conn != "refs") {
1✔
1162
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
1163
            }
1164

1165
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
1✔
1166
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
1167
            }
1168

1169
            // Trivially correct but checks inference
1170
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
1✔
1171
            types::Pointer ref_type(src_type);
1✔
1172
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
1173
                throw InvalidSDFGException("src type must be a pointer");
1174
            }
1175

1176
            auto& dst_type = function_.type(dst_node.data());
1✔
1177
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
1✔
1178
                throw InvalidSDFGException("dst type must be a pointer");
×
1179
            }
1180
        } else {
1✔
1181
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
1182
        }
1183
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::Tasklet*>(&dst)) {
608✔
1184
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
229✔
1185
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
229✔
1186
        if (src_conn != "void") {
229✔
1187
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1188
        }
1189
        bool found = false;
229✔
1190
        for (auto& input : dst_node.inputs()) {
306✔
1191
            if (input.first == dst_conn) {
306✔
1192
                found = true;
229✔
1193
                break;
229✔
1194
            }
1195
        }
1196
        if (!found) {
229✔
1197
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
1198
        }
1199
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
229✔
1200
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
229✔
1201
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
1202
        }
1203
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
607✔
1204
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
378✔
1205
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
378✔
1206
        if (src_conn != src_node.output().first) {
378✔
1207
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
1208
        }
1209
        if (dst_conn != "void") {
378✔
1210
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1211
        }
1212

1213
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
378✔
1214
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
378✔
1215
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
1216
        }
1217
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::LibraryNode*>(&dst)) {
378✔
1218
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
1219
        if (src_conn != "void") {
×
1220
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1221
        }
1222
        bool found = false;
×
1223
        for (auto& input : dst_node.inputs()) {
×
1224
            if (input == dst_conn) {
×
1225
                found = true;
×
1226
                break;
×
1227
            }
1228
        }
1229
        if (!found) {
×
1230
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
1231
        }
NEW
1232
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
1233
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
1234
        if (dst_conn != "void") {
×
1235
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1236
        }
1237
        bool found = false;
×
1238
        for (auto& output : src_node.outputs()) {
×
1239
            if (output == src_conn) {
×
1240
                found = true;
×
1241
                break;
×
1242
            }
1243
        }
1244
        if (!found) {
×
1245
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
1246
        }
1247
    } else {
×
1248
        throw InvalidSDFGException("Invalid src or dst node type");
×
1249
    }
1250

1251
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
608✔
1252
    auto res = block.dataflow_->edges_.insert(
1,216✔
1253
        {edge.first,
1,216✔
1254
         std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
1,216✔
1255
             this->new_element_id(), debug_info, edge.first, block.dataflow(), src, src_conn, dst, dst_conn, subset
608✔
1256
         ))}
1257
    );
1258

1259
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
608✔
1260
};
×
1261

NEW
1262
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block, const data_flow::Memlet& edge) {
×
UNCOV
1263
    auto& graph = block.dataflow();
×
1264
    auto e = edge.edge();
×
1265
    boost::remove_edge(e, graph.graph_);
×
1266
    graph.edges_.erase(e);
×
1267
};
×
1268

NEW
1269
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block, const data_flow::DataFlowNode& node) {
×
UNCOV
1270
    auto& graph = block.dataflow();
×
1271
    auto v = node.vertex();
×
1272
    boost::remove_vertex(v, graph.graph_);
×
1273
    graph.nodes_.erase(v);
×
1274
};
×
1275

1276
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block, const data_flow::Tasklet& node) {
8✔
1277
    auto& graph = block.dataflow();
8✔
1278

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

1281
    // Delete incoming
1282
    std::list<const data_flow::Memlet*> iedges;
8✔
1283
    for (auto& iedge : graph.in_edges(node)) {
15✔
1284
        iedges.push_back(&iedge);
7✔
1285
    }
1286
    for (auto iedge : iedges) {
15✔
1287
        auto& src = iedge->src();
7✔
1288
        to_delete.insert(&src);
7✔
1289

1290
        auto edge = iedge->edge();
7✔
1291
        graph.edges_.erase(edge);
7✔
1292
        boost::remove_edge(edge, graph.graph_);
7✔
1293
    }
1294

1295
    // Delete outgoing
1296
    std::list<const data_flow::Memlet*> oedges;
8✔
1297
    for (auto& oedge : graph.out_edges(node)) {
16✔
1298
        oedges.push_back(&oedge);
8✔
1299
    }
1300
    for (auto oedge : oedges) {
16✔
1301
        auto& dst = oedge->dst();
8✔
1302
        to_delete.insert(&dst);
8✔
1303

1304
        auto edge = oedge->edge();
8✔
1305
        graph.edges_.erase(edge);
8✔
1306
        boost::remove_edge(edge, graph.graph_);
8✔
1307
    }
1308

1309
    // Delete nodes
1310
    for (auto obsolete_node : to_delete) {
31✔
1311
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
23✔
1312
            auto vertex = obsolete_node->vertex();
23✔
1313
            graph.nodes_.erase(vertex);
23✔
1314
            boost::remove_vertex(vertex, graph.graph_);
23✔
1315
        }
23✔
1316
    }
1317
};
8✔
1318

1319
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block, const data_flow::AccessNode& node) {
1✔
1320
    auto& graph = block.dataflow();
1✔
1321
    if (graph.out_degree(node) != 0) {
1✔
1322
        throw InvalidSDFGException("StructuredSDFGBuilder: Access node has outgoing edges");
×
1323
    }
1324

1325
    std::list<const data_flow::Memlet*> tmp;
1✔
1326
    std::list<const data_flow::DataFlowNode*> queue = {&node};
1✔
1327
    while (!queue.empty()) {
3✔
1328
        auto current = queue.front();
2✔
1329
        queue.pop_front();
2✔
1330
        if (current != &node) {
2✔
1331
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
1✔
1332
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
1333
                    continue;
×
1334
                }
1335
            }
×
1336
        }
1✔
1337

1338
        tmp.clear();
2✔
1339
        for (auto& iedge : graph.in_edges(*current)) {
3✔
1340
            tmp.push_back(&iedge);
1✔
1341
        }
1342
        for (auto iedge : tmp) {
3✔
1343
            auto& src = iedge->src();
1✔
1344
            queue.push_back(&src);
1✔
1345

1346
            auto edge = iedge->edge();
1✔
1347
            graph.edges_.erase(edge);
1✔
1348
            boost::remove_edge(edge, graph.graph_);
1✔
1349
        }
1350

1351
        auto vertex = current->vertex();
2✔
1352
        graph.nodes_.erase(vertex);
2✔
1353
        boost::remove_vertex(vertex, graph.graph_);
2✔
1354
    }
1355
};
1✔
1356

1357
data_flow::AccessNode& StructuredSDFGBuilder::
NEW
1358
    symbolic_expression_to_dataflow(structured_control_flow::Block& parent, const symbolic::Expression& expr) {
×
UNCOV
1359
    auto& sdfg = this->subject();
×
1360

1361
    codegen::CPPLanguageExtension language_extension;
×
1362

1363
    // Base cases
1364
    if (SymEngine::is_a<SymEngine::Symbol>(*expr)) {
×
1365
        auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(expr);
×
1366

1367
        // Determine type
1368
        types::Scalar sym_type = types::Scalar(types::PrimitiveType::Void);
×
1369
        if (symbolic::is_nv(sym)) {
×
1370
            sym_type = types::Scalar(types::PrimitiveType::Int32);
×
1371
        } else {
×
1372
            sym_type = static_cast<const types::Scalar&>(sdfg.type(sym->get_name()));
×
1373
        }
1374

1375
        // Add new container for intermediate result
1376
        auto tmp = this->find_new_name();
×
1377
        this->add_container(tmp, sym_type);
×
1378

1379
        // Create dataflow graph
1380
        auto& input_node = this->add_access(parent, sym->get_name());
×
1381
        auto& output_node = this->add_access(parent, tmp);
×
NEW
1382
        auto& tasklet =
×
NEW
1383
            this->add_tasklet(parent, data_flow::TaskletCode::assign, {"_out", sym_type}, {{"_in", sym_type}});
×
1384
        this->add_memlet(parent, input_node, "void", tasklet, "_in", {});
×
1385
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1386

1387
        return output_node;
×
1388
    } else if (SymEngine::is_a<SymEngine::Integer>(*expr)) {
×
1389
        auto tmp = this->find_new_name();
×
1390
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Int64));
×
1391

1392
        auto& output_node = this->add_access(parent, tmp);
×
1393
        auto& tasklet = this->add_tasklet(
×
NEW
1394
            parent,
×
1395
            data_flow::TaskletCode::assign,
1396
            {"_out", types::Scalar(types::PrimitiveType::Int64)},
×
NEW
1397
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Int64)}}
×
1398
        );
1399
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1400
        return output_node;
×
1401
    } else if (SymEngine::is_a<SymEngine::BooleanAtom>(*expr)) {
×
1402
        auto tmp = this->find_new_name();
×
1403
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1404

1405
        auto& output_node = this->add_access(parent, tmp);
×
1406
        auto& tasklet = this->add_tasklet(
×
NEW
1407
            parent,
×
1408
            data_flow::TaskletCode::assign,
1409
            {"_out", types::Scalar(types::PrimitiveType::Bool)},
×
NEW
1410
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Bool)}}
×
1411
        );
1412
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1413
        return output_node;
×
1414
    } else if (SymEngine::is_a<SymEngine::Or>(*expr)) {
×
1415
        auto or_expr = SymEngine::rcp_static_cast<const SymEngine::Or>(expr);
×
1416
        if (or_expr->get_container().size() != 2) {
×
NEW
1417
            throw InvalidSDFGException("StructuredSDFGBuilder: Or expression must have exactly two arguments");
×
1418
        }
1419

1420
        std::vector<data_flow::AccessNode*> input_nodes;
×
1421
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1422
        for (auto& arg : or_expr->get_container()) {
×
1423
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1424
            input_nodes.push_back(&input_node);
×
1425
            input_types.push_back(
×
1426
                {"_in" + std::to_string(input_types.size() + 1),
×
NEW
1427
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))}
×
1428
            );
1429
        }
1430

1431
        // Add new container for intermediate result
1432
        auto tmp = this->find_new_name();
×
1433
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1434

1435
        auto& output_node = this->add_access(parent, tmp);
×
NEW
1436
        auto& tasklet = this->add_tasklet(
×
NEW
1437
            parent, data_flow::TaskletCode::logical_or, {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types
×
1438
        );
1439
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
NEW
1440
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first, {});
×
1441
        }
×
1442
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1443
        return output_node;
×
1444
    } else if (SymEngine::is_a<SymEngine::And>(*expr)) {
×
1445
        auto and_expr = SymEngine::rcp_static_cast<const SymEngine::And>(expr);
×
1446
        if (and_expr->get_container().size() != 2) {
×
NEW
1447
            throw InvalidSDFGException("StructuredSDFGBuilder: And expression must have exactly two arguments");
×
1448
        }
1449

1450
        std::vector<data_flow::AccessNode*> input_nodes;
×
1451
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1452
        for (auto& arg : and_expr->get_container()) {
×
1453
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1454
            input_nodes.push_back(&input_node);
×
1455
            input_types.push_back(
×
1456
                {"_in" + std::to_string(input_types.size() + 1),
×
NEW
1457
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))}
×
1458
            );
1459
        }
1460

1461
        // Add new container for intermediate result
1462
        auto tmp = this->find_new_name();
×
1463
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1464

1465
        auto& output_node = this->add_access(parent, tmp);
×
NEW
1466
        auto& tasklet = this->add_tasklet(
×
NEW
1467
            parent, data_flow::TaskletCode::logical_and, {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types
×
1468
        );
1469
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
NEW
1470
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first, {});
×
1471
        }
×
1472
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1473
        return output_node;
×
1474
    } else {
×
1475
        throw std::runtime_error("Unsupported expression type");
×
1476
    }
1477
};
×
1478

1479
void StructuredSDFGBuilder::add_dataflow(const data_flow::DataFlowGraph& from, Block& to) {
19✔
1480
    auto& to_dataflow = to.dataflow();
19✔
1481

1482
    std::unordered_map<graph::Vertex, graph::Vertex> node_mapping;
19✔
1483
    for (auto& entry : from.nodes_) {
20✔
1484
        auto vertex = boost::add_vertex(to_dataflow.graph_);
1✔
1485
        to_dataflow.nodes_.insert({vertex, entry.second->clone(this->new_element_id(), vertex, to_dataflow)});
1✔
1486
        node_mapping.insert({entry.first, vertex});
1✔
1487
    }
1488

1489
    for (auto& entry : from.edges_) {
19✔
1490
        auto src = node_mapping[entry.second->src().vertex()];
×
1491
        auto dst = node_mapping[entry.second->dst().vertex()];
×
1492

1493
        auto edge = boost::add_edge(src, dst, to_dataflow.graph_);
×
1494

1495
        to_dataflow.edges_.insert(
×
NEW
1496
            {edge.first,
×
NEW
1497
             entry.second->clone(
×
NEW
1498
                 this->new_element_id(), edge.first, to_dataflow, *to_dataflow.nodes_[src], *to_dataflow.nodes_[dst]
×
1499
             )}
1500
        );
1501
    }
1502
};
19✔
1503

1504
} // namespace builder
1505
} // 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