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

daisytuner / sdfglib / 15348660672

30 May 2025 02:11PM UTC coverage: 58.747% (+0.1%) from 58.631%
15348660672

Pull #48

github

web-flow
Merge eda241ed6 into d1039227e
Pull Request #48: adds scope tree analysis

53 of 66 new or added lines in 2 files covered. (80.3%)

103 existing lines in 1 file now uncovered.

8244 of 14033 relevant lines covered (58.75%)

109.65 hits per line

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

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

3
#include "sdfg/analysis/scope_tree_analysis.h"
4
#include "sdfg/codegen/language_extensions/cpp_language_extension.h"
5
#include "sdfg/data_flow/library_node.h"
6
#include "sdfg/structured_control_flow/map.h"
7
#include "sdfg/structured_control_flow/sequence.h"
8
#include "sdfg/types/utils.h"
9

10
using namespace sdfg::control_flow;
11
using namespace sdfg::structured_control_flow;
12

13
namespace sdfg {
14
namespace builder {
15

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

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

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

39
    return nodes;
1✔
40
};
1✔
41

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

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

57
    return false;
×
58
}
6✔
59

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

71
    return pdom;
3✔
72
}
3✔
73

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

79
    auto pdom_tree = sdfg.post_dominator_tree();
4✔
80

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

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

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

102
    auto in_edges = sdfg.in_edges(*current);
8✔
103

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

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

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

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

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

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

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

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

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

185
        auto out_edges = sdfg.out_edges(*curr);
15✔
186
        auto out_degree = sdfg.out_degree(*curr);
15✔
187

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

195
        // Case 2: Transition
196
        if (out_degree == 1) {
11✔
197
            auto& oedge = *out_edges.begin();
8✔
198
            if (!oedge.is_unconditional()) {
8✔
199
                throw InvalidSDFGException(
×
200
                    "Degenerated structured control flow: Non-deterministic transition");
×
201
            }
202
            this->add_block(scope, curr->dataflow(), oedge.assignments(), curr->debug_info());
8✔
203

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

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

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

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

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

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

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

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

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

291
StructuredSDFGBuilder::StructuredSDFGBuilder(const std::string& name)
460✔
292
    : FunctionBuilder(), structured_sdfg_(new StructuredSDFG(name)) {};
460✔
293

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

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

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

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

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

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

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

323
StructuredSDFG& StructuredSDFGBuilder::subject() const { return *this->structured_sdfg_; };
3,130✔
324

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

329
Sequence& StructuredSDFGBuilder::add_sequence(Sequence& parent,
36✔
330
                                              const sdfg::symbolic::Assignments& assignments,
331
                                              const DebugInfo& debug_info) {
332
    parent.children_.push_back(
36✔
333
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, debug_info)));
36✔
334
    this->element_counter_++;
36✔
335
    parent.transitions_.push_back(std::unique_ptr<Transition>(
36✔
336
        new Transition(this->element_counter_, debug_info, assignments)));
36✔
337
    this->element_counter_++;
36✔
338

339
    return static_cast<Sequence&>(*parent.children_.back().get());
36✔
UNCOV
340
};
×
341

342
std::pair<Sequence&, Transition&> StructuredSDFGBuilder::add_sequence_before(
4✔
343
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
344
    // Insert block before current block
345
    int index = -1;
4✔
346
    for (size_t i = 0; i < parent.children_.size(); i++) {
4✔
347
        if (parent.children_.at(i).get() == &block) {
4✔
348
            index = i;
4✔
349
            break;
4✔
350
        }
UNCOV
351
    }
×
352
    if (index == -1) {
4✔
UNCOV
353
        throw InvalidSDFGException("StructuredSDFGBuilder: Block not found");
×
354
    }
355

356
    parent.children_.insert(
4✔
357
        parent.children_.begin() + index,
4✔
358
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, debug_info)));
4✔
359
    this->element_counter_++;
4✔
360
    parent.transitions_.insert(
4✔
361
        parent.transitions_.begin() + index,
4✔
362
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
4✔
363
    this->element_counter_++;
4✔
364
    auto new_entry = parent.at(index);
4✔
365
    auto& new_block = dynamic_cast<structured_control_flow::Sequence&>(new_entry.first);
4✔
366

367
    return {new_block, new_entry.second};
4✔
UNCOV
368
};
×
369

370
void StructuredSDFGBuilder::remove_child(Sequence& parent, size_t i) {
7✔
371
    parent.children_.erase(parent.children_.begin() + i);
7✔
372
    parent.transitions_.erase(parent.transitions_.begin() + i);
7✔
373
};
7✔
374

375
void StructuredSDFGBuilder::remove_child(Sequence& parent, ControlFlowNode& child) {
3✔
376
    int index = -1;
3✔
377
    for (size_t i = 0; i < parent.children_.size(); i++) {
3✔
378
        if (parent.children_.at(i).get() == &child) {
3✔
379
            index = i;
3✔
380
            break;
3✔
381
        }
UNCOV
382
    }
×
383

384
    parent.children_.erase(parent.children_.begin() + index);
3✔
385
    parent.transitions_.erase(parent.transitions_.begin() + index);
3✔
386
};
3✔
387

388
void StructuredSDFGBuilder::insert_children(Sequence& parent, Sequence& other, size_t i) {
13✔
389
    parent.children_.insert(parent.children_.begin() + i,
26✔
390
                            std::make_move_iterator(other.children_.begin()),
13✔
391
                            std::make_move_iterator(other.children_.end()));
13✔
392
    parent.transitions_.insert(parent.transitions_.begin() + i,
26✔
393
                               std::make_move_iterator(other.transitions_.begin()),
13✔
394
                               std::make_move_iterator(other.transitions_.end()));
13✔
395
    other.children_.clear();
13✔
396
    other.transitions_.clear();
13✔
397
};
13✔
398

399
Block& StructuredSDFGBuilder::add_block(Sequence& parent,
462✔
400
                                        const sdfg::symbolic::Assignments& assignments,
401
                                        const DebugInfo& debug_info) {
402
    parent.children_.push_back(
462✔
403
        std::unique_ptr<Block>(new Block(this->element_counter_, debug_info)));
462✔
404
    this->element_counter_++;
462✔
405
    parent.transitions_.push_back(std::unique_ptr<Transition>(
462✔
406
        new Transition(this->element_counter_, debug_info, assignments)));
462✔
407
    this->element_counter_++;
462✔
408
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
462✔
409
    (*new_block.dataflow_).parent_ = &new_block;
462✔
410

411
    return new_block;
462✔
UNCOV
412
};
×
413

414
Block& StructuredSDFGBuilder::add_block(Sequence& parent,
28✔
415
                                        const data_flow::DataFlowGraph& data_flow_graph,
416
                                        const sdfg::symbolic::Assignments& assignments,
417
                                        const DebugInfo& debug_info) {
418
    parent.children_.push_back(
28✔
419
        std::unique_ptr<Block>(new Block(this->element_counter_, debug_info, data_flow_graph)));
28✔
420
    this->element_counter_++;
28✔
421
    parent.transitions_.push_back(std::unique_ptr<Transition>(
28✔
422
        new Transition(this->element_counter_, debug_info, assignments)));
28✔
423
    this->element_counter_++;
28✔
424
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(*parent.children_.back().get());
28✔
425
    (*new_block.dataflow_).parent_ = &new_block;
28✔
426

427
    return new_block;
28✔
UNCOV
428
};
×
429

430
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
12✔
431
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
432
    // Insert block before current block
433
    int index = -1;
12✔
434
    for (size_t i = 0; i < parent.children_.size(); i++) {
13✔
435
        if (parent.children_.at(i).get() == &block) {
13✔
436
            index = i;
12✔
437
            break;
12✔
438
        }
439
    }
1✔
440
    assert(index > -1);
12✔
441

442
    parent.children_.insert(parent.children_.begin() + index,
12✔
443
                            std::unique_ptr<Block>(new Block(this->element_counter_, debug_info)));
12✔
444
    this->element_counter_++;
12✔
445
    parent.transitions_.insert(
12✔
446
        parent.transitions_.begin() + index,
12✔
447
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
12✔
448
    this->element_counter_++;
12✔
449
    auto new_entry = parent.at(index);
12✔
450
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
12✔
451
    (*new_block.dataflow_).parent_ = &new_block;
12✔
452

453
    return {new_block, new_entry.second};
12✔
UNCOV
454
};
×
455

UNCOV
456
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_before(
×
457
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph,
458
    const DebugInfo& debug_info) {
459
    // Insert block before current block
460
    int index = -1;
×
461
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
462
        if (parent.children_.at(i).get() == &block) {
×
463
            index = i;
×
UNCOV
464
            break;
×
465
        }
466
    }
×
UNCOV
467
    assert(index > -1);
×
468

469
    parent.children_.insert(
×
470
        parent.children_.begin() + index,
×
471
        std::unique_ptr<Block>(new Block(this->element_counter_, debug_info, data_flow_graph)));
×
472
    this->element_counter_++;
×
473
    parent.transitions_.insert(
×
474
        parent.transitions_.begin() + index,
×
475
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
×
476
    this->element_counter_++;
×
477
    auto new_entry = parent.at(index);
×
478
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
UNCOV
479
    (*new_block.dataflow_).parent_ = &new_block;
×
480

481
    return {new_block, new_entry.second};
×
UNCOV
482
};
×
483

484
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(Sequence& parent,
6✔
485
                                                                      ControlFlowNode& block,
486
                                                                      const DebugInfo& debug_info) {
487
    // Insert block before current block
488
    int index = -1;
6✔
489
    for (size_t i = 0; i < parent.children_.size(); i++) {
7✔
490
        if (parent.children_.at(i).get() == &block) {
7✔
491
            index = i;
6✔
492
            break;
6✔
493
        }
494
    }
1✔
495
    assert(index > -1);
6✔
496

497
    parent.children_.insert(parent.children_.begin() + index + 1,
6✔
498
                            std::unique_ptr<Block>(new Block(this->element_counter_, debug_info)));
6✔
499
    this->element_counter_++;
6✔
500
    parent.transitions_.insert(
6✔
501
        parent.transitions_.begin() + index + 1,
6✔
502
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
6✔
503
    this->element_counter_++;
6✔
504
    auto new_entry = parent.at(index + 1);
6✔
505
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
6✔
506
    (*new_block.dataflow_).parent_ = &new_block;
6✔
507

508
    return {new_block, new_entry.second};
6✔
UNCOV
509
};
×
510

UNCOV
511
std::pair<Block&, Transition&> StructuredSDFGBuilder::add_block_after(
×
512
    Sequence& parent, ControlFlowNode& block, data_flow::DataFlowGraph& data_flow_graph,
513
    const DebugInfo& debug_info) {
514
    int index = -1;
×
515
    for (size_t i = 0; i < parent.children_.size(); i++) {
×
516
        if (parent.children_.at(i).get() == &block) {
×
517
            index = i;
×
UNCOV
518
            break;
×
519
        }
520
    }
×
UNCOV
521
    assert(index > -1);
×
522

523
    parent.children_.insert(
×
524
        parent.children_.begin() + index + 1,
×
525
        std::unique_ptr<Block>(new Block(this->element_counter_, debug_info, data_flow_graph)));
×
526
    this->element_counter_++;
×
527
    parent.transitions_.insert(
×
528
        parent.transitions_.begin() + index + 1,
×
529
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
×
530
    this->element_counter_++;
×
531
    auto new_entry = parent.at(index + 1);
×
532
    auto& new_block = dynamic_cast<structured_control_flow::Block&>(new_entry.first);
×
UNCOV
533
    (*new_block.dataflow_).parent_ = &new_block;
×
534

535
    return {new_block, new_entry.second};
×
UNCOV
536
};
×
537

538
For& StructuredSDFGBuilder::add_for(Sequence& parent, const symbolic::Symbol& indvar,
115✔
539
                                    const symbolic::Condition& condition,
540
                                    const symbolic::Expression& init,
541
                                    const symbolic::Expression& update,
542
                                    const sdfg::symbolic::Assignments& assignments,
543
                                    const DebugInfo& debug_info) {
544
    parent.children_.push_back(std::unique_ptr<For>(
230✔
545
        new For(this->element_counter_, debug_info, indvar, init, update, condition)));
115✔
546
    this->element_counter_ = this->element_counter_ + 2;
115✔
547
    parent.transitions_.push_back(std::unique_ptr<Transition>(
115✔
548
        new Transition(this->element_counter_, debug_info, assignments)));
115✔
549
    this->element_counter_++;
115✔
550

551
    return static_cast<For&>(*parent.children_.back().get());
115✔
UNCOV
552
};
×
553

554
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_before(
4✔
555
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
556
    const symbolic::Condition& condition, const symbolic::Expression& init,
557
    const symbolic::Expression& update, const DebugInfo& debug_info) {
558
    // Insert block before current block
559
    int index = -1;
4✔
560
    for (size_t i = 0; i < parent.children_.size(); i++) {
4✔
561
        if (parent.children_.at(i).get() == &block) {
4✔
562
            index = i;
4✔
563
            break;
4✔
564
        }
UNCOV
565
    }
×
566
    assert(index > -1);
4✔
567

568
    parent.children_.insert(parent.children_.begin() + index,
8✔
569
                            std::unique_ptr<For>(new For(this->element_counter_, debug_info, indvar,
8✔
570
                                                         init, update, condition)));
4✔
571
    this->element_counter_ = this->element_counter_ + 2;
4✔
572
    parent.transitions_.insert(
4✔
573
        parent.transitions_.begin() + index,
4✔
574
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
4✔
575
    this->element_counter_++;
4✔
576
    auto new_entry = parent.at(index);
4✔
577
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
4✔
578

579
    return {new_block, new_entry.second};
4✔
UNCOV
580
};
×
581

582
std::pair<For&, Transition&> StructuredSDFGBuilder::add_for_after(
2✔
583
    Sequence& parent, ControlFlowNode& block, const symbolic::Symbol& indvar,
584
    const symbolic::Condition& condition, const symbolic::Expression& init,
585
    const symbolic::Expression& update, const DebugInfo& debug_info) {
586
    // Insert block before current block
587
    int index = -1;
2✔
588
    for (size_t i = 0; i < parent.children_.size(); i++) {
3✔
589
        if (parent.children_.at(i).get() == &block) {
3✔
590
            index = i;
2✔
591
            break;
2✔
592
        }
593
    }
1✔
594
    assert(index > -1);
2✔
595

596
    parent.children_.insert(parent.children_.begin() + index + 1,
4✔
597
                            std::unique_ptr<For>(new For(this->element_counter_, debug_info, indvar,
4✔
598
                                                         init, update, condition)));
2✔
599
    this->element_counter_ = this->element_counter_ + 2;
2✔
600
    parent.transitions_.insert(
2✔
601
        parent.transitions_.begin() + index + 1,
2✔
602
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
2✔
603
    this->element_counter_++;
2✔
604
    auto new_entry = parent.at(index + 1);
2✔
605
    auto& new_block = dynamic_cast<structured_control_flow::For&>(new_entry.first);
2✔
606

607
    return {new_block, new_entry.second};
2✔
UNCOV
608
};
×
609

610
IfElse& StructuredSDFGBuilder::add_if_else(Sequence& parent, const DebugInfo& debug_info) {
34✔
611
    return this->add_if_else(parent, symbolic::Assignments{}, debug_info);
34✔
UNCOV
612
};
×
613

614
IfElse& StructuredSDFGBuilder::add_if_else(Sequence& parent,
35✔
615
                                           const sdfg::symbolic::Assignments& assignments,
616
                                           const DebugInfo& debug_info) {
617
    parent.children_.push_back(
35✔
618
        std::unique_ptr<IfElse>(new IfElse(this->element_counter_, debug_info)));
35✔
619
    this->element_counter_++;
35✔
620
    parent.transitions_.push_back(std::unique_ptr<Transition>(
35✔
621
        new Transition(this->element_counter_, debug_info, assignments)));
35✔
622
    this->element_counter_++;
35✔
623
    return static_cast<IfElse&>(*parent.children_.back().get());
35✔
UNCOV
624
};
×
625

626
std::pair<IfElse&, Transition&> StructuredSDFGBuilder::add_if_else_before(
1✔
627
    Sequence& parent, ControlFlowNode& block, const DebugInfo& debug_info) {
628
    // Insert block before current block
629
    int index = -1;
1✔
630
    for (size_t i = 0; i < parent.children_.size(); i++) {
1✔
631
        if (parent.children_.at(i).get() == &block) {
1✔
632
            index = i;
1✔
633
            break;
1✔
634
        }
UNCOV
635
    }
×
636
    assert(index > -1);
1✔
637

638
    parent.children_.insert(
1✔
639
        parent.children_.begin() + index,
1✔
640
        std::unique_ptr<IfElse>(new IfElse(this->element_counter_, debug_info)));
1✔
641
    this->element_counter_++;
1✔
642
    parent.transitions_.insert(
1✔
643
        parent.transitions_.begin() + index,
1✔
644
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
1✔
645
    this->element_counter_++;
1✔
646
    auto new_entry = parent.at(index);
1✔
647
    auto& new_block = dynamic_cast<structured_control_flow::IfElse&>(new_entry.first);
1✔
648

649
    return {new_block, new_entry.second};
1✔
UNCOV
650
};
×
651

652
Sequence& StructuredSDFGBuilder::add_case(IfElse& scope, const sdfg::symbolic::Condition cond,
61✔
653
                                          const DebugInfo& debug_info) {
654
    scope.cases_.push_back(
61✔
655
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, debug_info)));
61✔
656
    this->element_counter_++;
61✔
657
    scope.conditions_.push_back(cond);
61✔
658
    return *scope.cases_.back();
61✔
UNCOV
659
};
×
660

661
void StructuredSDFGBuilder::remove_case(IfElse& scope, size_t i, const DebugInfo& debug_info) {
1✔
662
    scope.cases_.erase(scope.cases_.begin() + i);
1✔
663
    scope.conditions_.erase(scope.conditions_.begin() + i);
1✔
664
};
1✔
665

666
While& StructuredSDFGBuilder::add_while(Sequence& parent,
36✔
667
                                        const sdfg::symbolic::Assignments& assignments,
668
                                        const DebugInfo& debug_info) {
669
    parent.children_.push_back(
36✔
670
        std::unique_ptr<While>(new While(this->element_counter_, debug_info)));
36✔
671
    this->element_counter_ = this->element_counter_ + 2;
36✔
672
    parent.transitions_.push_back(std::unique_ptr<Transition>(
36✔
673
        new Transition(this->element_counter_, debug_info, assignments)));
36✔
674
    this->element_counter_++;
36✔
675
    return static_cast<While&>(*parent.children_.back().get());
36✔
UNCOV
676
};
×
677

678
Kernel& StructuredSDFGBuilder::add_kernel(
16✔
679
    Sequence& parent, const std::string& suffix, const DebugInfo& debug_info,
680
    const symbolic::Expression& gridDim_x_init, const symbolic::Expression& gridDim_y_init,
681
    const symbolic::Expression& gridDim_z_init, const symbolic::Expression& blockDim_x_init,
682
    const symbolic::Expression& blockDim_y_init, const symbolic::Expression& blockDim_z_init,
683
    const symbolic::Expression& blockIdx_x_init, const symbolic::Expression& blockIdx_y_init,
684
    const symbolic::Expression& blockIdx_z_init, const symbolic::Expression& threadIdx_x_init,
685
    const symbolic::Expression& threadIdx_y_init, const symbolic::Expression& threadIdx_z_init) {
686
    parent.children_.push_back(std::unique_ptr<Kernel>(new Kernel(
32✔
687
        this->element_counter_, debug_info, suffix, gridDim_x_init, gridDim_y_init, gridDim_z_init,
16✔
688
        blockDim_x_init, blockDim_y_init, blockDim_z_init, blockIdx_x_init, blockIdx_y_init,
16✔
689
        blockIdx_z_init, threadIdx_x_init, threadIdx_y_init, threadIdx_z_init)));
16✔
690
    this->element_counter_ = this->element_counter_ + 2;
16✔
691
    parent.transitions_.push_back(
16✔
692
        std::unique_ptr<Transition>(new Transition(this->element_counter_, debug_info)));
16✔
693
    this->element_counter_++;
16✔
694
    return static_cast<Kernel&>(*parent.children_.back().get());
16✔
UNCOV
695
};
×
696

697
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent, const DebugInfo& debug_info) {
14✔
698
    return this->add_continue(parent, symbolic::Assignments{}, debug_info);
14✔
UNCOV
699
};
×
700

701
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent,
15✔
702
                                              const sdfg::symbolic::Assignments& assignments,
703
                                              const DebugInfo& debug_info) {
704
    // Check if continue is in a loop
705
    analysis::AnalysisManager analysis_manager(this->subject());
15✔
706
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeTreeAnalysis>();
15✔
707
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
15✔
708
    bool in_loop = false;
15✔
709
    while (current_scope != nullptr) {
27✔
710
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
27✔
711
            in_loop = true;
15✔
712
            break;
15✔
713
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
12✔
NEW
714
            throw InvalidSDFGException("Continue statement is not allowed in a for loop");
×
715
        }
716
        current_scope = scope_tree_analysis.parent_scope(current_scope);
12✔
717
    }
718
    if (!in_loop) {
15✔
NEW
719
        throw InvalidSDFGException("Continue statement is not allowed outside of a loop");
×
720
    }
721

722
    parent.children_.push_back(
30✔
723
        std::unique_ptr<Continue>(new Continue(this->element_counter_, debug_info)));
15✔
724
    this->element_counter_++;
15✔
725
    parent.transitions_.push_back(std::unique_ptr<Transition>(
30✔
726
        new Transition(this->element_counter_, debug_info, assignments)));
15✔
727
    this->element_counter_++;
15✔
728
    return static_cast<Continue&>(*parent.children_.back().get());
15✔
729
};
15✔
730

731
Break& StructuredSDFGBuilder::add_break(Sequence& parent, const DebugInfo& debug_info) {
15✔
732
    return this->add_break(parent, symbolic::Assignments{}, debug_info);
15✔
UNCOV
733
};
×
734

735
Break& StructuredSDFGBuilder::add_break(Sequence& parent,
16✔
736
                                        const sdfg::symbolic::Assignments& assignments,
737
                                        const DebugInfo& debug_info) {
738
    // Check if break is in a loop
739
    analysis::AnalysisManager analysis_manager(this->subject());
16✔
740
    auto& scope_tree_analysis = analysis_manager.get<analysis::ScopeTreeAnalysis>();
16✔
741
    auto current_scope = scope_tree_analysis.parent_scope(&parent);
16✔
742
    bool in_loop = false;
16✔
743
    while (current_scope != nullptr) {
28✔
744
        if (dynamic_cast<structured_control_flow::While*>(current_scope)) {
28✔
745
            in_loop = true;
16✔
746
            break;
16✔
747
        } else if (dynamic_cast<structured_control_flow::For*>(current_scope)) {
12✔
NEW
748
            throw InvalidSDFGException("Break statement is not allowed in a for loop");
×
749
        }
750
        current_scope = scope_tree_analysis.parent_scope(current_scope);
12✔
751
    }
752
    if (!in_loop) {
16✔
NEW
753
        throw InvalidSDFGException("Break statement is not allowed outside of a loop");
×
754
    }
755

756
    parent.children_.push_back(
32✔
757
        std::unique_ptr<Break>(new Break(this->element_counter_, debug_info)));
16✔
758
    this->element_counter_++;
16✔
759
    parent.transitions_.push_back(std::unique_ptr<Transition>(
32✔
760
        new Transition(this->element_counter_, debug_info, assignments)));
16✔
761
    this->element_counter_++;
16✔
762
    return static_cast<Break&>(*parent.children_.back().get());
16✔
763
};
16✔
764

765
Return& StructuredSDFGBuilder::add_return(Sequence& parent,
14✔
766
                                          const sdfg::symbolic::Assignments& assignments,
767
                                          const DebugInfo& debug_info) {
768
    parent.children_.push_back(
14✔
769
        std::unique_ptr<Return>(new Return(this->element_counter_, debug_info)));
14✔
770
    this->element_counter_++;
14✔
771
    parent.transitions_.push_back(std::unique_ptr<Transition>(
14✔
772
        new Transition(this->element_counter_, debug_info, assignments)));
14✔
773
    this->element_counter_++;
14✔
774
    return static_cast<Return&>(*parent.children_.back().get());
14✔
UNCOV
775
};
×
776

777
Map& StructuredSDFGBuilder::add_map(Sequence& parent, const symbolic::Symbol& indvar,
9✔
778
                                    const symbolic::Expression& num_iterations,
779
                                    const sdfg::symbolic::Assignments& assignments,
780
                                    const DebugInfo& debug_info) {
781
    parent.children_.push_back(
18✔
782
        std::unique_ptr<Map>(new Map(this->element_counter_, debug_info, indvar, num_iterations)));
9✔
783
    this->element_counter_ = this->element_counter_ + 2;
9✔
784
    parent.transitions_.push_back(std::unique_ptr<Transition>(
9✔
785
        new Transition(this->element_counter_, debug_info, assignments)));
9✔
786
    this->element_counter_++;
9✔
787

788
    return static_cast<Map&>(*parent.children_.back().get());
9✔
UNCOV
789
};
×
790

UNCOV
791
For& StructuredSDFGBuilder::convert_while(Sequence& parent, While& loop,
×
792
                                          const symbolic::Symbol& indvar,
793
                                          const symbolic::Condition& condition,
794
                                          const symbolic::Expression& init,
795
                                          const symbolic::Expression& update) {
796
    // Insert for loop
797
    size_t index = 0;
×
798
    for (auto& entry : parent.children_) {
×
799
        if (entry.get() == &loop) {
×
UNCOV
800
            break;
×
801
        }
UNCOV
802
        index++;
×
803
    }
804
    auto iter = parent.children_.begin() + index;
×
805
    auto& new_iter = *parent.children_.insert(
×
806
        iter + 1, std::unique_ptr<For>(new For(this->element_counter_, loop.debug_info(), indvar,
×
807
                                               init, update, condition)));
×
808
    this->element_counter_ = this->element_counter_ + 2;
×
809
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
UNCOV
810
    this->insert_children(for_loop.root(), loop.root(), 0);
×
811

812
    // Remove while loop
UNCOV
813
    parent.children_.erase(parent.children_.begin() + index);
×
814

815
    return for_loop;
×
UNCOV
816
};
×
817

818
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop,
4✔
819
                                        const symbolic::Expression& num_iterations) {
820
    // Insert for loop
821
    size_t index = 0;
4✔
822
    for (auto& entry : parent.children_) {
4✔
823
        if (entry.get() == &loop) {
4✔
824
            break;
4✔
825
        }
UNCOV
826
        index++;
×
827
    }
828
    auto iter = parent.children_.begin() + index;
4✔
829
    auto& new_iter = *parent.children_.insert(
8✔
830
        iter + 1, std::unique_ptr<Map>(new Map(this->element_counter_, loop.debug_info(),
8✔
831
                                               loop.indvar(), num_iterations)));
4✔
832
    this->element_counter_ = this->element_counter_ + 2;
4✔
833
    auto& map = dynamic_cast<Map&>(*new_iter);
4✔
834
    this->insert_children(map.root(), loop.root(), 0);
4✔
835

836
    // Remove for loop
837
    parent.children_.erase(parent.children_.begin() + index);
4✔
838

839
    return map;
4✔
UNCOV
840
};
×
841

842
void StructuredSDFGBuilder::clear_sequence(Sequence& parent) {
2✔
843
    parent.children_.clear();
2✔
844
    parent.transitions_.clear();
2✔
845
};
2✔
846

847
Sequence& StructuredSDFGBuilder::parent(const ControlFlowNode& node) {
6✔
848
    std::list<structured_control_flow::ControlFlowNode*> queue = {&this->structured_sdfg_->root()};
6✔
849
    while (!queue.empty()) {
6✔
850
        auto current = queue.front();
6✔
851
        queue.pop_front();
6✔
852

853
        if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
6✔
854
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
6✔
855
                if (&sequence_stmt->at(i).first == &node) {
6✔
856
                    return *sequence_stmt;
6✔
857
                }
858
                queue.push_back(&sequence_stmt->at(i).first);
×
859
            }
×
860
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
×
861
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
862
                queue.push_back(&if_else_stmt->at(i).first);
×
863
            }
×
864
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
×
865
            queue.push_back(&while_stmt->root());
×
866
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
×
867
            queue.push_back(&for_stmt->root());
×
868
        } else if (auto kern_stmt = dynamic_cast<const structured_control_flow::Kernel*>(current)) {
×
869
            queue.push_back(&kern_stmt->root());
×
UNCOV
870
        }
×
871
    }
872

UNCOV
873
    return this->structured_sdfg_->root();
×
874
};
6✔
875

876
Kernel& StructuredSDFGBuilder::convert_into_kernel() {
5✔
877
    auto old_root = std::move(this->structured_sdfg_->root_);
5✔
878
    this->structured_sdfg_->root_ =
5✔
879
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, old_root->debug_info()));
5✔
880
    this->element_counter_++;
5✔
881
    auto& new_root = this->structured_sdfg_->root();
5✔
882
    auto& kernel = this->add_kernel(new_root, this->function().name());
5✔
883

884
    this->insert_children(kernel.root(), *old_root, 0);
5✔
885

886
    types::Scalar gridDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
887
    add_container(kernel.gridDim_x()->get_name(), gridDim_x);
5✔
888
    types::Scalar gridDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
889
    add_container(kernel.gridDim_y()->get_name(), gridDim_y);
5✔
890
    types::Scalar gridDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
891
    add_container(kernel.gridDim_z()->get_name(), gridDim_z);
5✔
892
    types::Scalar blockDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
893
    add_container(kernel.blockDim_x()->get_name(), blockDim_x);
5✔
894
    types::Scalar blockDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
895
    add_container(kernel.blockDim_y()->get_name(), blockDim_y);
5✔
896
    types::Scalar blockDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
897
    add_container(kernel.blockDim_z()->get_name(), blockDim_z);
5✔
898
    types::Scalar blockIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
899
    add_container(kernel.blockIdx_x()->get_name(), blockIdx_x);
5✔
900
    types::Scalar blockIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
901
    add_container(kernel.blockIdx_y()->get_name(), blockIdx_y);
5✔
902
    types::Scalar blockIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
903
    add_container(kernel.blockIdx_z()->get_name(), blockIdx_z);
5✔
904
    types::Scalar threadIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
905
    add_container(kernel.threadIdx_x()->get_name(), threadIdx_x);
5✔
906
    types::Scalar threadIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
907
    add_container(kernel.threadIdx_y()->get_name(), threadIdx_y);
5✔
908
    types::Scalar threadIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
909
    add_container(kernel.threadIdx_z()->get_name(), threadIdx_z);
5✔
910

911
    kernel.root().replace(symbolic::symbol("gridDim.x"), kernel.gridDim_x());
5✔
912
    kernel.root().replace(symbolic::symbol("gridDim.y"), kernel.gridDim_y());
5✔
913
    kernel.root().replace(symbolic::symbol("gridDim.z"), kernel.gridDim_z());
5✔
914

915
    kernel.root().replace(symbolic::symbol("blockDim.x"), kernel.blockDim_x());
5✔
916
    kernel.root().replace(symbolic::symbol("blockDim.y"), kernel.blockDim_y());
5✔
917
    kernel.root().replace(symbolic::symbol("blockDim.z"), kernel.blockDim_z());
5✔
918

919
    kernel.root().replace(symbolic::symbol("blockIdx.x"), kernel.blockIdx_x());
5✔
920
    kernel.root().replace(symbolic::symbol("blockIdx.y"), kernel.blockIdx_y());
5✔
921
    kernel.root().replace(symbolic::symbol("blockIdx.z"), kernel.blockIdx_z());
5✔
922

923
    kernel.root().replace(symbolic::symbol("threadIdx.x"), kernel.threadIdx_x());
5✔
924
    kernel.root().replace(symbolic::symbol("threadIdx.y"), kernel.threadIdx_y());
5✔
925
    kernel.root().replace(symbolic::symbol("threadIdx.z"), kernel.threadIdx_z());
5✔
926

927
    return kernel;
5✔
928
};
5✔
929

930
/***** Section: Dataflow Graph *****/
931

932
data_flow::AccessNode& StructuredSDFGBuilder::add_access(structured_control_flow::Block& block,
547✔
933
                                                         const std::string& data,
934
                                                         const DebugInfo& debug_info) {
935
    // Check: Data exists
936
    if (!this->subject().exists(data)) {
547✔
UNCOV
937
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
938
    }
939

940
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
547✔
941
    auto res = block.dataflow_->nodes_.insert(
1,094✔
942
        {vertex, std::unique_ptr<data_flow::AccessNode>(new data_flow::AccessNode(
547✔
943
                     this->element_counter_, debug_info, vertex, block.dataflow(), data))});
547✔
944
    this->element_counter_++;
547✔
945
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
547✔
UNCOV
946
};
×
947

948
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
350✔
949
    structured_control_flow::Block& block, const data_flow::TaskletCode code,
950
    const std::pair<std::string, sdfg::types::Scalar>& output,
951
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
952
    const DebugInfo& debug_info) {
953
    // Check: Duplicate inputs
954
    std::unordered_set<std::string> input_names;
350✔
955
    for (auto& input : inputs) {
804✔
956
        if (input_names.find(input.first) != input_names.end()) {
454✔
UNCOV
957
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
958
        }
959
        input_names.insert(input.first);
454✔
960
    }
961

962
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
350✔
963
    auto res = block.dataflow_->nodes_.insert(
700✔
964
        {vertex, std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
700✔
965
                     this->element_counter_, debug_info, vertex, block.dataflow(), code, output,
350✔
966
                     inputs, symbolic::__true__()))});
350✔
967
    this->element_counter_++;
350✔
968
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
350✔
969
};
350✔
970

971
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
558✔
972
    structured_control_flow::Block& block, data_flow::DataFlowNode& src,
973
    const std::string& src_conn, data_flow::DataFlowNode& dst, const std::string& dst_conn,
974
    const data_flow::Subset& subset, const DebugInfo& debug_info) {
975
    auto& function_ = this->function();
558✔
976

977
    // Check - Case 1: Access Node -> Access Node
978
    // - src_conn or dst_conn must be refs. The other must be void.
979
    // - The side of the memlet that is void, is dereferenced.
980
    // - The dst type must always be a pointer after potential dereferencing.
981
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
982
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
558✔
983
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
3✔
984
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
3✔
985
        if (src_conn == "refs") {
3✔
986
            if (dst_conn != "void") {
1✔
UNCOV
987
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
988
            }
989

990
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
1✔
991
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
1✔
UNCOV
992
                throw InvalidSDFGException("dst type must be a pointer");
×
993
            }
994

995
            auto& src_type = function_.type(src_node.data());
1✔
996
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
1✔
UNCOV
997
                throw InvalidSDFGException("src type must be a pointer");
×
998
            }
999
        } else if (src_conn == "void") {
3✔
1000
            if (dst_conn != "refs") {
2✔
UNCOV
1001
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
1002
            }
1003

1004
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
2✔
UNCOV
1005
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
1006
            }
1007

1008
            // Trivially correct but checks inference
1009
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
2✔
1010
            types::Pointer ref_type(src_type);
2✔
1011
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
1012
                throw InvalidSDFGException("src type must be a pointer");
1013
            }
1014

1015
            auto& dst_type = function_.type(dst_node.data());
2✔
1016
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
2✔
UNCOV
1017
                throw InvalidSDFGException("dst type must be a pointer");
×
1018
            }
1019
        } else {
2✔
UNCOV
1020
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
1021
        }
1022
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
763✔
1023
               dynamic_cast<data_flow::Tasklet*>(&dst)) {
205✔
1024
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
205✔
1025
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
205✔
1026
        if (src_conn != "void") {
205✔
UNCOV
1027
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1028
        }
1029
        bool found = false;
205✔
1030
        for (auto& input : dst_node.inputs()) {
259✔
1031
            if (input.first == dst_conn) {
259✔
1032
                found = true;
205✔
1033
                break;
205✔
1034
            }
1035
        }
1036
        if (!found) {
205✔
UNCOV
1037
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
1038
        }
1039
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
205✔
1040
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
205✔
UNCOV
1041
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
1042
        }
1043
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) &&
905✔
1044
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
350✔
1045
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
350✔
1046
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
350✔
1047
        if (src_conn != src_node.outputs()[0].first) {
350✔
UNCOV
1048
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
1049
        }
1050
        if (dst_conn != "void") {
350✔
UNCOV
1051
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1052
        }
1053

1054
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
350✔
1055
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
350✔
UNCOV
1056
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
1057
        }
1058
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
350✔
1059
               dynamic_cast<data_flow::LibraryNode*>(&dst)) {
×
1060
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
×
1061
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
1062
        if (src_conn != "void") {
×
UNCOV
1063
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1064
        }
1065
        bool found = false;
×
1066
        for (auto& input : dst_node.inputs()) {
×
1067
            if (input.first == dst_conn) {
×
1068
                found = true;
×
UNCOV
1069
                break;
×
1070
            }
1071
        }
1072
        if (!found) {
×
UNCOV
1073
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
1074
        }
1075

1076
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
×
1077
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
×
UNCOV
1078
            throw InvalidSDFGException("Library node inputs must be scalars");
×
1079
        }
1080
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) &&
×
1081
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
1082
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
1083
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
×
1084
        if (dst_conn != "void") {
×
UNCOV
1085
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1086
        }
1087
        bool found = false;
×
1088
        for (auto& output : src_node.outputs()) {
×
1089
            if (output.first == src_conn) {
×
1090
                found = true;
×
UNCOV
1091
                break;
×
1092
            }
1093
        }
1094
        if (!found) {
×
UNCOV
1095
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
1096
        }
1097

1098
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
1099
        if (!dynamic_cast<const types::Pointer*>(&element_type)) {
×
UNCOV
1100
            throw InvalidSDFGException("Access node must be a pointer");
×
1101
        }
1102
    } else {
×
UNCOV
1103
        throw InvalidSDFGException("Invalid src or dst node type");
×
1104
    }
1105

1106
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
558✔
1107
    auto res = block.dataflow_->edges_.insert(
1,116✔
1108
        {edge.first, std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
558✔
1109
                         this->element_counter_, debug_info, edge.first, block.dataflow(), src,
558✔
1110
                         src_conn, dst, dst_conn, subset))});
558✔
1111
    this->element_counter_++;
558✔
1112
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
558✔
UNCOV
1113
};
×
1114

1115
data_flow::LibraryNode& StructuredSDFGBuilder::add_library_node(
9✔
1116
    structured_control_flow::Block& block, const data_flow::LibraryNodeType& call,
1117
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& outputs,
1118
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
1119
    const bool has_side_effect, const DebugInfo& debug_info) {
1120
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
9✔
1121
    auto res = block.dataflow_->nodes_.insert(
18✔
1122
        {vertex, std::unique_ptr<data_flow::LibraryNode>(new data_flow::LibraryNode(
9✔
1123
                     this->element_counter_, debug_info, vertex, block.dataflow(), outputs, inputs,
9✔
1124
                     call, has_side_effect))});
9✔
1125
    this->element_counter_++;
9✔
1126
    return dynamic_cast<data_flow::LibraryNode&>(*(res.first->second));
9✔
UNCOV
1127
}
×
1128

UNCOV
1129
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block,
×
1130
                                          const data_flow::Memlet& edge) {
1131
    auto& graph = block.dataflow();
×
1132
    auto e = edge.edge();
×
1133
    boost::remove_edge(e, graph.graph_);
×
1134
    graph.edges_.erase(e);
×
UNCOV
1135
};
×
1136

UNCOV
1137
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block,
×
1138
                                        const data_flow::DataFlowNode& node) {
1139
    auto& graph = block.dataflow();
×
1140
    auto v = node.vertex();
×
1141
    boost::remove_vertex(v, graph.graph_);
×
1142
    graph.nodes_.erase(v);
×
UNCOV
1143
};
×
1144

1145
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
8✔
1146
                                       const data_flow::Tasklet& node) {
1147
    auto& graph = block.dataflow();
8✔
1148

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

1151
    // Delete incoming
1152
    std::list<const data_flow::Memlet*> iedges;
8✔
1153
    for (auto& iedge : graph.in_edges(node)) {
15✔
1154
        iedges.push_back(&iedge);
7✔
1155
    }
1156
    for (auto iedge : iedges) {
15✔
1157
        auto& src = iedge->src();
7✔
1158
        to_delete.insert(&src);
7✔
1159

1160
        auto edge = iedge->edge();
7✔
1161
        graph.edges_.erase(edge);
7✔
1162
        boost::remove_edge(edge, graph.graph_);
7✔
1163
    }
1164

1165
    // Delete outgoing
1166
    std::list<const data_flow::Memlet*> oedges;
8✔
1167
    for (auto& oedge : graph.out_edges(node)) {
16✔
1168
        oedges.push_back(&oedge);
8✔
1169
    }
1170
    for (auto oedge : oedges) {
16✔
1171
        auto& dst = oedge->dst();
8✔
1172
        to_delete.insert(&dst);
8✔
1173

1174
        auto edge = oedge->edge();
8✔
1175
        graph.edges_.erase(edge);
8✔
1176
        boost::remove_edge(edge, graph.graph_);
8✔
1177
    }
1178

1179
    // Delete nodes
1180
    for (auto obsolete_node : to_delete) {
31✔
1181
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
23✔
1182
            auto vertex = obsolete_node->vertex();
23✔
1183
            graph.nodes_.erase(vertex);
23✔
1184
            boost::remove_vertex(vertex, graph.graph_);
23✔
1185
        }
23✔
1186
    }
1187
};
8✔
1188

1189
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
1✔
1190
                                       const data_flow::AccessNode& node) {
1191
    auto& graph = block.dataflow();
1✔
1192
    if (graph.out_degree(node) != 0) {
1✔
UNCOV
1193
        throw InvalidSDFGException("StructuredSDFGBuilder: Access node has outgoing edges");
×
1194
    }
1195

1196
    std::list<const data_flow::Memlet*> tmp;
1✔
1197
    std::list<const data_flow::DataFlowNode*> queue = {&node};
1✔
1198
    while (!queue.empty()) {
3✔
1199
        auto current = queue.front();
2✔
1200
        queue.pop_front();
2✔
1201
        if (current != &node) {
2✔
1202
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
1✔
1203
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
UNCOV
1204
                    continue;
×
1205
                }
UNCOV
1206
            }
×
1207
        }
1✔
1208

1209
        tmp.clear();
2✔
1210
        for (auto& iedge : graph.in_edges(*current)) {
3✔
1211
            tmp.push_back(&iedge);
1✔
1212
        }
1213
        for (auto iedge : tmp) {
3✔
1214
            auto& src = iedge->src();
1✔
1215
            queue.push_back(&src);
1✔
1216

1217
            auto edge = iedge->edge();
1✔
1218
            graph.edges_.erase(edge);
1✔
1219
            boost::remove_edge(edge, graph.graph_);
1✔
1220
        }
1221

1222
        auto vertex = current->vertex();
2✔
1223
        graph.nodes_.erase(vertex);
2✔
1224
        boost::remove_vertex(vertex, graph.graph_);
2✔
1225
    }
1226
};
1✔
1227

UNCOV
1228
data_flow::AccessNode& StructuredSDFGBuilder::symbolic_expression_to_dataflow(
×
1229
    structured_control_flow::Block& parent, const symbolic::Expression& expr) {
UNCOV
1230
    auto& sdfg = this->subject();
×
1231

UNCOV
1232
    codegen::CPPLanguageExtension language_extension;
×
1233

1234
    // Base cases
1235
    if (SymEngine::is_a<SymEngine::Symbol>(*expr)) {
×
UNCOV
1236
        auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(expr);
×
1237

1238
        // Determine type
1239
        types::Scalar sym_type = types::Scalar(types::PrimitiveType::Void);
×
1240
        if (symbolic::is_nvptx(sym)) {
×
1241
            sym_type = types::Scalar(types::PrimitiveType::Int32);
×
1242
        } else {
×
UNCOV
1243
            sym_type = static_cast<const types::Scalar&>(sdfg.type(sym->get_name()));
×
1244
        }
1245

1246
        // Add new container for intermediate result
1247
        auto tmp = this->find_new_name();
×
UNCOV
1248
        this->add_container(tmp, sym_type);
×
1249

1250
        // Create dataflow graph
1251
        auto& input_node = this->add_access(parent, sym->get_name());
×
1252
        auto& output_node = this->add_access(parent, tmp);
×
1253
        auto& tasklet = this->add_tasklet(parent, data_flow::TaskletCode::assign,
×
1254
                                          {"_out", sym_type}, {{"_in", sym_type}});
×
1255
        this->add_memlet(parent, input_node, "void", tasklet, "_in", {});
×
UNCOV
1256
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1257

1258
        return output_node;
×
1259
    } else if (SymEngine::is_a<SymEngine::Integer>(*expr)) {
×
1260
        auto tmp = this->find_new_name();
×
UNCOV
1261
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Int64));
×
1262

1263
        auto& output_node = this->add_access(parent, tmp);
×
1264
        auto& tasklet = this->add_tasklet(
×
1265
            parent, data_flow::TaskletCode::assign,
×
1266
            {"_out", types::Scalar(types::PrimitiveType::Int64)},
×
1267
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Int64)}});
×
1268
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1269
        return output_node;
×
1270
    } else if (SymEngine::is_a<SymEngine::BooleanAtom>(*expr)) {
×
1271
        auto tmp = this->find_new_name();
×
UNCOV
1272
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1273

1274
        auto& output_node = this->add_access(parent, tmp);
×
1275
        auto& tasklet = this->add_tasklet(
×
1276
            parent, data_flow::TaskletCode::assign,
×
1277
            {"_out", types::Scalar(types::PrimitiveType::Bool)},
×
1278
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Bool)}});
×
1279
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1280
        return output_node;
×
1281
    } else if (SymEngine::is_a<SymEngine::Or>(*expr)) {
×
1282
        auto or_expr = SymEngine::rcp_static_cast<const SymEngine::Or>(expr);
×
1283
        if (or_expr->get_container().size() != 2) {
×
1284
            throw InvalidSDFGException(
×
UNCOV
1285
                "StructuredSDFGBuilder: Or expression must have exactly two arguments");
×
1286
        }
1287

1288
        std::vector<data_flow::AccessNode*> input_nodes;
×
1289
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1290
        for (auto& arg : or_expr->get_container()) {
×
1291
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1292
            input_nodes.push_back(&input_node);
×
1293
            input_types.push_back(
×
1294
                {"_in" + std::to_string(input_types.size() + 1),
×
UNCOV
1295
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1296
        }
1297

1298
        // Add new container for intermediate result
1299
        auto tmp = this->find_new_name();
×
UNCOV
1300
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1301

1302
        auto& output_node = this->add_access(parent, tmp);
×
1303
        auto& tasklet =
×
1304
            this->add_tasklet(parent, data_flow::TaskletCode::logical_or,
×
1305
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1306
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1307
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1308
                             {});
×
1309
        }
×
1310
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1311
        return output_node;
×
1312
    } else if (SymEngine::is_a<SymEngine::And>(*expr)) {
×
1313
        auto and_expr = SymEngine::rcp_static_cast<const SymEngine::And>(expr);
×
1314
        if (and_expr->get_container().size() != 2) {
×
1315
            throw InvalidSDFGException(
×
UNCOV
1316
                "StructuredSDFGBuilder: And expression must have exactly two arguments");
×
1317
        }
1318

1319
        std::vector<data_flow::AccessNode*> input_nodes;
×
1320
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1321
        for (auto& arg : and_expr->get_container()) {
×
1322
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1323
            input_nodes.push_back(&input_node);
×
1324
            input_types.push_back(
×
1325
                {"_in" + std::to_string(input_types.size() + 1),
×
UNCOV
1326
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1327
        }
1328

1329
        // Add new container for intermediate result
1330
        auto tmp = this->find_new_name();
×
UNCOV
1331
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1332

1333
        auto& output_node = this->add_access(parent, tmp);
×
1334
        auto& tasklet =
×
1335
            this->add_tasklet(parent, data_flow::TaskletCode::logical_and,
×
1336
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1337
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1338
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1339
                             {});
×
1340
        }
×
1341
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1342
        return output_node;
×
1343
    } else {
×
UNCOV
1344
        throw std::runtime_error("Unsupported expression type");
×
1345
    }
UNCOV
1346
};
×
1347

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

© 2026 Coveralls, Inc