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

daisytuner / docc / 23339184494

20 Mar 2026 10:38AM UTC coverage: 64.298% (+0.2%) from 64.115%
23339184494

Pull #603

github

web-flow
Merge 9a713ba83 into 0c83c4ac4
Pull Request #603: Loop Collapse Transform

243 of 265 new or added lines in 4 files covered. (91.7%)

118 existing lines in 5 files now uncovered.

26616 of 41395 relevant lines covered (64.3%)

407.15 hits per line

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

65.48
/sdfg/src/data_flow/data_flow_graph.cpp
1
#include "sdfg/data_flow/data_flow_graph.h"
2

3
#include <algorithm>
4
#include <boost/graph/exception.hpp>
5
#include <cstddef>
6
#include <list>
7
#include <map>
8
#include <stdexcept>
9
#include <unordered_set>
10
#include <vector>
11

12
#include "sdfg/data_flow/access_node.h"
13
#include "sdfg/data_flow/code_node.h"
14
#include "sdfg/data_flow/data_flow_node.h"
15
#include "sdfg/data_flow/library_node.h"
16
#include "sdfg/data_flow/memlet.h"
17
#include "sdfg/graph/graph.h"
18

19
namespace sdfg {
20
namespace data_flow {
21

22
void DataFlowGraph::validate(const Function& function) const {
3,395✔
23
    for (auto& node : this->nodes_) {
10,913✔
24
        node.second->validate(function);
10,913✔
25
        if (&node.second->get_parent() != this) {
10,913✔
26
            throw InvalidSDFGException("DataFlowGraph: Node parent mismatch.");
×
27
        }
×
28
    }
10,913✔
29
    for (auto& edge : this->edges_) {
8,163✔
30
        edge.second->validate(function);
8,163✔
31
    }
8,163✔
32
};
3,395✔
33

34
const Element* DataFlowGraph::get_parent() const { return this->parent_; };
3,388✔
35

36
Element* DataFlowGraph::get_parent() { return this->parent_; };
4,793✔
37

38
const data_flow::Memlet* DataFlowGraph::in_edge_for_connector(const data_flow::CodeNode& node, const std::string& conn)
39
    const {
1✔
40
    for (const auto& edge : this->in_edges(node)) {
1✔
41
        if (edge.dst_conn() == conn) {
1✔
42
            return &edge;
1✔
43
        }
1✔
44
    }
1✔
45
    return nullptr;
×
46
}
1✔
47

48
const data_flow::Memlet* DataFlowGraph::in_edge(const data_flow::AccessNode& node) const {
×
49
    auto edges = in_edges(node);
×
50
    auto it = edges.begin();
×
51
    if (it == edges.end()) {
×
52
        return nullptr;
×
53
    }
×
54
    const auto& edge = *it;
×
55
    if (++it != edges.end()) {
×
56
        throw InvalidSDFGException("Access node " + node.data() + " has multiple incoming edges.");
×
57
    }
×
58
    return &edge;
×
59
}
×
60

61
std::vector<data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) {
436✔
62
    std::vector<data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
436✔
63
    for (auto& iedge : this->in_edges(node)) {
696✔
64
        for (size_t i = 0; i < node.inputs().size(); i++) {
964✔
65
            if (iedge.dst_conn() == node.input(i)) {
964✔
66
                in_edges[i] = &iedge;
696✔
67
                break;
696✔
68
            }
696✔
69
        }
964✔
70
    }
696✔
71
    return in_edges;
436✔
72
}
436✔
73

74
std::vector<const data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) const {
13✔
75
    std::vector<const data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
13✔
76
    for (const auto& iedge : this->in_edges(node)) {
14✔
77
        for (size_t i = 0; i < node.inputs().size(); i++) {
18✔
78
            if (iedge.dst_conn() == node.input(i)) {
18✔
79
                in_edges[i] = &iedge;
14✔
80
                break;
14✔
81
            }
14✔
82
        }
18✔
83
    }
14✔
84
    return in_edges;
13✔
85
}
13✔
86

UNCOV
87
std::vector<data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) {
×
UNCOV
88
    std::vector<data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
×
UNCOV
89
    for (auto& oedge : this->out_edges(node)) {
×
UNCOV
90
        for (size_t i = 0; i < node.outputs().size(); i++) {
×
UNCOV
91
            if (oedge.src_conn() == node.output(i)) {
×
UNCOV
92
                out_edges[i] = &oedge;
×
UNCOV
93
                break;
×
UNCOV
94
            }
×
UNCOV
95
        }
×
UNCOV
96
    }
×
UNCOV
97
    return out_edges;
×
UNCOV
98
}
×
99

100
std::vector<const data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) const {
18✔
101
    std::vector<const data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
18✔
102
    for (const auto& oedge : this->out_edges(node)) {
18✔
103
        for (size_t i = 0; i < node.outputs().size(); i++) {
18✔
104
            if (oedge.src_conn() == node.output(i)) {
18✔
105
                out_edges[i] = &oedge;
18✔
106
                break;
18✔
107
            }
18✔
108
        }
18✔
109
    }
18✔
110
    return out_edges;
18✔
111
}
18✔
112

113
std::vector<const data_flow::Memlet*> DataFlowGraph::
114
    out_edges_for_connector(const data_flow::CodeNode& node, const std::string& conn) const {
1✔
115
    std::vector<const data_flow::Memlet*> outs;
1✔
116
    for (auto& edge : out_edges(node)) {
1✔
117
        if (edge.src_conn() == conn) {
1✔
118
            outs.push_back(&edge);
1✔
119
        }
1✔
120
    }
1✔
121
    return outs;
1✔
122
}
1✔
123

124
size_t DataFlowGraph::in_degree(const data_flow::DataFlowNode& node) const {
16,318✔
125
    return boost::in_degree(node.vertex(), this->graph_);
16,318✔
126
};
16,318✔
127

128
size_t DataFlowGraph::out_degree(const data_flow::DataFlowNode& node) const {
21,443✔
129
    return boost::out_degree(node.vertex(), this->graph_);
21,443✔
130
};
21,443✔
131

132
void DataFlowGraph::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
68✔
133
    for (auto& node : this->nodes_) {
251✔
134
        node.second->replace(old_expression, new_expression);
251✔
135
    }
251✔
136

137
    for (auto& edge : this->edges_) {
188✔
138
        edge.second->replace(old_expression, new_expression);
188✔
139
    }
188✔
140
};
68✔
141

142
/***** Section: Analysis *****/
143

144
std::unordered_set<const data_flow::Tasklet*> DataFlowGraph::tasklets() const {
×
145
    std::unordered_set<const data_flow::Tasklet*> ts;
×
146
    for (auto& node : this->nodes_) {
×
147
        if (auto tasklet = dynamic_cast<const data_flow::Tasklet*>(node.second.get())) {
×
148
            ts.insert(tasklet);
×
149
        }
×
150
    }
×
151

152
    return ts;
×
153
};
×
154

155
std::unordered_set<data_flow::Tasklet*> DataFlowGraph::tasklets() {
1,070✔
156
    std::unordered_set<data_flow::Tasklet*> ts;
1,070✔
157
    for (auto& node : this->nodes_) {
3,925✔
158
        if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node.second.get())) {
3,925✔
159
            ts.insert(tasklet);
765✔
160
        }
765✔
161
    }
3,925✔
162

163
    return ts;
1,070✔
164
};
1,070✔
165

166
std::unordered_set<const data_flow::LibraryNode*> DataFlowGraph::library_nodes() const {
×
167
    std::unordered_set<const data_flow::LibraryNode*> ls;
×
168
    for (auto& node : this->nodes_) {
×
169
        if (auto lib_node = dynamic_cast<const data_flow::LibraryNode*>(node.second.get())) {
×
170
            ls.insert(lib_node);
×
171
        }
×
172
    }
×
173

174
    return ls;
×
175
};
×
176

177
std::unordered_set<data_flow::LibraryNode*> DataFlowGraph::library_nodes() {
1,534✔
178
    std::unordered_set<data_flow::LibraryNode*> ls;
1,534✔
179
    for (auto& node : this->nodes_) {
5,311✔
180
        if (auto lib_node = dynamic_cast<data_flow::LibraryNode*>(node.second.get())) {
5,311✔
181
            ls.insert(lib_node);
852✔
182
        }
852✔
183
    }
5,311✔
184

185
    return ls;
1,534✔
186
};
1,534✔
187

188
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::data_nodes() const {
×
189
    std::unordered_set<const data_flow::AccessNode*> dnodes;
×
190
    for (auto& node : this->nodes_) {
×
191
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
192
            dnodes.insert(access_node);
×
193
        }
×
194
    }
×
195

196
    return dnodes;
×
197
};
×
198

199
std::unordered_set<data_flow::AccessNode*> DataFlowGraph::data_nodes() {
519✔
200
    std::unordered_set<data_flow::AccessNode*> dnodes;
519✔
201
    for (auto& node : this->nodes_) {
1,866✔
202
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node.second.get())) {
1,866✔
203
            dnodes.insert(access_node);
1,341✔
204
        }
1,341✔
205
    }
1,866✔
206

207
    return dnodes;
519✔
208
};
519✔
209

210
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::reads() const {
×
211
    std::unordered_set<const data_flow::AccessNode*> rs;
×
212
    for (auto& node : this->nodes_) {
×
213
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
214
            if (this->out_degree(*access_node) > 0) {
×
215
                rs.insert(access_node);
×
216
            }
×
217
        }
×
218
    }
×
219

220
    return rs;
×
221
};
×
222

223
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::writes() const {
×
224
    std::unordered_set<const data_flow::AccessNode*> ws;
×
225
    for (auto& node : this->nodes_) {
×
226
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
227
            if (this->in_degree(*access_node) > 0) {
×
228
                ws.insert(access_node);
×
229
            }
×
230
        }
×
231
    }
×
232

233
    return ws;
×
234
};
×
235

236
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sources() const {
×
237
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
238
    for (auto& node : this->nodes_) {
×
239
        if (this->in_degree(*node.second) == 0) {
×
240
            ss.insert(node.second.get());
×
241
        }
×
242
    }
×
243

244
    return ss;
×
245
};
×
246

247
std::unordered_set<data_flow::DataFlowNode*> DataFlowGraph::sources() {
3✔
248
    std::unordered_set<data_flow::DataFlowNode*> ss;
3✔
249
    for (auto& node : this->nodes_) {
13✔
250
        if (this->in_degree(*node.second) == 0) {
13✔
251
            ss.insert(node.second.get());
7✔
252
        }
7✔
253
    }
13✔
254

255
    return ss;
3✔
256
};
3✔
257

258
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sinks() const {
×
259
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
260
    for (auto& node : this->nodes_) {
×
261
        if (this->out_degree(*node.second) == 0) {
×
262
            ss.insert(node.second.get());
×
263
        }
×
264
    }
×
265

266
    return ss;
×
267
};
×
268

269
std::unordered_set<data_flow::DataFlowNode*> DataFlowGraph::sinks() {
×
270
    std::unordered_set<data_flow::DataFlowNode*> ss;
×
271
    for (auto& node : this->nodes_) {
×
272
        if (this->out_degree(*node.second) == 0) {
×
273
            ss.insert(node.second.get());
×
274
        }
×
275
    }
×
276

277
    return ss;
×
278
};
×
279

280
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::predecessors(const data_flow::DataFlowNode& node
281
) const {
×
282
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
283
    for (auto& edge : this->in_edges(node)) {
×
284
        ss.insert(&edge.src());
×
285
    }
×
286

287
    return ss;
×
288
};
×
289

290
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::successors(const data_flow::DataFlowNode& node
291
) const {
×
292
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
293
    for (auto& edge : this->out_edges(node)) {
×
294
        ss.insert(&edge.dst());
×
295
    }
×
296

297
    return ss;
×
298
};
×
299

300
std::list<const DataFlowNode*> DataFlowGraph::topological_sort() const {
41✔
301
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
41✔
302

303
    // Build deterministic topological sort for each weakly connected component
304
    std::vector<std::list<const DataFlowNode*>> components(num_components);
41✔
305
    for (size_t i = 0; i < num_components; i++) {
83✔
306
        // Get all sinks of the current component
307
        std::vector<const DataFlowNode*> sinks;
42✔
308
        bool component_empty = true;
42✔
309
        for (auto [v, comp] : components_map) {
128✔
310
            if (comp == i) {
128✔
311
                component_empty = false;
122✔
312
                if (boost::out_degree(v, this->graph_) == 0) {
122✔
313
                    sinks.push_back(this->nodes_.at(v).get());
42✔
314
                }
42✔
315
            }
122✔
316
        }
128✔
317
        if (sinks.size() == 0) {
42✔
318
            if (component_empty) {
×
319
                continue;
×
320
            } else {
×
321
                throw boost::not_a_dag();
×
322
            }
×
323
        }
×
324

325
        // Create a queue with all sinks
326
        std::list<const DataFlowNode*> queue;
42✔
327
        queue.insert(queue.end(), sinks.begin(), sinks.end());
42✔
328

329
        // Perform a reversed DFS for each element in the queue
330
        std::unordered_map<const DataFlowNode*, std::list<const DataFlowNode*>> lists;
42✔
331
        std::unordered_map<const DataFlowNode*, std::list<std::pair<const DataFlowNode*, long long>>> dependencies;
42✔
332
        std::map<std::pair<const DataFlowNode*, long long>, const DataFlowNode*> backward_dependencies;
42✔
333
        std::unordered_set<const DataFlowNode*> visited;
42✔
334
        while (!queue.empty()) {
85✔
335
            const auto* start = queue.front();
43✔
336
            queue.pop_front();
43✔
337

338
            if (visited.contains(start)) {
43✔
339
                continue;
×
340
            }
×
341

342
            // Reversed DFS
343
            lists.insert({start, {}});
43✔
344
            dependencies.insert({start, {}});
43✔
345
            std::stack<std::pair<const DataFlowNode*, const DataFlowNode*>> stack({{start, nullptr}});
43✔
346
            while (!stack.empty()) {
166✔
347
                const auto* current = stack.top().first;
123✔
348
                const auto* successor = stack.top().second;
123✔
349
                stack.pop();
123✔
350

351
                // If multiple out edges, add to queue, add dependency, and skip
352
                if (current != start && this->out_degree(*current) > 1) {
123✔
353
                    queue.push_back(current);
1✔
354
                    long long dependency_id = -1;
1✔
355
                    if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
1✔
356
                        for (const auto& oedge : this->out_edges(*current)) {
×
357
                            const auto* dst = &oedge.dst();
×
358
                            if (dst == successor) {
×
359
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
×
360
                                    if (oedge.src_conn() == code_node->output(j)) {
×
361
                                        dependency_id = j;
×
362
                                        break;
×
363
                                    }
×
364
                                }
×
365
                                break;
×
366
                            }
×
367
                        }
×
368
                    } else {
1✔
369
                        std::vector<std::pair<const DataFlowNode*, size_t>> tmp_outputs;
1✔
370
                        std::unordered_set<const DataFlowNode*> local_visited;
1✔
371
                        for (const auto& oedge : this->out_edges(*current)) {
2✔
372
                            const auto* dst = &oedge.dst();
2✔
373
                            if (local_visited.contains(dst)) {
2✔
374
                                continue;
1✔
375
                            }
1✔
376
                            local_visited.insert(dst);
1✔
377
                            size_t value = 0;
1✔
378
                            if (const auto* tasklet = dynamic_cast<const Tasklet*>(dst)) {
1✔
379
                                value = tasklet->code();
1✔
380
                            } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(dst)) {
1✔
381
                                value = 52;
×
382
                                for (char c : libnode->code().value()) {
×
383
                                    value += c;
×
384
                                }
×
385
                            }
×
386
                            tmp_outputs.push_back({dst, value});
1✔
387
                        }
1✔
388
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
1✔
389
                            return a.second > b.second ||
×
390
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
×
391
                        });
×
392
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
1✔
393
                            if (tmp_outputs.at(j).first == successor) {
1✔
394
                                dependency_id = j;
1✔
395
                                break;
1✔
396
                            }
1✔
397
                        }
1✔
398
                    }
1✔
399
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
1✔
400
                        throw std::runtime_error("Could not create dependency in topological sort");
×
401
                    }
×
402
                    dependencies.at(start).push_front({current, dependency_id});
1✔
403
                    backward_dependencies.insert({{current, dependency_id}, start});
1✔
404
                    continue;
1✔
405
                }
1✔
406

407
                // Put the current element in the list
408
                if (visited.contains(current)) {
122✔
409
                    throw boost::not_a_dag();
×
410
                }
×
411
                visited.insert(current);
122✔
412
                lists.at(start).push_front(current);
122✔
413

414
                // Put all predecessors on the stack
415
                if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
122✔
416
                    std::unordered_set<const DataFlowNode*> local_visited;
37✔
417
                    for (const auto& input : code_node->inputs()) {
48✔
418
                        const Memlet* iedge = nullptr;
48✔
419
                        for (const auto& in_edge : this->in_edges(*code_node)) {
48✔
420
                            if (in_edge.dst_conn() == input) {
48✔
421
                                iedge = &in_edge;
38✔
422
                                break;
38✔
423
                            }
38✔
424
                        }
48✔
425
                        if (!iedge) {
48✔
426
                            continue;
10✔
427
                        }
10✔
428
                        const auto* src = &iedge->src();
38✔
429
                        if (!local_visited.contains(src)) {
38✔
430
                            local_visited.insert(src);
37✔
431
                            stack.push({src, current});
37✔
432
                        }
37✔
433
                    }
38✔
434
                } else {
85✔
435
                    std::vector<std::pair<const DataFlowNode*, size_t>> tmp_inputs;
85✔
436
                    std::unordered_set<const DataFlowNode*> local_visited;
85✔
437
                    for (const auto& iedge : this->in_edges(*current)) {
85✔
438
                        const auto* src = &iedge.src();
43✔
439
                        if (local_visited.contains(src)) {
43✔
440
                            continue;
×
441
                        }
×
442
                        local_visited.insert(src);
43✔
443
                        size_t value = 0;
43✔
444
                        if (const auto* tasklet = dynamic_cast<const Tasklet*>(src)) {
43✔
445
                            value = tasklet->code();
34✔
446
                        } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(src)) {
34✔
447
                            value = 52;
2✔
448
                            for (char c : libnode->code().value()) {
18✔
449
                                value += c;
18✔
450
                            }
18✔
451
                        }
2✔
452
                        tmp_inputs.push_back({src, value});
43✔
453
                    }
43✔
454
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
85✔
455
                        return a.second > b.second ||
×
456
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
×
457
                    });
×
458
                    for (auto& tmp_input : tmp_inputs) {
85✔
459
                        stack.push({tmp_input.first, current});
43✔
460
                    }
43✔
461
                }
85✔
462
            }
122✔
463
        }
43✔
464

465
        // Sort sinks if necessary
466
        if (sinks.size() > 1) {
42✔
467
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
×
468
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
×
469
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
×
470
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
×
471
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
×
472
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
×
473
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
×
474
                if (a_tasklet && b_tasklet) {
×
475
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
476
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
477
                } else if (a_tasklet && b_libnode) {
×
478
                    return true;
×
479
                } else if (a_tasklet && b_access_node) {
×
480
                    return true;
×
481
                } else if (a_libnode && b_libnode) {
×
482
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
483
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
484
                            a_libnode->element_id() < b_libnode->element_id());
×
485
                } else if (a_libnode && b_access_node) {
×
486
                    return true;
×
487
                } else if (a_access_node && b_access_node) {
×
488
                    return a_access_node->data() < b_access_node->data() ||
×
489
                           (a_access_node->data() == b_access_node->data() &&
×
490
                            a_access_node->element_id() < b_access_node->element_id());
×
491
                } else {
×
492
                    return false;
×
493
                }
×
494
            });
×
495
        }
×
496

497
        // Stich together by resolving dependencies
498
        visited.clear();
42✔
499
        for (const auto* sink : sinks) {
42✔
500
            if (visited.contains(sink)) {
42✔
501
                continue;
×
502
            }
×
503

504
            std::stack<const DataFlowNode*> stack({sink});
42✔
505
            while (!stack.empty()) {
86✔
506
                const auto* current = stack.top();
44✔
507
                visited.insert(current);
44✔
508

509
                bool all_resolved = true;
44✔
510
                for (auto [node, dependency_id] : dependencies.at(current)) {
44✔
511
                    if (!visited.contains(node)) {
2✔
512
                        stack.push(node);
1✔
513
                        all_resolved = false;
1✔
514
                        break;
1✔
515
                    }
1✔
516

517
                    for (long long j = 0; j < dependency_id; j++) {
1✔
518
                        if (!backward_dependencies.contains({node, j})) {
×
519
                            continue;
×
520
                        }
×
521
                        const auto* node2 = backward_dependencies.at({node, j});
×
522
                        if (!visited.contains(node2)) {
×
523
                            stack.push(node2);
×
524
                            all_resolved = false;
×
525
                            break;
×
526
                        }
×
527
                    }
×
528
                    if (!all_resolved) {
1✔
529
                        break;
×
530
                    }
×
531
                }
1✔
532
                if (!all_resolved) {
44✔
533
                    continue;
1✔
534
                }
1✔
535

536
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
43✔
537
                stack.pop();
43✔
538
            }
43✔
539
        }
42✔
540
    }
42✔
541

542
    // Sort components
543
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
41✔
544
        return a.size() > b.size() ||
2✔
545
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
2✔
546
    });
2✔
547

548
    // Resulting data structure
549
    std::list<const DataFlowNode*> order;
41✔
550
    for (auto& component : components) {
42✔
551
        order.insert(order.end(), component.begin(), component.end());
42✔
552
    }
42✔
553

554
    return order;
41✔
555
}
41✔
556

557
std::list<DataFlowNode*> DataFlowGraph::topological_sort() {
2,456✔
558
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
2,456✔
559

560
    // Build deterministic topological sort for each weakly connected component
561
    std::vector<std::list<DataFlowNode*>> components(num_components);
2,456✔
562
    for (size_t i = 0; i < num_components; i++) {
4,578✔
563
        // Get all sinks of the current component
564
        std::vector<DataFlowNode*> sinks;
2,122✔
565
        bool component_empty = true;
2,122✔
566
        for (auto [v, comp] : components_map) {
8,126✔
567
            if (comp == i) {
8,126✔
568
                component_empty = false;
7,880✔
569
                if (boost::out_degree(v, this->graph_) == 0) {
7,880✔
570
                    sinks.push_back(this->nodes_.at(v).get());
2,161✔
571
                }
2,161✔
572
            }
7,880✔
573
        }
8,126✔
574
        if (sinks.size() == 0) {
2,122✔
575
            if (component_empty) {
×
576
                continue;
×
577
            } else {
×
578
                throw boost::not_a_dag();
×
579
            }
×
580
        }
×
581

582
        // Create a queue with all sinks
583
        std::list<DataFlowNode*> queue;
2,122✔
584
        queue.insert(queue.end(), sinks.begin(), sinks.end());
2,122✔
585

586
        // Perform a reversed DFS for each element in the queue
587
        std::unordered_map<DataFlowNode*, std::list<DataFlowNode*>> lists;
2,122✔
588
        std::unordered_map<DataFlowNode*, std::list<std::pair<DataFlowNode*, long long>>> dependencies;
2,122✔
589
        std::map<std::pair<DataFlowNode*, long long>, DataFlowNode*> backward_dependencies;
2,122✔
590
        std::unordered_set<DataFlowNode*> visited;
2,122✔
591
        while (!queue.empty()) {
4,606✔
592
            auto* start = queue.front();
2,484✔
593
            queue.pop_front();
2,484✔
594

595
            if (visited.contains(start)) {
2,484✔
596
                continue;
92✔
597
            }
92✔
598

599
            // Reversed DFS
600
            lists.insert({start, {}});
2,392✔
601
            dependencies.insert({start, {}});
2,392✔
602
            std::stack<std::pair<DataFlowNode*, DataFlowNode*>> stack({{start, nullptr}});
2,392✔
603
            while (!stack.empty()) {
10,594✔
604
                auto* current = stack.top().first;
8,202✔
605
                auto* successor = stack.top().second;
8,202✔
606
                stack.pop();
8,202✔
607

608
                // If multiple out edges, add to queue, add dependency, and skip
609
                if (current != start && this->out_degree(*current) > 1) {
8,202✔
610
                    queue.push_back(current);
323✔
611
                    long long dependency_id = -1;
323✔
612
                    if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
323✔
613
                        for (auto& oedge : this->out_edges(*current)) {
12✔
614
                            auto* dst = &oedge.dst();
12✔
615
                            if (dst == successor) {
12✔
616
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
12✔
617
                                    if (oedge.src_conn() == code_node->output(j)) {
12✔
618
                                        dependency_id = j;
8✔
619
                                        break;
8✔
620
                                    }
8✔
621
                                }
12✔
622
                                break;
8✔
623
                            }
8✔
624
                        }
12✔
625
                    } else {
315✔
626
                        std::vector<std::pair<DataFlowNode*, size_t>> tmp_outputs;
315✔
627
                        std::unordered_set<DataFlowNode*> local_visited;
315✔
628
                        for (auto& oedge : this->out_edges(*current)) {
640✔
629
                            auto* dst = &oedge.dst();
640✔
630
                            if (local_visited.contains(dst)) {
640✔
631
                                continue;
143✔
632
                            }
143✔
633
                            local_visited.insert(dst);
497✔
634
                            size_t value = 0;
497✔
635
                            if (auto* tasklet = dynamic_cast<Tasklet*>(dst)) {
497✔
636
                                value = tasklet->code();
481✔
637
                            } else if (auto* libnode = dynamic_cast<LibraryNode*>(dst)) {
481✔
638
                                value = 52;
16✔
639
                                for (char c : libnode->code().value()) {
64✔
640
                                    value += c;
64✔
641
                                }
64✔
642
                            }
16✔
643
                            tmp_outputs.push_back({dst, value});
497✔
644
                        }
497✔
645
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
338✔
646
                            return a.second > b.second ||
338✔
647
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
338✔
648
                        });
338✔
649
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
406✔
650
                            if (tmp_outputs.at(j).first == successor) {
406✔
651
                                dependency_id = j;
315✔
652
                                break;
315✔
653
                            }
315✔
654
                        }
406✔
655
                    }
315✔
656
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
323✔
657
                        throw std::runtime_error("Could not create dependency in topological sort");
×
658
                    }
×
659
                    dependencies.at(start).push_front({current, dependency_id});
323✔
660
                    backward_dependencies.insert({{current, dependency_id}, start});
323✔
661
                    continue;
323✔
662
                }
323✔
663

664
                // Put the current element in the list
665
                if (visited.contains(current)) {
7,879✔
666
                    throw boost::not_a_dag();
×
667
                }
×
668
                visited.insert(current);
7,879✔
669
                lists.at(start).push_front(current);
7,879✔
670

671
                // Put all predecessors on the stack
672
                if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
7,879✔
673
                    std::unordered_set<DataFlowNode*> local_visited;
2,232✔
674
                    for (auto& input : code_node->inputs()) {
3,733✔
675
                        Memlet* iedge = nullptr;
3,733✔
676
                        for (auto& in_edge : this->in_edges(*code_node)) {
5,641✔
677
                            if (in_edge.dst_conn() == input) {
5,641✔
678
                                iedge = &in_edge;
3,693✔
679
                                break;
3,693✔
680
                            }
3,693✔
681
                        }
5,641✔
682
                        if (!iedge) {
3,733✔
683
                            continue;
40✔
684
                        }
40✔
685
                        auto* src = &iedge->src();
3,693✔
686
                        if (!local_visited.contains(src)) {
3,693✔
687
                            local_visited.insert(src);
3,550✔
688
                            stack.push({src, current});
3,550✔
689
                        }
3,550✔
690
                    }
3,693✔
691
                } else {
5,647✔
692
                    std::vector<std::pair<DataFlowNode*, size_t>> tmp_inputs;
5,647✔
693
                    std::unordered_set<DataFlowNode*> local_visited;
5,647✔
694
                    for (auto& iedge : this->in_edges(*current)) {
5,647✔
695
                        auto* src = &iedge.src();
2,260✔
696
                        if (local_visited.contains(src)) {
2,260✔
697
                            continue;
×
698
                        }
×
699
                        local_visited.insert(src);
2,260✔
700
                        size_t value = 0;
2,260✔
701
                        if (auto* tasklet = dynamic_cast<Tasklet*>(src)) {
2,260✔
702
                            value = tasklet->code();
2,081✔
703
                        } else if (auto* libnode = dynamic_cast<LibraryNode*>(src)) {
2,081✔
704
                            value = 52;
106✔
705
                            for (char c : libnode->code().value()) {
807✔
706
                                value += c;
807✔
707
                            }
807✔
708
                        }
106✔
709
                        tmp_inputs.push_back({src, value});
2,260✔
710
                    }
2,260✔
711
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
5,647✔
712
                        return a.second > b.second ||
26✔
713
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
26✔
714
                    });
26✔
715
                    for (auto& tmp_input : tmp_inputs) {
5,647✔
716
                        stack.push({tmp_input.first, current});
2,260✔
717
                    }
2,260✔
718
                }
5,647✔
719
            }
7,879✔
720
        }
2,392✔
721

722
        // Sort sinks if necessary
723
        if (sinks.size() > 1) {
2,122✔
724
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
72✔
725
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
72✔
726
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
72✔
727
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
72✔
728
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
72✔
729
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
72✔
730
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
72✔
731
                if (a_tasklet && b_tasklet) {
72✔
732
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
733
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
734
                } else if (a_tasklet && b_libnode) {
72✔
735
                    return true;
×
736
                } else if (a_tasklet && b_access_node) {
72✔
737
                    return true;
×
738
                } else if (a_libnode && b_libnode) {
72✔
739
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
740
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
741
                            a_libnode->element_id() < b_libnode->element_id());
×
742
                } else if (a_libnode && b_access_node) {
72✔
743
                    return true;
×
744
                } else if (a_access_node && b_access_node) {
72✔
745
                    return a_access_node->data() < b_access_node->data() ||
72✔
746
                           (a_access_node->data() == b_access_node->data() &&
72✔
747
                            a_access_node->element_id() < b_access_node->element_id());
66✔
748
                } else {
72✔
749
                    return false;
×
750
                }
×
751
            });
72✔
752
        }
37✔
753

754
        // Stich together by resolving dependencies
755
        visited.clear();
2,122✔
756
        for (auto* sink : sinks) {
2,161✔
757
            if (visited.contains(sink)) {
2,161✔
758
                continue;
30✔
759
            }
30✔
760

761
            std::stack<DataFlowNode*> stack({sink});
2,131✔
762
            while (!stack.empty()) {
4,784✔
763
                auto* current = stack.top();
2,653✔
764
                visited.insert(current);
2,653✔
765

766
                bool all_resolved = true;
2,653✔
767
                for (auto [node, dependency_id] : dependencies.at(current)) {
2,653✔
768
                    if (!visited.contains(node)) {
621✔
769
                        stack.push(node);
231✔
770
                        all_resolved = false;
231✔
771
                        break;
231✔
772
                    }
231✔
773

774
                    for (long long j = 0; j < dependency_id; j++) {
489✔
775
                        if (!backward_dependencies.contains({node, j})) {
129✔
776
                            continue;
×
777
                        }
×
778
                        auto* node2 = backward_dependencies.at({node, j});
129✔
779
                        if (!visited.contains(node2)) {
129✔
780
                            stack.push(node2);
30✔
781
                            all_resolved = false;
30✔
782
                            break;
30✔
783
                        }
30✔
784
                    }
129✔
785
                    if (!all_resolved) {
390✔
786
                        break;
30✔
787
                    }
30✔
788
                }
390✔
789
                if (!all_resolved) {
2,653✔
790
                    continue;
261✔
791
                }
261✔
792

793
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
2,392✔
794
                stack.pop();
2,392✔
795
            }
2,392✔
796
        }
2,131✔
797
    }
2,122✔
798

799
    // Sort components
800
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
2,456✔
801
        return a.size() > b.size() ||
58✔
802
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
58✔
803
    });
58✔
804

805
    // Resulting data structure
806
    std::list<DataFlowNode*> order;
2,456✔
807
    for (auto& component : components) {
2,456✔
808
        order.insert(order.end(), component.begin(), component.end());
2,122✔
809
    }
2,122✔
810

811
    return order;
2,456✔
812
}
2,456✔
813

814
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::dominators() const {
×
815
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
816
    for (auto& node : this->topological_sort()) {
×
817
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
818
            if (frontier.find(access_node->data()) == frontier.end()) {
×
819
                frontier[access_node->data()] = access_node;
×
820
            }
×
821
        }
×
822
    }
×
823

824
    return frontier;
×
825
};
×
826

827
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::post_dominators() const {
×
828
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
829
    for (auto& node : this->topological_sort()) {
×
830
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
831
            frontier[access_node->data()] = access_node;
×
832
        }
×
833
    }
×
834

835
    return frontier;
×
836
};
×
837

838
std::unordered_map<std::string, data_flow::AccessNode*> DataFlowGraph::post_dominators() {
3✔
839
    std::unordered_map<std::string, data_flow::AccessNode*> frontier;
3✔
840
    for (auto& node : this->topological_sort()) {
13✔
841
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
13✔
842
            frontier[access_node->data()] = access_node;
10✔
843
        }
10✔
844
    }
13✔
845

846
    return frontier;
3✔
847
};
3✔
848

849
auto DataFlowGraph::all_simple_paths(const data_flow::DataFlowNode& src, const data_flow::DataFlowNode& dst) const {
×
850
    std::list<std::list<graph::Edge>> all_paths_raw = graph::all_simple_paths(this->graph_, src.vertex(), dst.vertex());
×
851

852
    std::list<std::list<std::reference_wrapper<data_flow::Memlet>>> all_paths;
×
853
    for (auto& path_raw : all_paths_raw) {
×
854
        std::list<std::reference_wrapper<data_flow::Memlet>> path;
×
855
        for (auto& edge : path_raw) {
×
856
            path.push_back(*this->edges_.at(edge));
×
857
        }
×
858
        all_paths.push_back(path);
×
859
    }
×
860

861
    return all_paths;
×
862
};
×
863

864
const std::pair<size_t, const std::unordered_map<const data_flow::DataFlowNode*, size_t>> DataFlowGraph::
865
    weakly_connected_components() const {
8✔
866
    auto ccs_vertex = graph::weakly_connected_components(this->graph_);
8✔
867

868
    std::unordered_map<const data_flow::DataFlowNode*, size_t> ccs;
8✔
869
    for (auto& entry : ccs_vertex.second) {
41✔
870
        ccs[this->nodes_.at(entry.first).get()] = entry.second;
41✔
871
    }
41✔
872

873
    return {ccs_vertex.first, ccs};
8✔
874
};
8✔
875

876

877
} // namespace data_flow
878
} // 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