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

daisytuner / sdfglib / 15340968114

30 May 2025 06:47AM UTC coverage: 58.553% (+0.2%) from 58.324%
15340968114

push

github

web-flow
Add parallel Map node

* Introduce Map data structure

* Prepare infrastructure

* implement analysis support

* Add basic infrastructure

* visualizer/serializer

* include fix

* update from main

* remove default

* happens before test + fixes

* builder test

* dispatcher test

* visitor, copy and serializer tests

* for2map structures

* for2map conversion draft

* add tests

* Bug fixes

* small updates from feedback

* Visitor style pass implementation

* cleanup

* fixes linting errors

---------

Co-authored-by: Lukas Truemper <lukas.truemper@outlook.de>

258 of 381 new or added lines in 26 files covered. (67.72%)

17 existing lines in 14 files now uncovered.

8184 of 13977 relevant lines covered (58.55%)

109.83 hits per line

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

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

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

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

12
namespace sdfg {
13
namespace builder {
14

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

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

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

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

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

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

56
    return false;
×
57
}
6✔
58

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

70
    return pdom;
3✔
71
}
3✔
72

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

410
    return new_block;
462✔
411
};
×
412

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

426
    return new_block;
28✔
427
};
×
428

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

700
Continue& StructuredSDFGBuilder::add_continue(Sequence& parent,
15✔
701
                                              const sdfg::symbolic::Assignments& assignments,
702
                                              const DebugInfo& debug_info) {
703
    parent.children_.push_back(
15✔
704
        std::unique_ptr<Continue>(new Continue(this->element_counter_, debug_info)));
15✔
705
    this->element_counter_++;
15✔
706
    parent.transitions_.push_back(std::unique_ptr<Transition>(
15✔
707
        new Transition(this->element_counter_, debug_info, assignments)));
15✔
708
    this->element_counter_++;
15✔
709
    return static_cast<Continue&>(*parent.children_.back().get());
15✔
710
};
×
711

712
Break& StructuredSDFGBuilder::add_break(Sequence& parent, const DebugInfo& debug_info) {
15✔
713
    return this->add_break(parent, symbolic::Assignments{}, debug_info);
15✔
714
};
×
715

716
Break& StructuredSDFGBuilder::add_break(Sequence& parent,
16✔
717
                                        const sdfg::symbolic::Assignments& assignments,
718
                                        const DebugInfo& debug_info) {
719
    parent.children_.push_back(
16✔
720
        std::unique_ptr<Break>(new Break(this->element_counter_, debug_info)));
16✔
721
    this->element_counter_++;
16✔
722
    parent.transitions_.push_back(std::unique_ptr<Transition>(
16✔
723
        new Transition(this->element_counter_, debug_info, assignments)));
16✔
724
    this->element_counter_++;
16✔
725
    return static_cast<Break&>(*parent.children_.back().get());
16✔
726
};
×
727

728
Return& StructuredSDFGBuilder::add_return(Sequence& parent,
14✔
729
                                          const sdfg::symbolic::Assignments& assignments,
730
                                          const DebugInfo& debug_info) {
731
    parent.children_.push_back(
14✔
732
        std::unique_ptr<Return>(new Return(this->element_counter_, debug_info)));
14✔
733
    this->element_counter_++;
14✔
734
    parent.transitions_.push_back(std::unique_ptr<Transition>(
14✔
735
        new Transition(this->element_counter_, debug_info, assignments)));
14✔
736
    this->element_counter_++;
14✔
737
    return static_cast<Return&>(*parent.children_.back().get());
14✔
738
};
×
739

740
Map& StructuredSDFGBuilder::add_map(Sequence& parent, const symbolic::Symbol& indvar,
9✔
741
                                    const symbolic::Expression& num_iterations,
742
                                    const sdfg::symbolic::Assignments& assignments,
743
                                    const DebugInfo& debug_info) {
744
    parent.children_.push_back(
18✔
745
        std::unique_ptr<Map>(new Map(this->element_counter_, debug_info, indvar, num_iterations)));
9✔
746
    this->element_counter_ = this->element_counter_ + 2;
9✔
747
    parent.transitions_.push_back(std::unique_ptr<Transition>(
9✔
748
        new Transition(this->element_counter_, debug_info, assignments)));
9✔
749
    this->element_counter_++;
9✔
750

751
    return static_cast<Map&>(*parent.children_.back().get());
9✔
NEW
752
};
×
753

UNCOV
754
For& StructuredSDFGBuilder::convert_while(Sequence& parent, While& loop,
×
755
                                          const symbolic::Symbol& indvar,
756
                                          const symbolic::Condition& condition,
757
                                          const symbolic::Expression& init,
758
                                          const symbolic::Expression& update) {
759
    // Insert for loop
760
    size_t index = 0;
×
761
    for (auto& entry : parent.children_) {
×
762
        if (entry.get() == &loop) {
×
763
            break;
×
764
        }
765
        index++;
×
766
    }
767
    auto iter = parent.children_.begin() + index;
×
768
    auto& new_iter = *parent.children_.insert(
×
769
        iter + 1, std::unique_ptr<For>(new For(this->element_counter_, loop.debug_info(), indvar,
×
770
                                               init, update, condition)));
×
771
    this->element_counter_ = this->element_counter_ + 2;
×
772
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
773
    this->insert_children(for_loop.root(), loop.root(), 0);
×
774

775
    // Remove while loop
776
    parent.children_.erase(parent.children_.begin() + index);
×
777

778
    return for_loop;
×
779
};
×
780

781
Map& StructuredSDFGBuilder::convert_for(Sequence& parent, For& loop,
4✔
782
                                        const symbolic::Expression& num_iterations) {
783
    // Insert for loop
784
    size_t index = 0;
4✔
785
    for (auto& entry : parent.children_) {
4✔
786
        if (entry.get() == &loop) {
4✔
787
            break;
4✔
788
        }
NEW
789
        index++;
×
790
    }
791
    auto iter = parent.children_.begin() + index;
4✔
792
    auto& new_iter = *parent.children_.insert(
8✔
793
        iter + 1, std::unique_ptr<Map>(new Map(this->element_counter_, loop.debug_info(),
8✔
794
                                               loop.indvar(), num_iterations)));
4✔
795
    this->element_counter_ = this->element_counter_ + 2;
4✔
796
    auto& map = dynamic_cast<Map&>(*new_iter);
4✔
797
    this->insert_children(map.root(), loop.root(), 0);
4✔
798

799
    // Remove for loop
800
    parent.children_.erase(parent.children_.begin() + index);
4✔
801

802
    return map;
4✔
NEW
803
};
×
804

805
void StructuredSDFGBuilder::clear_sequence(Sequence& parent) {
2✔
806
    parent.children_.clear();
2✔
807
    parent.transitions_.clear();
2✔
808
};
2✔
809

810
Sequence& StructuredSDFGBuilder::parent(const ControlFlowNode& node) {
6✔
811
    std::list<structured_control_flow::ControlFlowNode*> queue = {&this->structured_sdfg_->root()};
6✔
812
    while (!queue.empty()) {
6✔
813
        auto current = queue.front();
6✔
814
        queue.pop_front();
6✔
815

816
        if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
6✔
817
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
6✔
818
                if (&sequence_stmt->at(i).first == &node) {
6✔
819
                    return *sequence_stmt;
6✔
820
                }
821
                queue.push_back(&sequence_stmt->at(i).first);
×
822
            }
×
823
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
×
824
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
825
                queue.push_back(&if_else_stmt->at(i).first);
×
826
            }
×
827
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
×
828
            queue.push_back(&while_stmt->root());
×
829
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
×
830
            queue.push_back(&for_stmt->root());
×
831
        } else if (auto kern_stmt = dynamic_cast<const structured_control_flow::Kernel*>(current)) {
×
832
            queue.push_back(&kern_stmt->root());
×
833
        }
×
834
    }
835

836
    return this->structured_sdfg_->root();
×
837
};
6✔
838

839
Kernel& StructuredSDFGBuilder::convert_into_kernel() {
5✔
840
    auto old_root = std::move(this->structured_sdfg_->root_);
5✔
841
    this->structured_sdfg_->root_ =
5✔
842
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, old_root->debug_info()));
5✔
843
    this->element_counter_++;
5✔
844
    auto& new_root = this->structured_sdfg_->root();
5✔
845
    auto& kernel = this->add_kernel(new_root, this->function().name());
5✔
846

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

849
    types::Scalar gridDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
850
    add_container(kernel.gridDim_x()->get_name(), gridDim_x);
5✔
851
    types::Scalar gridDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
852
    add_container(kernel.gridDim_y()->get_name(), gridDim_y);
5✔
853
    types::Scalar gridDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
854
    add_container(kernel.gridDim_z()->get_name(), gridDim_z);
5✔
855
    types::Scalar blockDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
856
    add_container(kernel.blockDim_x()->get_name(), blockDim_x);
5✔
857
    types::Scalar blockDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
858
    add_container(kernel.blockDim_y()->get_name(), blockDim_y);
5✔
859
    types::Scalar blockDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
860
    add_container(kernel.blockDim_z()->get_name(), blockDim_z);
5✔
861
    types::Scalar blockIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
862
    add_container(kernel.blockIdx_x()->get_name(), blockIdx_x);
5✔
863
    types::Scalar blockIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
864
    add_container(kernel.blockIdx_y()->get_name(), blockIdx_y);
5✔
865
    types::Scalar blockIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
866
    add_container(kernel.blockIdx_z()->get_name(), blockIdx_z);
5✔
867
    types::Scalar threadIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
868
    add_container(kernel.threadIdx_x()->get_name(), threadIdx_x);
5✔
869
    types::Scalar threadIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
870
    add_container(kernel.threadIdx_y()->get_name(), threadIdx_y);
5✔
871
    types::Scalar threadIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
872
    add_container(kernel.threadIdx_z()->get_name(), threadIdx_z);
5✔
873

874
    kernel.root().replace(symbolic::symbol("gridDim.x"), kernel.gridDim_x());
5✔
875
    kernel.root().replace(symbolic::symbol("gridDim.y"), kernel.gridDim_y());
5✔
876
    kernel.root().replace(symbolic::symbol("gridDim.z"), kernel.gridDim_z());
5✔
877

878
    kernel.root().replace(symbolic::symbol("blockDim.x"), kernel.blockDim_x());
5✔
879
    kernel.root().replace(symbolic::symbol("blockDim.y"), kernel.blockDim_y());
5✔
880
    kernel.root().replace(symbolic::symbol("blockDim.z"), kernel.blockDim_z());
5✔
881

882
    kernel.root().replace(symbolic::symbol("blockIdx.x"), kernel.blockIdx_x());
5✔
883
    kernel.root().replace(symbolic::symbol("blockIdx.y"), kernel.blockIdx_y());
5✔
884
    kernel.root().replace(symbolic::symbol("blockIdx.z"), kernel.blockIdx_z());
5✔
885

886
    kernel.root().replace(symbolic::symbol("threadIdx.x"), kernel.threadIdx_x());
5✔
887
    kernel.root().replace(symbolic::symbol("threadIdx.y"), kernel.threadIdx_y());
5✔
888
    kernel.root().replace(symbolic::symbol("threadIdx.z"), kernel.threadIdx_z());
5✔
889

890
    return kernel;
5✔
891
};
5✔
892

893
/***** Section: Dataflow Graph *****/
894

895
data_flow::AccessNode& StructuredSDFGBuilder::add_access(structured_control_flow::Block& block,
547✔
896
                                                         const std::string& data,
897
                                                         const DebugInfo& debug_info) {
898
    // Check: Data exists
899
    if (!this->subject().exists(data)) {
547✔
900
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
901
    }
902

903
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
547✔
904
    auto res = block.dataflow_->nodes_.insert(
1,094✔
905
        {vertex, std::unique_ptr<data_flow::AccessNode>(new data_flow::AccessNode(
547✔
906
                     this->element_counter_, debug_info, vertex, block.dataflow(), data))});
547✔
907
    this->element_counter_++;
547✔
908
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
547✔
909
};
×
910

911
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
350✔
912
    structured_control_flow::Block& block, const data_flow::TaskletCode code,
913
    const std::pair<std::string, sdfg::types::Scalar>& output,
914
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
915
    const DebugInfo& debug_info) {
916
    // Check: Duplicate inputs
917
    std::unordered_set<std::string> input_names;
350✔
918
    for (auto& input : inputs) {
804✔
919
        if (input_names.find(input.first) != input_names.end()) {
454✔
920
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
921
        }
922
        input_names.insert(input.first);
454✔
923
    }
924

925
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
350✔
926
    auto res = block.dataflow_->nodes_.insert(
700✔
927
        {vertex, std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
700✔
928
                     this->element_counter_, debug_info, vertex, block.dataflow(), code, output,
350✔
929
                     inputs, symbolic::__true__()))});
350✔
930
    this->element_counter_++;
350✔
931
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
350✔
932
};
350✔
933

934
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
558✔
935
    structured_control_flow::Block& block, data_flow::DataFlowNode& src,
936
    const std::string& src_conn, data_flow::DataFlowNode& dst, const std::string& dst_conn,
937
    const data_flow::Subset& subset, const DebugInfo& debug_info) {
938
    auto& function_ = this->function();
558✔
939

940
    // Check - Case 1: Access Node -> Access Node
941
    // - src_conn or dst_conn must be refs. The other must be void.
942
    // - The side of the memlet that is void, is dereferenced.
943
    // - The dst type must always be a pointer after potential dereferencing.
944
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
945
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
558✔
946
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
3✔
947
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
3✔
948
        if (src_conn == "refs") {
3✔
949
            if (dst_conn != "void") {
1✔
950
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
951
            }
952

953
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
1✔
954
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
1✔
955
                throw InvalidSDFGException("dst type must be a pointer");
×
956
            }
957

958
            auto& src_type = function_.type(src_node.data());
1✔
959
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
1✔
960
                throw InvalidSDFGException("src type must be a pointer");
×
961
            }
962
        } else if (src_conn == "void") {
3✔
963
            if (dst_conn != "refs") {
2✔
964
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
965
            }
966

967
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
2✔
968
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
969
            }
970

971
            // Trivially correct but checks inference
972
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
2✔
973
            types::Pointer ref_type(src_type);
2✔
974
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
975
                throw InvalidSDFGException("src type must be a pointer");
976
            }
977

978
            auto& dst_type = function_.type(dst_node.data());
2✔
979
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
2✔
980
                throw InvalidSDFGException("dst type must be a pointer");
×
981
            }
982
        } else {
2✔
983
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
984
        }
985
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
763✔
986
               dynamic_cast<data_flow::Tasklet*>(&dst)) {
205✔
987
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
205✔
988
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
205✔
989
        if (src_conn != "void") {
205✔
990
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
991
        }
992
        bool found = false;
205✔
993
        for (auto& input : dst_node.inputs()) {
259✔
994
            if (input.first == dst_conn) {
259✔
995
                found = true;
205✔
996
                break;
205✔
997
            }
998
        }
999
        if (!found) {
205✔
1000
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
1001
        }
1002
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
205✔
1003
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
205✔
1004
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
1005
        }
1006
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) &&
905✔
1007
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
350✔
1008
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
350✔
1009
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
350✔
1010
        if (src_conn != src_node.outputs()[0].first) {
350✔
1011
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
1012
        }
1013
        if (dst_conn != "void") {
350✔
1014
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1015
        }
1016

1017
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
350✔
1018
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
350✔
1019
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
1020
        }
1021
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
350✔
1022
               dynamic_cast<data_flow::LibraryNode*>(&dst)) {
×
1023
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
×
1024
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
1025
        if (src_conn != "void") {
×
1026
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
1027
        }
1028
        bool found = false;
×
1029
        for (auto& input : dst_node.inputs()) {
×
1030
            if (input.first == dst_conn) {
×
1031
                found = true;
×
1032
                break;
×
1033
            }
1034
        }
1035
        if (!found) {
×
1036
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
1037
        }
1038

1039
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
×
1040
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
×
1041
            throw InvalidSDFGException("Library node inputs must be scalars");
×
1042
        }
1043
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) &&
×
1044
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
1045
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
1046
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
×
1047
        if (dst_conn != "void") {
×
1048
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1049
        }
1050
        bool found = false;
×
1051
        for (auto& output : src_node.outputs()) {
×
1052
            if (output.first == src_conn) {
×
1053
                found = true;
×
1054
                break;
×
1055
            }
1056
        }
1057
        if (!found) {
×
1058
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
1059
        }
1060

1061
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
1062
        if (!dynamic_cast<const types::Pointer*>(&element_type)) {
×
1063
            throw InvalidSDFGException("Access node must be a pointer");
×
1064
        }
1065
    } else {
×
1066
        throw InvalidSDFGException("Invalid src or dst node type");
×
1067
    }
1068

1069
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
558✔
1070
    auto res = block.dataflow_->edges_.insert(
1,116✔
1071
        {edge.first, std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
558✔
1072
                         this->element_counter_, debug_info, edge.first, block.dataflow(), src,
558✔
1073
                         src_conn, dst, dst_conn, subset))});
558✔
1074
    this->element_counter_++;
558✔
1075
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
558✔
1076
};
×
1077

1078
data_flow::LibraryNode& StructuredSDFGBuilder::add_library_node(
9✔
1079
    structured_control_flow::Block& block, const data_flow::LibraryNodeType& call,
1080
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& outputs,
1081
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
1082
    const bool has_side_effect, const DebugInfo& debug_info) {
1083
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
9✔
1084
    auto res = block.dataflow_->nodes_.insert(
18✔
1085
        {vertex, std::unique_ptr<data_flow::LibraryNode>(new data_flow::LibraryNode(
9✔
1086
                     this->element_counter_, debug_info, vertex, block.dataflow(), outputs, inputs,
9✔
1087
                     call, has_side_effect))});
9✔
1088
    this->element_counter_++;
9✔
1089
    return dynamic_cast<data_flow::LibraryNode&>(*(res.first->second));
9✔
1090
}
×
1091

1092
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block,
×
1093
                                          const data_flow::Memlet& edge) {
1094
    auto& graph = block.dataflow();
×
1095
    auto e = edge.edge();
×
1096
    boost::remove_edge(e, graph.graph_);
×
1097
    graph.edges_.erase(e);
×
1098
};
×
1099

1100
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block,
×
1101
                                        const data_flow::DataFlowNode& node) {
1102
    auto& graph = block.dataflow();
×
1103
    auto v = node.vertex();
×
1104
    boost::remove_vertex(v, graph.graph_);
×
1105
    graph.nodes_.erase(v);
×
1106
};
×
1107

1108
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
8✔
1109
                                       const data_flow::Tasklet& node) {
1110
    auto& graph = block.dataflow();
8✔
1111

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

1114
    // Delete incoming
1115
    std::list<const data_flow::Memlet*> iedges;
8✔
1116
    for (auto& iedge : graph.in_edges(node)) {
15✔
1117
        iedges.push_back(&iedge);
7✔
1118
    }
1119
    for (auto iedge : iedges) {
15✔
1120
        auto& src = iedge->src();
7✔
1121
        to_delete.insert(&src);
7✔
1122

1123
        auto edge = iedge->edge();
7✔
1124
        graph.edges_.erase(edge);
7✔
1125
        boost::remove_edge(edge, graph.graph_);
7✔
1126
    }
1127

1128
    // Delete outgoing
1129
    std::list<const data_flow::Memlet*> oedges;
8✔
1130
    for (auto& oedge : graph.out_edges(node)) {
16✔
1131
        oedges.push_back(&oedge);
8✔
1132
    }
1133
    for (auto oedge : oedges) {
16✔
1134
        auto& dst = oedge->dst();
8✔
1135
        to_delete.insert(&dst);
8✔
1136

1137
        auto edge = oedge->edge();
8✔
1138
        graph.edges_.erase(edge);
8✔
1139
        boost::remove_edge(edge, graph.graph_);
8✔
1140
    }
1141

1142
    // Delete nodes
1143
    for (auto obsolete_node : to_delete) {
31✔
1144
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
23✔
1145
            auto vertex = obsolete_node->vertex();
23✔
1146
            graph.nodes_.erase(vertex);
23✔
1147
            boost::remove_vertex(vertex, graph.graph_);
23✔
1148
        }
23✔
1149
    }
1150
};
8✔
1151

1152
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
1✔
1153
                                       const data_flow::AccessNode& node) {
1154
    auto& graph = block.dataflow();
1✔
1155
    if (graph.out_degree(node) != 0) {
1✔
1156
        throw InvalidSDFGException("StructuredSDFGBuilder: Access node has outgoing edges");
×
1157
    }
1158

1159
    std::list<const data_flow::Memlet*> tmp;
1✔
1160
    std::list<const data_flow::DataFlowNode*> queue = {&node};
1✔
1161
    while (!queue.empty()) {
3✔
1162
        auto current = queue.front();
2✔
1163
        queue.pop_front();
2✔
1164
        if (current != &node) {
2✔
1165
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
1✔
1166
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
1167
                    continue;
×
1168
                }
1169
            }
×
1170
        }
1✔
1171

1172
        tmp.clear();
2✔
1173
        for (auto& iedge : graph.in_edges(*current)) {
3✔
1174
            tmp.push_back(&iedge);
1✔
1175
        }
1176
        for (auto iedge : tmp) {
3✔
1177
            auto& src = iedge->src();
1✔
1178
            queue.push_back(&src);
1✔
1179

1180
            auto edge = iedge->edge();
1✔
1181
            graph.edges_.erase(edge);
1✔
1182
            boost::remove_edge(edge, graph.graph_);
1✔
1183
        }
1184

1185
        auto vertex = current->vertex();
2✔
1186
        graph.nodes_.erase(vertex);
2✔
1187
        boost::remove_vertex(vertex, graph.graph_);
2✔
1188
    }
1189
};
1✔
1190

1191
data_flow::AccessNode& StructuredSDFGBuilder::symbolic_expression_to_dataflow(
×
1192
    structured_control_flow::Block& parent, const symbolic::Expression& expr) {
1193
    auto& sdfg = this->subject();
×
1194

1195
    codegen::CPPLanguageExtension language_extension;
×
1196

1197
    // Base cases
1198
    if (SymEngine::is_a<SymEngine::Symbol>(*expr)) {
×
1199
        auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(expr);
×
1200

1201
        // Determine type
1202
        types::Scalar sym_type = types::Scalar(types::PrimitiveType::Void);
×
1203
        if (symbolic::is_nvptx(sym)) {
×
1204
            sym_type = types::Scalar(types::PrimitiveType::Int32);
×
1205
        } else {
×
1206
            sym_type = static_cast<const types::Scalar&>(sdfg.type(sym->get_name()));
×
1207
        }
1208

1209
        // Add new container for intermediate result
1210
        auto tmp = this->find_new_name();
×
1211
        this->add_container(tmp, sym_type);
×
1212

1213
        // Create dataflow graph
1214
        auto& input_node = this->add_access(parent, sym->get_name());
×
1215
        auto& output_node = this->add_access(parent, tmp);
×
1216
        auto& tasklet = this->add_tasklet(parent, data_flow::TaskletCode::assign,
×
1217
                                          {"_out", sym_type}, {{"_in", sym_type}});
×
1218
        this->add_memlet(parent, input_node, "void", tasklet, "_in", {});
×
1219
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1220

1221
        return output_node;
×
1222
    } else if (SymEngine::is_a<SymEngine::Integer>(*expr)) {
×
1223
        auto tmp = this->find_new_name();
×
1224
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Int64));
×
1225

1226
        auto& output_node = this->add_access(parent, tmp);
×
1227
        auto& tasklet = this->add_tasklet(
×
1228
            parent, data_flow::TaskletCode::assign,
×
1229
            {"_out", types::Scalar(types::PrimitiveType::Int64)},
×
1230
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Int64)}});
×
1231
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1232
        return output_node;
×
1233
    } else if (SymEngine::is_a<SymEngine::BooleanAtom>(*expr)) {
×
1234
        auto tmp = this->find_new_name();
×
1235
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1236

1237
        auto& output_node = this->add_access(parent, tmp);
×
1238
        auto& tasklet = this->add_tasklet(
×
1239
            parent, data_flow::TaskletCode::assign,
×
1240
            {"_out", types::Scalar(types::PrimitiveType::Bool)},
×
1241
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Bool)}});
×
1242
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1243
        return output_node;
×
1244
    } else if (SymEngine::is_a<SymEngine::Or>(*expr)) {
×
1245
        auto or_expr = SymEngine::rcp_static_cast<const SymEngine::Or>(expr);
×
1246
        if (or_expr->get_container().size() != 2) {
×
1247
            throw InvalidSDFGException(
×
1248
                "StructuredSDFGBuilder: Or expression must have exactly two arguments");
×
1249
        }
1250

1251
        std::vector<data_flow::AccessNode*> input_nodes;
×
1252
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1253
        for (auto& arg : or_expr->get_container()) {
×
1254
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1255
            input_nodes.push_back(&input_node);
×
1256
            input_types.push_back(
×
1257
                {"_in" + std::to_string(input_types.size() + 1),
×
1258
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1259
        }
1260

1261
        // Add new container for intermediate result
1262
        auto tmp = this->find_new_name();
×
1263
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1264

1265
        auto& output_node = this->add_access(parent, tmp);
×
1266
        auto& tasklet =
×
1267
            this->add_tasklet(parent, data_flow::TaskletCode::logical_or,
×
1268
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1269
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1270
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1271
                             {});
×
1272
        }
×
1273
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1274
        return output_node;
×
1275
    } else if (SymEngine::is_a<SymEngine::And>(*expr)) {
×
1276
        auto and_expr = SymEngine::rcp_static_cast<const SymEngine::And>(expr);
×
1277
        if (and_expr->get_container().size() != 2) {
×
1278
            throw InvalidSDFGException(
×
1279
                "StructuredSDFGBuilder: And expression must have exactly two arguments");
×
1280
        }
1281

1282
        std::vector<data_flow::AccessNode*> input_nodes;
×
1283
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1284
        for (auto& arg : and_expr->get_container()) {
×
1285
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1286
            input_nodes.push_back(&input_node);
×
1287
            input_types.push_back(
×
1288
                {"_in" + std::to_string(input_types.size() + 1),
×
1289
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1290
        }
1291

1292
        // Add new container for intermediate result
1293
        auto tmp = this->find_new_name();
×
1294
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1295

1296
        auto& output_node = this->add_access(parent, tmp);
×
1297
        auto& tasklet =
×
1298
            this->add_tasklet(parent, data_flow::TaskletCode::logical_and,
×
1299
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1300
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1301
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
1302
                             {});
×
1303
        }
×
1304
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1305
        return output_node;
×
1306
    } else {
×
1307
        throw std::runtime_error("Unsupported expression type");
×
1308
    }
1309
};
×
1310

1311
}  // namespace builder
1312
}  // 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