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

daisytuner / sdfglib / 15262928007

26 May 2025 10:36PM UTC coverage: 58.284% (-2.0%) from 60.304%
15262928007

push

github

web-flow
Merge pull request #36 from daisytuner/sdfg-validation

Introduces new definition of memlets plus sanity checks

104 of 266 new or added lines in 6 files covered. (39.1%)

241 existing lines in 15 files now uncovered.

7908 of 13568 relevant lines covered (58.28%)

96.1 hits per line

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

68.19
/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/types/utils.h"
6

7
using namespace sdfg::control_flow;
8
using namespace sdfg::structured_control_flow;
9

10
namespace sdfg {
11
namespace builder {
12

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

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

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

36
    return nodes;
1✔
37
};
1✔
38

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

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

54
    return false;
×
55
}
6✔
56

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

68
    return pdom;
3✔
69
}
3✔
70

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

76
    auto pdom_tree = sdfg.post_dominator_tree();
4✔
77

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

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

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

99
    auto in_edges = sdfg.in_edges(*current);
8✔
100

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

409
    return new_block;
453✔
410
};
×
411

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

425
    return new_block;
28✔
426
};
×
427

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

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

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

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

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

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

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

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

506
    return {new_block, new_entry.second};
2✔
507
};
×
508

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

739
For& StructuredSDFGBuilder::convert_while(Sequence& parent, While& loop,
×
740
                                          const symbolic::Symbol& indvar,
741
                                          const symbolic::Condition& condition,
742
                                          const symbolic::Expression& init,
743
                                          const symbolic::Expression& update) {
744
    // Insert for loop
745
    size_t index = 0;
×
746
    for (auto& entry : parent.children_) {
×
747
        if (entry.get() == &loop) {
×
748
            break;
×
749
        }
750
        index++;
×
751
    }
752
    auto iter = parent.children_.begin() + index;
×
753
    auto& new_iter = *parent.children_.insert(
×
754
        iter + 1, std::unique_ptr<For>(new For(this->element_counter_, loop.debug_info(), indvar,
×
755
                                               init, update, condition)));
×
756
    this->element_counter_ = this->element_counter_ + 2;
×
757
    auto& for_loop = dynamic_cast<For&>(*new_iter);
×
758
    this->insert_children(for_loop.root(), loop.root(), 0);
×
759

760
    // Remove while loop
761
    parent.children_.erase(parent.children_.begin() + index);
×
762

763
    return for_loop;
×
764
};
×
765

766
void StructuredSDFGBuilder::clear_sequence(Sequence& parent) {
2✔
767
    parent.children_.clear();
2✔
768
    parent.transitions_.clear();
2✔
769
};
2✔
770

771
Sequence& StructuredSDFGBuilder::parent(const ControlFlowNode& node) {
2✔
772
    std::list<structured_control_flow::ControlFlowNode*> queue = {&this->structured_sdfg_->root()};
2✔
773
    while (!queue.empty()) {
2✔
774
        auto current = queue.front();
2✔
775
        queue.pop_front();
2✔
776

777
        if (auto sequence_stmt = dynamic_cast<structured_control_flow::Sequence*>(current)) {
2✔
778
            for (size_t i = 0; i < sequence_stmt->size(); i++) {
2✔
779
                if (&sequence_stmt->at(i).first == &node) {
2✔
780
                    return *sequence_stmt;
2✔
781
                }
782
                queue.push_back(&sequence_stmt->at(i).first);
×
783
            }
×
784
        } else if (auto if_else_stmt = dynamic_cast<structured_control_flow::IfElse*>(current)) {
×
785
            for (size_t i = 0; i < if_else_stmt->size(); i++) {
×
786
                queue.push_back(&if_else_stmt->at(i).first);
×
787
            }
×
788
        } else if (auto while_stmt = dynamic_cast<structured_control_flow::While*>(current)) {
×
789
            queue.push_back(&while_stmt->root());
×
790
        } else if (auto for_stmt = dynamic_cast<structured_control_flow::For*>(current)) {
×
791
            queue.push_back(&for_stmt->root());
×
792
        } else if (auto kern_stmt = dynamic_cast<const structured_control_flow::Kernel*>(current)) {
×
793
            queue.push_back(&kern_stmt->root());
×
794
        }
×
795
    }
796

797
    return this->structured_sdfg_->root();
×
798
};
2✔
799

800
Kernel& StructuredSDFGBuilder::convert_into_kernel() {
5✔
801
    auto old_root = std::move(this->structured_sdfg_->root_);
5✔
802
    this->structured_sdfg_->root_ =
5✔
803
        std::unique_ptr<Sequence>(new Sequence(this->element_counter_, old_root->debug_info()));
5✔
804
    this->element_counter_++;
5✔
805
    auto& new_root = this->structured_sdfg_->root();
5✔
806
    auto& kernel = this->add_kernel(new_root, this->function().name());
5✔
807

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

810
    types::Scalar gridDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
811
    add_container(kernel.gridDim_x()->get_name(), gridDim_x);
5✔
812
    types::Scalar gridDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
813
    add_container(kernel.gridDim_y()->get_name(), gridDim_y);
5✔
814
    types::Scalar gridDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
815
    add_container(kernel.gridDim_z()->get_name(), gridDim_z);
5✔
816
    types::Scalar blockDim_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
817
    add_container(kernel.blockDim_x()->get_name(), blockDim_x);
5✔
818
    types::Scalar blockDim_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
819
    add_container(kernel.blockDim_y()->get_name(), blockDim_y);
5✔
820
    types::Scalar blockDim_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
821
    add_container(kernel.blockDim_z()->get_name(), blockDim_z);
5✔
822
    types::Scalar blockIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
823
    add_container(kernel.blockIdx_x()->get_name(), blockIdx_x);
5✔
824
    types::Scalar blockIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
825
    add_container(kernel.blockIdx_y()->get_name(), blockIdx_y);
5✔
826
    types::Scalar blockIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
827
    add_container(kernel.blockIdx_z()->get_name(), blockIdx_z);
5✔
828
    types::Scalar threadIdx_x(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
829
    add_container(kernel.threadIdx_x()->get_name(), threadIdx_x);
5✔
830
    types::Scalar threadIdx_y(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
831
    add_container(kernel.threadIdx_y()->get_name(), threadIdx_y);
5✔
832
    types::Scalar threadIdx_z(types::PrimitiveType::Int32, types::DeviceLocation::nvptx, 0);
5✔
833
    add_container(kernel.threadIdx_z()->get_name(), threadIdx_z);
5✔
834

835
    kernel.root().replace(symbolic::symbol("gridDim.x"), kernel.gridDim_x());
5✔
836
    kernel.root().replace(symbolic::symbol("gridDim.y"), kernel.gridDim_y());
5✔
837
    kernel.root().replace(symbolic::symbol("gridDim.z"), kernel.gridDim_z());
5✔
838

839
    kernel.root().replace(symbolic::symbol("blockDim.x"), kernel.blockDim_x());
5✔
840
    kernel.root().replace(symbolic::symbol("blockDim.y"), kernel.blockDim_y());
5✔
841
    kernel.root().replace(symbolic::symbol("blockDim.z"), kernel.blockDim_z());
5✔
842

843
    kernel.root().replace(symbolic::symbol("blockIdx.x"), kernel.blockIdx_x());
5✔
844
    kernel.root().replace(symbolic::symbol("blockIdx.y"), kernel.blockIdx_y());
5✔
845
    kernel.root().replace(symbolic::symbol("blockIdx.z"), kernel.blockIdx_z());
5✔
846

847
    kernel.root().replace(symbolic::symbol("threadIdx.x"), kernel.threadIdx_x());
5✔
848
    kernel.root().replace(symbolic::symbol("threadIdx.y"), kernel.threadIdx_y());
5✔
849
    kernel.root().replace(symbolic::symbol("threadIdx.z"), kernel.threadIdx_z());
5✔
850

851
    return kernel;
5✔
852
};
5✔
853

854
/***** Section: Dataflow Graph *****/
855

856
data_flow::AccessNode& StructuredSDFGBuilder::add_access(structured_control_flow::Block& block,
533✔
857
                                                         const std::string& data,
858
                                                         const DebugInfo& debug_info) {
859
    // Check: Data exists
860
    if (!this->subject().exists(data)) {
533✔
NEW
861
        throw InvalidSDFGException("Data does not exist in SDFG: " + data);
×
862
    }
863

864
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
533✔
865
    auto res = block.dataflow_->nodes_.insert(
1,066✔
866
        {vertex, std::unique_ptr<data_flow::AccessNode>(new data_flow::AccessNode(
533✔
867
                     this->element_counter_, debug_info, vertex, block.dataflow(), data))});
533✔
868
    this->element_counter_++;
533✔
869
    return dynamic_cast<data_flow::AccessNode&>(*(res.first->second));
533✔
870
};
×
871

872
data_flow::Tasklet& StructuredSDFGBuilder::add_tasklet(
343✔
873
    structured_control_flow::Block& block, const data_flow::TaskletCode code,
874
    const std::pair<std::string, sdfg::types::Scalar>& output,
875
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
876
    const DebugInfo& debug_info) {
877
    // Check: Duplicate inputs
878
    std::unordered_set<std::string> input_names;
343✔
879
    for (auto& input : inputs) {
790✔
880
        if (input_names.find(input.first) != input_names.end()) {
447✔
NEW
881
            throw InvalidSDFGException("Input " + input.first + " already exists in SDFG");
×
882
        }
883
        input_names.insert(input.first);
447✔
884
    }
885

886
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
343✔
887
    auto res = block.dataflow_->nodes_.insert(
686✔
888
        {vertex, std::unique_ptr<data_flow::Tasklet>(new data_flow::Tasklet(
686✔
889
                     this->element_counter_, debug_info, vertex, block.dataflow(), code, output,
343✔
890
                     inputs, symbolic::__true__()))});
343✔
891
    this->element_counter_++;
343✔
892
    return dynamic_cast<data_flow::Tasklet&>(*(res.first->second));
343✔
893
};
343✔
894

895
data_flow::Memlet& StructuredSDFGBuilder::add_memlet(
544✔
896
    structured_control_flow::Block& block, data_flow::DataFlowNode& src,
897
    const std::string& src_conn, data_flow::DataFlowNode& dst, const std::string& dst_conn,
898
    const data_flow::Subset& subset, const DebugInfo& debug_info) {
899
    auto& function_ = this->function();
544✔
900

901
    // Check - Case 1: Access Node -> Access Node
902
    // - src_conn or dst_conn must be refs. The other must be void.
903
    // - The side of the memlet that is void, is dereferenced.
904
    // - The dst type must always be a pointer after potential dereferencing.
905
    // - The src type can be any type after dereferecing (&dereferenced_src_type).
906
    if (dynamic_cast<data_flow::AccessNode*>(&src) && dynamic_cast<data_flow::AccessNode*>(&dst)) {
544✔
907
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
3✔
908
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
3✔
909
        if (src_conn == "refs") {
3✔
910
            if (dst_conn != "void") {
1✔
NEW
911
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
912
            }
913

914
            auto& dst_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
1✔
915
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
1✔
NEW
916
                throw InvalidSDFGException("dst type must be a pointer");
×
917
            }
918

919
            auto& src_type = function_.type(src_node.data());
1✔
920
            if (!dynamic_cast<const types::Pointer*>(&src_type)) {
1✔
NEW
921
                throw InvalidSDFGException("src type must be a pointer");
×
922
            }
923
        } else if (src_conn == "void") {
3✔
924
            if (dst_conn != "refs") {
2✔
NEW
925
                throw InvalidSDFGException("Invalid dst connector: " + dst_conn);
×
926
            }
927

928
            if (symbolic::is_pointer(symbolic::symbol(src_node.data()))) {
2✔
NEW
929
                throw InvalidSDFGException("src_conn is void: src cannot be a raw pointer");
×
930
            }
931

932
            // Trivially correct but checks inference
933
            auto& src_type = types::infer_type(function_, function_.type(src_node.data()), subset);
2✔
934
            types::Pointer ref_type(src_type);
2✔
935
            if (!dynamic_cast<const types::Pointer*>(&ref_type)) {
936
                throw InvalidSDFGException("src type must be a pointer");
937
            }
938

939
            auto& dst_type = function_.type(dst_node.data());
2✔
940
            if (!dynamic_cast<const types::Pointer*>(&dst_type)) {
2✔
NEW
941
                throw InvalidSDFGException("dst type must be a pointer");
×
942
            }
943
        } else {
2✔
NEW
944
            throw InvalidSDFGException("Invalid src connector: " + src_conn);
×
945
        }
946
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
742✔
947
               dynamic_cast<data_flow::Tasklet*>(&dst)) {
198✔
948
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
198✔
949
        auto& dst_node = dynamic_cast<data_flow::Tasklet&>(dst);
198✔
950
        if (src_conn != "void") {
198✔
NEW
951
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
952
        }
953
        bool found = false;
198✔
954
        for (auto& input : dst_node.inputs()) {
252✔
955
            if (input.first == dst_conn) {
252✔
956
                found = true;
198✔
957
                break;
198✔
958
            }
959
        }
960
        if (!found) {
198✔
NEW
961
            throw InvalidSDFGException("dst_conn not found in tasklet: " + dst_conn);
×
962
        }
963
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
198✔
964
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
198✔
NEW
965
            throw InvalidSDFGException("Tasklets inputs must be scalars");
×
966
        }
967
    } else if (dynamic_cast<data_flow::Tasklet*>(&src) &&
884✔
968
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
343✔
969
        auto& src_node = dynamic_cast<data_flow::Tasklet&>(src);
343✔
970
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
343✔
971
        if (src_conn != src_node.outputs()[0].first) {
343✔
NEW
972
            throw InvalidSDFGException("src_conn must match tasklet output name");
×
973
        }
974
        if (dst_conn != "void") {
343✔
NEW
975
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
976
        }
977

978
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
343✔
979
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
343✔
NEW
980
            throw InvalidSDFGException("Tasklet output must be a scalar");
×
981
        }
982
    } else if (dynamic_cast<data_flow::AccessNode*>(&src) &&
343✔
NEW
983
               dynamic_cast<data_flow::LibraryNode*>(&dst)) {
×
NEW
984
        auto& src_node = dynamic_cast<data_flow::AccessNode&>(src);
×
NEW
985
        auto& dst_node = dynamic_cast<data_flow::LibraryNode&>(dst);
×
NEW
986
        if (src_conn != "void") {
×
NEW
987
            throw InvalidSDFGException("src_conn must be void. Found: " + src_conn);
×
988
        }
NEW
989
        bool found = false;
×
NEW
990
        for (auto& input : dst_node.inputs()) {
×
NEW
991
            if (input.first == dst_conn) {
×
NEW
992
                found = true;
×
NEW
993
                break;
×
994
            }
995
        }
NEW
996
        if (!found) {
×
NEW
997
            throw InvalidSDFGException("dst_conn not found in library node: " + dst_conn);
×
998
        }
999

NEW
1000
        auto& element_type = types::infer_type(function_, function_.type(src_node.data()), subset);
×
NEW
1001
        if (!dynamic_cast<const types::Scalar*>(&element_type)) {
×
NEW
1002
            throw InvalidSDFGException("Library node inputs must be scalars");
×
1003
        }
NEW
1004
    } else if (dynamic_cast<data_flow::LibraryNode*>(&src) &&
×
NEW
1005
               dynamic_cast<data_flow::AccessNode*>(&dst)) {
×
NEW
1006
        auto& src_node = dynamic_cast<data_flow::LibraryNode&>(src);
×
NEW
1007
        auto& dst_node = dynamic_cast<data_flow::AccessNode&>(dst);
×
NEW
1008
        if (dst_conn != "void") {
×
NEW
1009
            throw InvalidSDFGException("dst_conn must be void. Found: " + dst_conn);
×
1010
        }
NEW
1011
        bool found = false;
×
NEW
1012
        for (auto& output : src_node.outputs()) {
×
NEW
1013
            if (output.first == src_conn) {
×
NEW
1014
                found = true;
×
NEW
1015
                break;
×
1016
            }
1017
        }
NEW
1018
        if (!found) {
×
NEW
1019
            throw InvalidSDFGException("src_conn not found in library node: " + src_conn);
×
1020
        }
1021

NEW
1022
        auto& element_type = types::infer_type(function_, function_.type(dst_node.data()), subset);
×
NEW
1023
        if (!dynamic_cast<const types::Pointer*>(&element_type)) {
×
NEW
1024
            throw InvalidSDFGException("Access node must be a pointer");
×
1025
        }
NEW
1026
    } else {
×
NEW
1027
        throw InvalidSDFGException("Invalid src or dst node type");
×
1028
    }
1029

1030
    auto edge = boost::add_edge(src.vertex_, dst.vertex_, block.dataflow_->graph_);
544✔
1031
    auto res = block.dataflow_->edges_.insert(
1,088✔
1032
        {edge.first, std::unique_ptr<data_flow::Memlet>(new data_flow::Memlet(
544✔
1033
                         this->element_counter_, debug_info, edge.first, block.dataflow(), src,
544✔
1034
                         src_conn, dst, dst_conn, subset))});
544✔
1035
    this->element_counter_++;
544✔
1036
    return dynamic_cast<data_flow::Memlet&>(*(res.first->second));
544✔
1037
};
×
1038

1039
data_flow::LibraryNode& StructuredSDFGBuilder::add_library_node(
9✔
1040
    structured_control_flow::Block& block, const data_flow::LibraryNodeType& call,
1041
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& outputs,
1042
    const std::vector<std::pair<std::string, sdfg::types::Scalar>>& inputs,
1043
    const bool has_side_effect, const DebugInfo& debug_info) {
1044
    auto vertex = boost::add_vertex(block.dataflow_->graph_);
9✔
1045
    auto res = block.dataflow_->nodes_.insert(
18✔
1046
        {vertex, std::unique_ptr<data_flow::LibraryNode>(new data_flow::LibraryNode(
9✔
1047
                     this->element_counter_, debug_info, vertex, block.dataflow(), outputs, inputs,
9✔
1048
                     call, has_side_effect))});
9✔
1049
    this->element_counter_++;
9✔
1050
    return dynamic_cast<data_flow::LibraryNode&>(*(res.first->second));
9✔
1051
}
×
1052

1053
void StructuredSDFGBuilder::remove_memlet(structured_control_flow::Block& block,
×
1054
                                          const data_flow::Memlet& edge) {
1055
    auto& graph = block.dataflow();
×
1056
    auto e = edge.edge();
×
1057
    boost::remove_edge(e, graph.graph_);
×
1058
    graph.edges_.erase(e);
×
1059
};
×
1060

1061
void StructuredSDFGBuilder::remove_node(structured_control_flow::Block& block,
×
1062
                                        const data_flow::DataFlowNode& node) {
1063
    auto& graph = block.dataflow();
×
1064
    auto v = node.vertex();
×
1065
    boost::remove_vertex(v, graph.graph_);
×
1066
    graph.nodes_.erase(v);
×
1067
};
×
1068

1069
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
8✔
1070
                                       const data_flow::Tasklet& node) {
1071
    auto& graph = block.dataflow();
8✔
1072

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

1075
    // Delete incoming
1076
    std::list<const data_flow::Memlet*> iedges;
8✔
1077
    for (auto& iedge : graph.in_edges(node)) {
15✔
1078
        iedges.push_back(&iedge);
7✔
1079
    }
1080
    for (auto iedge : iedges) {
15✔
1081
        auto& src = iedge->src();
7✔
1082
        to_delete.insert(&src);
7✔
1083

1084
        auto edge = iedge->edge();
7✔
1085
        graph.edges_.erase(edge);
7✔
1086
        boost::remove_edge(edge, graph.graph_);
7✔
1087
    }
1088

1089
    // Delete outgoing
1090
    std::list<const data_flow::Memlet*> oedges;
8✔
1091
    for (auto& oedge : graph.out_edges(node)) {
16✔
1092
        oedges.push_back(&oedge);
8✔
1093
    }
1094
    for (auto oedge : oedges) {
16✔
1095
        auto& dst = oedge->dst();
8✔
1096
        to_delete.insert(&dst);
8✔
1097

1098
        auto edge = oedge->edge();
8✔
1099
        graph.edges_.erase(edge);
8✔
1100
        boost::remove_edge(edge, graph.graph_);
8✔
1101
    }
1102

1103
    // Delete nodes
1104
    for (auto obsolete_node : to_delete) {
31✔
1105
        if (graph.in_degree(*obsolete_node) == 0 && graph.out_degree(*obsolete_node) == 0) {
23✔
1106
            auto vertex = obsolete_node->vertex();
23✔
1107
            graph.nodes_.erase(vertex);
23✔
1108
            boost::remove_vertex(vertex, graph.graph_);
23✔
1109
        }
23✔
1110
    }
1111
};
8✔
1112

1113
void StructuredSDFGBuilder::clear_node(structured_control_flow::Block& block,
1✔
1114
                                       const data_flow::AccessNode& node) {
1115
    auto& graph = block.dataflow();
1✔
1116
    if (graph.out_degree(node) != 0) {
1✔
1117
        throw InvalidSDFGException("StructuredSDFGBuilder: Access node has outgoing edges");
×
1118
    }
1119

1120
    std::list<const data_flow::Memlet*> tmp;
1✔
1121
    std::list<const data_flow::DataFlowNode*> queue = {&node};
1✔
1122
    while (!queue.empty()) {
3✔
1123
        auto current = queue.front();
2✔
1124
        queue.pop_front();
2✔
1125
        if (current != &node) {
2✔
1126
            if (dynamic_cast<const data_flow::AccessNode*>(current)) {
1✔
1127
                if (graph.in_degree(*current) > 0 || graph.out_degree(*current) > 0) {
×
1128
                    continue;
×
1129
                }
1130
            }
×
1131
        }
1✔
1132

1133
        tmp.clear();
2✔
1134
        for (auto& iedge : graph.in_edges(*current)) {
3✔
1135
            tmp.push_back(&iedge);
1✔
1136
        }
1137
        for (auto iedge : tmp) {
3✔
1138
            auto& src = iedge->src();
1✔
1139
            queue.push_back(&src);
1✔
1140

1141
            auto edge = iedge->edge();
1✔
1142
            graph.edges_.erase(edge);
1✔
1143
            boost::remove_edge(edge, graph.graph_);
1✔
1144
        }
1145

1146
        auto vertex = current->vertex();
2✔
1147
        graph.nodes_.erase(vertex);
2✔
1148
        boost::remove_vertex(vertex, graph.graph_);
2✔
1149
    }
1150
};
1✔
1151

1152
data_flow::AccessNode& StructuredSDFGBuilder::symbolic_expression_to_dataflow(
×
1153
    structured_control_flow::Block& parent, const symbolic::Expression& expr) {
1154
    auto& sdfg = this->subject();
×
1155

1156
    codegen::CPPLanguageExtension language_extension;
×
1157

1158
    // Base cases
1159
    if (SymEngine::is_a<SymEngine::Symbol>(*expr)) {
×
1160
        auto sym = SymEngine::rcp_static_cast<const SymEngine::Symbol>(expr);
×
1161

1162
        // Determine type
1163
        types::Scalar sym_type = types::Scalar(types::PrimitiveType::Void);
×
1164
        if (symbolic::is_nvptx(sym)) {
×
1165
            sym_type = types::Scalar(types::PrimitiveType::Int32);
×
1166
        } else {
×
1167
            sym_type = static_cast<const types::Scalar&>(sdfg.type(sym->get_name()));
×
1168
        }
1169

1170
        // Add new container for intermediate result
1171
        auto tmp = this->find_new_name();
×
1172
        this->add_container(tmp, sym_type);
×
1173

1174
        // Create dataflow graph
1175
        auto& input_node = this->add_access(parent, sym->get_name());
×
1176
        auto& output_node = this->add_access(parent, tmp);
×
1177
        auto& tasklet = this->add_tasklet(parent, data_flow::TaskletCode::assign,
×
1178
                                          {"_out", sym_type}, {{"_in", sym_type}});
×
NEW
1179
        this->add_memlet(parent, input_node, "void", tasklet, "_in", {});
×
NEW
1180
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1181

1182
        return output_node;
×
1183
    } else if (SymEngine::is_a<SymEngine::Integer>(*expr)) {
×
1184
        auto tmp = this->find_new_name();
×
1185
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Int64));
×
1186

1187
        auto& output_node = this->add_access(parent, tmp);
×
1188
        auto& tasklet = this->add_tasklet(
×
1189
            parent, data_flow::TaskletCode::assign,
×
1190
            {"_out", types::Scalar(types::PrimitiveType::Int64)},
×
1191
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Int64)}});
×
NEW
1192
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1193
        return output_node;
×
1194
    } else if (SymEngine::is_a<SymEngine::BooleanAtom>(*expr)) {
×
1195
        auto tmp = this->find_new_name();
×
1196
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1197

1198
        auto& output_node = this->add_access(parent, tmp);
×
1199
        auto& tasklet = this->add_tasklet(
×
1200
            parent, data_flow::TaskletCode::assign,
×
1201
            {"_out", types::Scalar(types::PrimitiveType::Bool)},
×
1202
            {{language_extension.expression(expr), types::Scalar(types::PrimitiveType::Bool)}});
×
NEW
1203
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1204
        return output_node;
×
1205
    } else if (SymEngine::is_a<SymEngine::Or>(*expr)) {
×
1206
        auto or_expr = SymEngine::rcp_static_cast<const SymEngine::Or>(expr);
×
1207
        if (or_expr->get_container().size() != 2) {
×
1208
            throw InvalidSDFGException(
×
1209
                "StructuredSDFGBuilder: Or expression must have exactly two arguments");
×
1210
        }
1211

1212
        std::vector<data_flow::AccessNode*> input_nodes;
×
1213
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1214
        for (auto& arg : or_expr->get_container()) {
×
1215
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1216
            input_nodes.push_back(&input_node);
×
1217
            input_types.push_back(
×
1218
                {"_in" + std::to_string(input_types.size() + 1),
×
1219
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1220
        }
1221

1222
        // Add new container for intermediate result
1223
        auto tmp = this->find_new_name();
×
1224
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1225

1226
        auto& output_node = this->add_access(parent, tmp);
×
1227
        auto& tasklet =
×
1228
            this->add_tasklet(parent, data_flow::TaskletCode::logical_or,
×
1229
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1230
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1231
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
NEW
1232
                             {});
×
1233
        }
×
NEW
1234
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1235
        return output_node;
×
1236
    } else if (SymEngine::is_a<SymEngine::And>(*expr)) {
×
1237
        auto and_expr = SymEngine::rcp_static_cast<const SymEngine::And>(expr);
×
1238
        if (and_expr->get_container().size() != 2) {
×
1239
            throw InvalidSDFGException(
×
1240
                "StructuredSDFGBuilder: And expression must have exactly two arguments");
×
1241
        }
1242

1243
        std::vector<data_flow::AccessNode*> input_nodes;
×
1244
        std::vector<std::pair<std::string, types::Scalar>> input_types;
×
1245
        for (auto& arg : and_expr->get_container()) {
×
1246
            auto& input_node = symbolic_expression_to_dataflow(parent, arg);
×
1247
            input_nodes.push_back(&input_node);
×
1248
            input_types.push_back(
×
1249
                {"_in" + std::to_string(input_types.size() + 1),
×
1250
                 static_cast<const types::Scalar&>(sdfg.type(input_node.data()))});
×
1251
        }
1252

1253
        // Add new container for intermediate result
1254
        auto tmp = this->find_new_name();
×
1255
        this->add_container(tmp, types::Scalar(types::PrimitiveType::Bool));
×
1256

1257
        auto& output_node = this->add_access(parent, tmp);
×
1258
        auto& tasklet =
×
1259
            this->add_tasklet(parent, data_flow::TaskletCode::logical_and,
×
1260
                              {"_out", types::Scalar(types::PrimitiveType::Bool)}, input_types);
×
1261
        for (size_t i = 0; i < input_nodes.size(); i++) {
×
1262
            this->add_memlet(parent, *input_nodes.at(i), "void", tasklet, input_types.at(i).first,
×
NEW
1263
                             {});
×
1264
        }
×
NEW
1265
        this->add_memlet(parent, tasklet, "_out", output_node, "void", {});
×
1266
        return output_node;
×
1267
    } else {
×
1268
        throw std::runtime_error("Unsupported expression type");
×
1269
    }
1270
};
×
1271

1272
}  // namespace builder
1273
}  // 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