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

daisytuner / sdfglib / 20990425281

14 Jan 2026 10:13AM UTC coverage: 62.264% (+0.3%) from 61.95%
20990425281

Pull #402

github

web-flow
Merge 33037e1b8 into 600fd8ca9
Pull Request #402: Replace topological sort with deterministic implementation

379 of 540 new or added lines in 2 files covered. (70.19%)

8 existing lines in 2 files now uncovered.

15723 of 25252 relevant lines covered (62.26%)

93.3 hits per line

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

63.49
/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 {
868✔
23
    for (auto& node : this->nodes_) {
1,337✔
24
        node.second->validate(function);
1,337✔
25
        if (&node.second->get_parent() != this) {
1,337✔
26
            throw InvalidSDFGException("DataFlowGraph: Node parent mismatch.");
×
27
        }
×
28
    }
1,337✔
29
    for (auto& edge : this->edges_) {
963✔
30
        edge.second->validate(function);
963✔
31
    }
963✔
32
};
868✔
33

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

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

NEW
38
std::vector<data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) {
×
NEW
39
    std::vector<data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
×
NEW
40
    for (auto& iedge : this->in_edges(node)) {
×
NEW
41
        for (size_t i = 0; i < node.inputs().size(); i++) {
×
NEW
42
            if (iedge.dst_conn() == node.input(i)) {
×
NEW
43
                in_edges[i] = &iedge;
×
NEW
44
                break;
×
NEW
45
            }
×
NEW
46
        }
×
NEW
47
    }
×
NEW
48
    return in_edges;
×
NEW
49
}
×
50

51
std::vector<const data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) const {
3✔
52
    std::vector<const data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
3✔
53
    for (const auto& iedge : this->in_edges(node)) {
4✔
54
        for (size_t i = 0; i < node.inputs().size(); i++) {
5✔
55
            if (iedge.dst_conn() == node.input(i)) {
5✔
56
                in_edges[i] = &iedge;
4✔
57
                break;
4✔
58
            }
4✔
59
        }
5✔
60
    }
4✔
61
    return in_edges;
3✔
62
}
3✔
63

NEW
64
std::vector<data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) {
×
NEW
65
    std::vector<data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
×
NEW
66
    for (auto& oedge : this->out_edges(node)) {
×
NEW
67
        for (size_t i = 0; i < node.outputs().size(); i++) {
×
NEW
68
            if (oedge.src_conn() == node.output(i)) {
×
NEW
69
                out_edges[i] = &oedge;
×
NEW
70
                break;
×
NEW
71
            }
×
NEW
72
        }
×
NEW
73
    }
×
NEW
74
    return out_edges;
×
NEW
75
}
×
76

77
std::vector<const data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) const {
3✔
78
    std::vector<const data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
3✔
79
    for (const auto& oedge : this->out_edges(node)) {
3✔
80
        for (size_t i = 0; i < node.outputs().size(); i++) {
3✔
81
            if (oedge.src_conn() == node.output(i)) {
3✔
82
                out_edges[i] = &oedge;
3✔
83
                break;
3✔
84
            }
3✔
85
        }
3✔
86
    }
3✔
87
    return out_edges;
3✔
88
}
3✔
89

90
size_t DataFlowGraph::in_degree(const data_flow::DataFlowNode& node) const {
2,556✔
91
    return boost::in_degree(node.vertex(), this->graph_);
2,556✔
92
};
2,556✔
93

94
size_t DataFlowGraph::out_degree(const data_flow::DataFlowNode& node) const {
3,265✔
95
    return boost::out_degree(node.vertex(), this->graph_);
3,265✔
96
};
3,265✔
97

98
void DataFlowGraph::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
10✔
99
    for (auto& node : this->nodes_) {
31✔
100
        node.second->replace(old_expression, new_expression);
31✔
101
    }
31✔
102

103
    for (auto& edge : this->edges_) {
23✔
104
        edge.second->replace(old_expression, new_expression);
23✔
105
    }
23✔
106
};
10✔
107

108
/***** Section: Analysis *****/
109

110
std::unordered_set<const data_flow::Tasklet*> DataFlowGraph::tasklets() const {
×
111
    std::unordered_set<const data_flow::Tasklet*> ts;
×
112
    for (auto& node : this->nodes_) {
×
113
        if (auto tasklet = dynamic_cast<const data_flow::Tasklet*>(node.second.get())) {
×
114
            ts.insert(tasklet);
×
115
        }
×
116
    }
×
117

118
    return ts;
×
119
};
×
120

121
std::unordered_set<data_flow::Tasklet*> DataFlowGraph::tasklets() {
316✔
122
    std::unordered_set<data_flow::Tasklet*> ts;
316✔
123
    for (auto& node : this->nodes_) {
1,192✔
124
        if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node.second.get())) {
1,192✔
125
            ts.insert(tasklet);
284✔
126
        }
284✔
127
    }
1,192✔
128

129
    return ts;
316✔
130
};
316✔
131

132
std::unordered_set<const data_flow::LibraryNode*> DataFlowGraph::library_nodes() const {
×
133
    std::unordered_set<const data_flow::LibraryNode*> ls;
×
134
    for (auto& node : this->nodes_) {
×
135
        if (auto lib_node = dynamic_cast<const data_flow::LibraryNode*>(node.second.get())) {
×
136
            ls.insert(lib_node);
×
137
        }
×
138
    }
×
139

140
    return ls;
×
141
};
×
142

143
std::unordered_set<data_flow::LibraryNode*> DataFlowGraph::library_nodes() {
338✔
144
    std::unordered_set<data_flow::LibraryNode*> ls;
338✔
145
    for (auto& node : this->nodes_) {
1,229✔
146
        if (auto lib_node = dynamic_cast<data_flow::LibraryNode*>(node.second.get())) {
1,229✔
147
            ls.insert(lib_node);
237✔
148
        }
237✔
149
    }
1,229✔
150

151
    return ls;
338✔
152
};
338✔
153

154
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::data_nodes() const {
×
155
    std::unordered_set<const data_flow::AccessNode*> dnodes;
×
156
    for (auto& node : this->nodes_) {
×
157
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
158
            dnodes.insert(access_node);
×
159
        }
×
160
    }
×
161

162
    return dnodes;
×
163
};
×
164

165
std::unordered_set<data_flow::AccessNode*> DataFlowGraph::data_nodes() {
70✔
166
    std::unordered_set<data_flow::AccessNode*> dnodes;
70✔
167
    for (auto& node : this->nodes_) {
251✔
168
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node.second.get())) {
251✔
169
            dnodes.insert(access_node);
175✔
170
        }
175✔
171
    }
251✔
172

173
    return dnodes;
70✔
174
};
70✔
175

176
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::reads() const {
×
177
    std::unordered_set<const data_flow::AccessNode*> rs;
×
178
    for (auto& node : this->nodes_) {
×
179
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
180
            if (this->out_degree(*access_node) > 0) {
×
181
                rs.insert(access_node);
×
182
            }
×
183
        }
×
184
    }
×
185

186
    return rs;
×
187
};
×
188

189
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::writes() const {
×
190
    std::unordered_set<const data_flow::AccessNode*> ws;
×
191
    for (auto& node : this->nodes_) {
×
192
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
193
            if (this->in_degree(*access_node) > 0) {
×
194
                ws.insert(access_node);
×
195
            }
×
196
        }
×
197
    }
×
198

199
    return ws;
×
200
};
×
201

202
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sources() const {
×
203
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
204
    for (auto& node : this->nodes_) {
×
205
        if (this->in_degree(*node.second) == 0) {
×
206
            ss.insert(node.second.get());
×
207
        }
×
208
    }
×
209

210
    return ss;
×
211
};
×
212

213
std::unordered_set<data_flow::DataFlowNode*> DataFlowGraph::sources() {
3✔
214
    std::unordered_set<data_flow::DataFlowNode*> ss;
3✔
215
    for (auto& node : this->nodes_) {
13✔
216
        if (this->in_degree(*node.second) == 0) {
13✔
217
            ss.insert(node.second.get());
7✔
218
        }
7✔
219
    }
13✔
220

221
    return ss;
3✔
222
};
3✔
223

224
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sinks() const {
×
225
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
226
    for (auto& node : this->nodes_) {
×
227
        if (this->out_degree(*node.second) == 0) {
×
228
            ss.insert(node.second.get());
×
229
        }
×
230
    }
×
231

232
    return ss;
×
233
};
×
234

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

243
    return ss;
×
244
};
×
245

246
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::predecessors(const data_flow::DataFlowNode& node
247
) const {
×
248
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
249
    for (auto& edge : this->in_edges(node)) {
×
250
        ss.insert(&edge.src());
×
251
    }
×
252

253
    return ss;
×
254
};
×
255

256
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::successors(const data_flow::DataFlowNode& node
257
) const {
×
258
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
259
    for (auto& edge : this->out_edges(node)) {
×
260
        ss.insert(&edge.dst());
×
261
    }
×
262

263
    return ss;
×
264
};
×
265

266
std::list<const DataFlowNode*> DataFlowGraph::topological_sort() const {
34✔
267
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
34✔
268

269
    // Build deterministic topological sort for each weakly connected component
270
    std::vector<std::list<const DataFlowNode*>> components(num_components);
34✔
271
    for (size_t i = 0; i < num_components; i++) {
69✔
272
        // Get all sinks of the current component
273
        std::vector<const DataFlowNode*> sinks;
35✔
274
        bool component_empty = true;
35✔
275
        for (auto [v, comp] : components_map) {
107✔
276
            if (comp == i) {
107✔
277
                component_empty = false;
101✔
278
                if (boost::out_degree(v, this->graph_) == 0) {
101✔
279
                    sinks.push_back(this->nodes_.at(v).get());
35✔
280
                }
35✔
281
            }
101✔
282
        }
107✔
283
        if (sinks.size() == 0) {
35✔
NEW
284
            if (component_empty) {
×
NEW
285
                continue;
×
NEW
286
            } else {
×
NEW
287
                throw boost::not_a_dag();
×
NEW
288
            }
×
NEW
289
        }
×
290

291
        // Create a queue with all sinks
292
        std::list<const DataFlowNode*> queue;
35✔
293
        queue.insert(queue.end(), sinks.begin(), sinks.end());
35✔
294

295
        // Perform a reversed DFS for each element in the queue
296
        std::unordered_map<const DataFlowNode*, std::list<const DataFlowNode*>> lists;
35✔
297
        std::unordered_map<const DataFlowNode*, std::list<std::pair<const DataFlowNode*, long long>>> dependencies;
35✔
298
        std::map<std::pair<const DataFlowNode*, long long>, const DataFlowNode*> backward_dependencies;
35✔
299
        std::unordered_set<const DataFlowNode*> visited;
35✔
300
        while (!queue.empty()) {
71✔
301
            const auto* start = queue.front();
36✔
302
            queue.pop_front();
36✔
303

304
            if (visited.contains(start)) {
36✔
NEW
305
                continue;
×
NEW
306
            }
×
307

308
            // Reversed DFS
309
            lists.insert({start, {}});
36✔
310
            dependencies.insert({start, {}});
36✔
311
            std::stack<std::pair<const DataFlowNode*, const DataFlowNode*>> stack({{start, nullptr}});
36✔
312
            while (!stack.empty()) {
138✔
313
                const auto* current = stack.top().first;
102✔
314
                const auto* successor = stack.top().second;
102✔
315
                stack.pop();
102✔
316

317
                // If multiple out edges, add to queue, add dependency, and skip
318
                if (current != start && this->out_degree(*current) > 1) {
102✔
319
                    queue.push_back(current);
1✔
320
                    long long dependency_id = -1;
1✔
321
                    if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
1✔
NEW
322
                        for (const auto& oedge : this->out_edges(*current)) {
×
NEW
323
                            const auto* dst = &oedge.dst();
×
NEW
324
                            if (dst == successor) {
×
NEW
325
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
×
NEW
326
                                    if (oedge.src_conn() == code_node->output(j)) {
×
NEW
327
                                        dependency_id = j;
×
NEW
328
                                        break;
×
NEW
329
                                    }
×
NEW
330
                                }
×
NEW
331
                                break;
×
NEW
332
                            }
×
NEW
333
                        }
×
334
                    } else {
1✔
335
                        std::vector<std::pair<const DataFlowNode*, size_t>> tmp_outputs;
1✔
336
                        std::unordered_set<const DataFlowNode*> local_visited;
1✔
337
                        for (const auto& oedge : this->out_edges(*current)) {
2✔
338
                            const auto* dst = &oedge.dst();
2✔
339
                            if (local_visited.contains(dst)) {
2✔
340
                                continue;
1✔
341
                            }
1✔
342
                            local_visited.insert(dst);
1✔
343
                            size_t value = 0;
1✔
344
                            if (const auto* tasklet = dynamic_cast<const Tasklet*>(dst)) {
1✔
345
                                value = tasklet->code();
1✔
346
                            } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(dst)) {
1✔
NEW
347
                                value = 52;
×
NEW
348
                                for (char c : libnode->code().value()) {
×
NEW
349
                                    value += c;
×
NEW
350
                                }
×
NEW
351
                            }
×
352
                            tmp_outputs.push_back({dst, value});
1✔
353
                        }
1✔
354
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
1✔
NEW
355
                            return a.second > b.second ||
×
NEW
356
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
×
NEW
357
                        });
×
358
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
1✔
359
                            if (tmp_outputs.at(j).first == successor) {
1✔
360
                                dependency_id = j;
1✔
361
                                break;
1✔
362
                            }
1✔
363
                        }
1✔
364
                    }
1✔
365
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
1✔
NEW
366
                        throw std::runtime_error("Could not create dependency in topological sort");
×
NEW
367
                    }
×
368
                    dependencies.at(start).push_front({current, dependency_id});
1✔
369
                    backward_dependencies.insert({{current, dependency_id}, start});
1✔
370
                    continue;
1✔
371
                }
1✔
372

373
                // Put the current element in the list
374
                if (visited.contains(current)) {
101✔
NEW
375
                    throw boost::not_a_dag();
×
NEW
376
                }
×
377
                visited.insert(current);
101✔
378
                lists.at(start).push_front(current);
101✔
379

380
                // Put all predecessors on the stack
381
                if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
101✔
382
                    std::unordered_set<const DataFlowNode*> local_visited;
30✔
383
                    for (const auto& input : code_node->inputs()) {
41✔
384
                        const Memlet* iedge = nullptr;
41✔
385
                        for (const auto& in_edge : this->in_edges(*code_node)) {
41✔
386
                            if (in_edge.dst_conn() == input) {
38✔
387
                                iedge = &in_edge;
31✔
388
                                break;
31✔
389
                            }
31✔
390
                        }
38✔
391
                        if (!iedge) {
41✔
392
                            continue;
10✔
393
                        }
10✔
394
                        const auto* src = &iedge->src();
31✔
395
                        if (!local_visited.contains(src)) {
31✔
396
                            local_visited.insert(src);
30✔
397
                            stack.push({src, current});
30✔
398
                        }
30✔
399
                    }
31✔
400
                } else {
71✔
401
                    std::vector<std::pair<const DataFlowNode*, size_t>> tmp_inputs;
71✔
402
                    std::unordered_set<const DataFlowNode*> local_visited;
71✔
403
                    for (const auto& iedge : this->in_edges(*current)) {
71✔
404
                        const auto* src = &iedge.src();
36✔
405
                        if (local_visited.contains(src)) {
36✔
NEW
406
                            continue;
×
NEW
407
                        }
×
408
                        local_visited.insert(src);
36✔
409
                        size_t value = 0;
36✔
410
                        if (const auto* tasklet = dynamic_cast<const Tasklet*>(src)) {
36✔
411
                            value = tasklet->code();
29✔
412
                        } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(src)) {
29✔
NEW
413
                            value = 52;
×
NEW
414
                            for (char c : libnode->code().value()) {
×
NEW
415
                                value += c;
×
NEW
416
                            }
×
NEW
417
                        }
×
418
                        tmp_inputs.push_back({src, value});
36✔
419
                    }
36✔
420
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
71✔
NEW
421
                        return a.second > b.second ||
×
NEW
422
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
×
NEW
423
                    });
×
424
                    for (auto& tmp_input : tmp_inputs) {
71✔
425
                        stack.push({tmp_input.first, current});
36✔
426
                    }
36✔
427
                }
71✔
428
            }
101✔
429
        }
36✔
430

431
        // Sort sinks if necessary
432
        if (sinks.size() > 1) {
35✔
NEW
433
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
×
NEW
434
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
×
NEW
435
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
×
NEW
436
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
×
NEW
437
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
×
NEW
438
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
×
NEW
439
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
×
NEW
440
                if (a_tasklet && b_tasklet) {
×
NEW
441
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
NEW
442
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
NEW
443
                } else if (a_tasklet && b_libnode) {
×
NEW
444
                    return true;
×
NEW
445
                } else if (a_tasklet && b_access_node) {
×
NEW
446
                    return true;
×
NEW
447
                } else if (a_libnode && b_libnode) {
×
NEW
448
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
NEW
449
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
NEW
450
                            a_libnode->element_id() < b_libnode->element_id());
×
NEW
451
                } else if (a_libnode && b_access_node) {
×
NEW
452
                    return true;
×
NEW
453
                } else if (a_access_node && b_access_node) {
×
NEW
454
                    return a_access_node->data() < b_access_node->data() ||
×
NEW
455
                           (a_access_node->data() == b_access_node->data() &&
×
NEW
456
                            a_access_node->element_id() < b_access_node->element_id());
×
NEW
457
                } else {
×
NEW
458
                    return false;
×
NEW
459
                }
×
NEW
460
            });
×
NEW
461
        }
×
462

463
        // Stich together by resolving dependencies
464
        visited.clear();
35✔
465
        for (const auto* sink : sinks) {
35✔
466
            if (visited.contains(sink)) {
35✔
NEW
467
                continue;
×
NEW
468
            }
×
469

470
            std::stack<const DataFlowNode*> stack({sink});
35✔
471
            while (!stack.empty()) {
72✔
472
                const auto* current = stack.top();
37✔
473
                visited.insert(current);
37✔
474

475
                bool all_resolved = true;
37✔
476
                for (auto [node, dependency_id] : dependencies.at(current)) {
37✔
477
                    if (!visited.contains(node)) {
2✔
478
                        stack.push(node);
1✔
479
                        all_resolved = false;
1✔
480
                        break;
1✔
481
                    }
1✔
482

483
                    for (long long j = 0; j < dependency_id; j++) {
1✔
NEW
484
                        if (!backward_dependencies.contains({node, j})) {
×
NEW
485
                            continue;
×
NEW
486
                        }
×
NEW
487
                        const auto* node2 = backward_dependencies.at({node, j});
×
NEW
488
                        if (!visited.contains(node2)) {
×
NEW
489
                            stack.push(node2);
×
NEW
490
                            all_resolved = false;
×
NEW
491
                            break;
×
NEW
492
                        }
×
NEW
493
                    }
×
494
                    if (!all_resolved) {
1✔
NEW
495
                        break;
×
NEW
496
                    }
×
497
                }
1✔
498
                if (!all_resolved) {
37✔
499
                    continue;
1✔
500
                }
1✔
501

502
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
36✔
503
                stack.pop();
36✔
504
            }
36✔
505
        }
35✔
506
    }
35✔
507

508
    // Sort components
509
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
34✔
510
        return a.size() > b.size() ||
2✔
511
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
2✔
512
    });
2✔
513

514
    // Resulting data structure
515
    std::list<const DataFlowNode*> order;
34✔
516
    for (auto& component : components) {
35✔
517
        order.insert(order.end(), component.begin(), component.end());
35✔
518
    }
35✔
519

520
    return order;
34✔
521
}
34✔
522

523
std::list<DataFlowNode*> DataFlowGraph::topological_sort() {
659✔
524
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
659✔
525

526
    // Build deterministic topological sort for each weakly connected component
527
    std::vector<std::list<DataFlowNode*>> components(num_components);
659✔
528
    for (size_t i = 0; i < num_components; i++) {
1,034✔
529
        // Get all sinks of the current component
530
        std::vector<DataFlowNode*> sinks;
375✔
531
        bool component_empty = true;
375✔
532
        for (auto [v, comp] : components_map) {
1,327✔
533
            if (comp == i) {
1,327✔
534
                component_empty = false;
1,265✔
535
                if (boost::out_degree(v, this->graph_) == 0) {
1,265✔
536
                    sinks.push_back(this->nodes_.at(v).get());
385✔
537
                }
385✔
538
            }
1,265✔
539
        }
1,327✔
540
        if (sinks.size() == 0) {
375✔
NEW
541
            if (component_empty) {
×
NEW
542
                continue;
×
NEW
543
            } else {
×
NEW
544
                throw boost::not_a_dag();
×
NEW
545
            }
×
NEW
546
        }
×
547

548
        // Create a queue with all sinks
549
        std::list<DataFlowNode*> queue;
375✔
550
        queue.insert(queue.end(), sinks.begin(), sinks.end());
375✔
551

552
        // Perform a reversed DFS for each element in the queue
553
        std::unordered_map<DataFlowNode*, std::list<DataFlowNode*>> lists;
375✔
554
        std::unordered_map<DataFlowNode*, std::list<std::pair<DataFlowNode*, long long>>> dependencies;
375✔
555
        std::map<std::pair<DataFlowNode*, long long>, DataFlowNode*> backward_dependencies;
375✔
556
        std::unordered_set<DataFlowNode*> visited;
375✔
557
        while (!queue.empty()) {
836✔
558
            auto* start = queue.front();
461✔
559
            queue.pop_front();
461✔
560

561
            if (visited.contains(start)) {
461✔
562
                continue;
35✔
563
            }
35✔
564

565
            // Reversed DFS
566
            lists.insert({start, {}});
426✔
567
            dependencies.insert({start, {}});
426✔
568
            std::stack<std::pair<DataFlowNode*, DataFlowNode*>> stack({{start, nullptr}});
426✔
569
            while (!stack.empty()) {
1,766✔
570
                auto* current = stack.top().first;
1,340✔
571
                auto* successor = stack.top().second;
1,340✔
572
                stack.pop();
1,340✔
573

574
                // If multiple out edges, add to queue, add dependency, and skip
575
                if (current != start && this->out_degree(*current) > 1) {
1,340✔
576
                    queue.push_back(current);
76✔
577
                    long long dependency_id = -1;
76✔
578
                    if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
76✔
579
                        for (auto& oedge : this->out_edges(*current)) {
12✔
580
                            auto* dst = &oedge.dst();
12✔
581
                            if (dst == successor) {
12✔
582
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
12✔
583
                                    if (oedge.src_conn() == code_node->output(j)) {
12✔
584
                                        dependency_id = j;
8✔
585
                                        break;
8✔
586
                                    }
8✔
587
                                }
12✔
588
                                break;
8✔
589
                            }
8✔
590
                        }
12✔
591
                    } else {
68✔
592
                        std::vector<std::pair<DataFlowNode*, size_t>> tmp_outputs;
68✔
593
                        std::unordered_set<DataFlowNode*> local_visited;
68✔
594
                        for (auto& oedge : this->out_edges(*current)) {
146✔
595
                            auto* dst = &oedge.dst();
146✔
596
                            if (local_visited.contains(dst)) {
146✔
597
                                continue;
10✔
598
                            }
10✔
599
                            local_visited.insert(dst);
136✔
600
                            size_t value = 0;
136✔
601
                            if (auto* tasklet = dynamic_cast<Tasklet*>(dst)) {
136✔
602
                                value = tasklet->code();
120✔
603
                            } else if (auto* libnode = dynamic_cast<LibraryNode*>(dst)) {
120✔
604
                                value = 52;
16✔
605
                                for (char c : libnode->code().value()) {
64✔
606
                                    value += c;
64✔
607
                                }
64✔
608
                            }
16✔
609
                            tmp_outputs.push_back({dst, value});
136✔
610
                        }
136✔
611
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
114✔
612
                            return a.second > b.second ||
114✔
613
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
114✔
614
                        });
114✔
615
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
102✔
616
                            if (tmp_outputs.at(j).first == successor) {
102✔
617
                                dependency_id = j;
68✔
618
                                break;
68✔
619
                            }
68✔
620
                        }
102✔
621
                    }
68✔
622
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
76✔
NEW
623
                        throw std::runtime_error("Could not create dependency in topological sort");
×
NEW
624
                    }
×
625
                    dependencies.at(start).push_front({current, dependency_id});
76✔
626
                    backward_dependencies.insert({{current, dependency_id}, start});
76✔
627
                    continue;
76✔
628
                }
76✔
629

630
                // Put the current element in the list
631
                if (visited.contains(current)) {
1,264✔
NEW
632
                    throw boost::not_a_dag();
×
NEW
633
                }
×
634
                visited.insert(current);
1,264✔
635
                lists.at(start).push_front(current);
1,264✔
636

637
                // Put all predecessors on the stack
638
                if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
1,264✔
639
                    std::unordered_set<DataFlowNode*> local_visited;
403✔
640
                    for (auto& input : code_node->inputs()) {
499✔
641
                        Memlet* iedge = nullptr;
499✔
642
                        for (auto& in_edge : this->in_edges(*code_node)) {
597✔
643
                            if (in_edge.dst_conn() == input) {
597✔
644
                                iedge = &in_edge;
461✔
645
                                break;
461✔
646
                            }
461✔
647
                        }
597✔
648
                        if (!iedge) {
499✔
649
                            continue;
38✔
650
                        }
38✔
651
                        auto* src = &iedge->src();
461✔
652
                        if (!local_visited.contains(src)) {
461✔
653
                            local_visited.insert(src);
451✔
654
                            stack.push({src, current});
451✔
655
                        }
451✔
656
                    }
461✔
657
                } else {
861✔
658
                    std::vector<std::pair<DataFlowNode*, size_t>> tmp_inputs;
861✔
659
                    std::unordered_set<DataFlowNode*> local_visited;
861✔
660
                    for (auto& iedge : this->in_edges(*current)) {
861✔
661
                        auto* src = &iedge.src();
463✔
662
                        if (local_visited.contains(src)) {
463✔
NEW
663
                            continue;
×
NEW
664
                        }
×
665
                        local_visited.insert(src);
463✔
666
                        size_t value = 0;
463✔
667
                        if (auto* tasklet = dynamic_cast<Tasklet*>(src)) {
463✔
668
                            value = tasklet->code();
373✔
669
                        } else if (auto* libnode = dynamic_cast<LibraryNode*>(src)) {
373✔
670
                            value = 52;
27✔
671
                            for (char c : libnode->code().value()) {
132✔
672
                                value += c;
132✔
673
                            }
132✔
674
                        }
27✔
675
                        tmp_inputs.push_back({src, value});
463✔
676
                    }
463✔
677
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
861✔
678
                        return a.second > b.second ||
25✔
679
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
25✔
680
                    });
25✔
681
                    for (auto& tmp_input : tmp_inputs) {
861✔
682
                        stack.push({tmp_input.first, current});
463✔
683
                    }
463✔
684
                }
861✔
685
            }
1,264✔
686
        }
426✔
687

688
        // Sort sinks if necessary
689
        if (sinks.size() > 1) {
375✔
690
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
15✔
691
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
15✔
692
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
15✔
693
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
15✔
694
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
15✔
695
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
15✔
696
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
15✔
697
                if (a_tasklet && b_tasklet) {
15✔
NEW
698
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
NEW
699
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
700
                } else if (a_tasklet && b_libnode) {
15✔
NEW
701
                    return true;
×
702
                } else if (a_tasklet && b_access_node) {
15✔
NEW
703
                    return true;
×
704
                } else if (a_libnode && b_libnode) {
15✔
NEW
705
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
NEW
706
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
NEW
707
                            a_libnode->element_id() < b_libnode->element_id());
×
708
                } else if (a_libnode && b_access_node) {
15✔
NEW
709
                    return true;
×
710
                } else if (a_access_node && b_access_node) {
15✔
711
                    return a_access_node->data() < b_access_node->data() ||
15✔
712
                           (a_access_node->data() == b_access_node->data() &&
15✔
713
                            a_access_node->element_id() < b_access_node->element_id());
6✔
714
                } else {
15✔
NEW
715
                    return false;
×
NEW
716
                }
×
717
            });
15✔
718
        }
8✔
719

720
        // Stich together by resolving dependencies
721
        visited.clear();
375✔
722
        for (auto* sink : sinks) {
385✔
723
            if (visited.contains(sink)) {
385✔
724
                continue;
2✔
725
            }
2✔
726

727
            std::stack<DataFlowNode*> stack({sink});
383✔
728
            while (!stack.empty()) {
852✔
729
                auto* current = stack.top();
469✔
730
                visited.insert(current);
469✔
731

732
                bool all_resolved = true;
469✔
733
                for (auto [node, dependency_id] : dependencies.at(current)) {
469✔
734
                    if (!visited.contains(node)) {
128✔
735
                        stack.push(node);
41✔
736
                        all_resolved = false;
41✔
737
                        break;
41✔
738
                    }
41✔
739

740
                    for (long long j = 0; j < dependency_id; j++) {
129✔
741
                        if (!backward_dependencies.contains({node, j})) {
44✔
NEW
742
                            continue;
×
NEW
743
                        }
×
744
                        auto* node2 = backward_dependencies.at({node, j});
44✔
745
                        if (!visited.contains(node2)) {
44✔
746
                            stack.push(node2);
2✔
747
                            all_resolved = false;
2✔
748
                            break;
2✔
749
                        }
2✔
750
                    }
44✔
751
                    if (!all_resolved) {
87✔
752
                        break;
2✔
753
                    }
2✔
754
                }
87✔
755
                if (!all_resolved) {
469✔
756
                    continue;
43✔
757
                }
43✔
758

759
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
426✔
760
                stack.pop();
426✔
761
            }
426✔
762
        }
383✔
763
    }
375✔
764

765
    // Sort components
766
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
659✔
767
        return a.size() > b.size() ||
9✔
768
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
9✔
769
    });
9✔
770

771
    // Resulting data structure
772
    std::list<DataFlowNode*> order;
659✔
773
    for (auto& component : components) {
659✔
774
        order.insert(order.end(), component.begin(), component.end());
375✔
775
    }
375✔
776

777
    return order;
659✔
778
}
659✔
779

780
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::dominators() const {
×
781
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
782
    for (auto& node : this->topological_sort()) {
×
783
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
784
            if (frontier.find(access_node->data()) == frontier.end()) {
×
785
                frontier[access_node->data()] = access_node;
×
786
            }
×
787
        }
×
788
    }
×
789

790
    return frontier;
×
791
};
×
792

793
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::post_dominators() const {
×
794
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
795
    for (auto& node : this->topological_sort()) {
×
796
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
797
            frontier[access_node->data()] = access_node;
×
798
        }
×
799
    }
×
800

801
    return frontier;
×
802
};
×
803

804
std::unordered_map<std::string, data_flow::AccessNode*> DataFlowGraph::post_dominators() {
3✔
805
    std::unordered_map<std::string, data_flow::AccessNode*> frontier;
3✔
806
    for (auto& node : this->topological_sort()) {
13✔
807
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
13✔
808
            frontier[access_node->data()] = access_node;
10✔
809
        }
10✔
810
    }
13✔
811

812
    return frontier;
3✔
813
};
3✔
814

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

818
    std::list<std::list<std::reference_wrapper<data_flow::Memlet>>> all_paths;
×
819
    for (auto& path_raw : all_paths_raw) {
×
820
        std::list<std::reference_wrapper<data_flow::Memlet>> path;
×
821
        for (auto& edge : path_raw) {
×
822
            path.push_back(*this->edges_.at(edge));
×
823
        }
×
824
        all_paths.push_back(path);
×
825
    }
×
826

827
    return all_paths;
×
828
};
×
829

830
const std::pair<size_t, const std::unordered_map<const data_flow::DataFlowNode*, size_t>> DataFlowGraph::
831
    weakly_connected_components() const {
8✔
832
    auto ccs_vertex = graph::weakly_connected_components(this->graph_);
8✔
833

834
    std::unordered_map<const data_flow::DataFlowNode*, size_t> ccs;
8✔
835
    for (auto& entry : ccs_vertex.second) {
41✔
836
        ccs[this->nodes_.at(entry.first).get()] = entry.second;
41✔
837
    }
41✔
838

839
    return {ccs_vertex.first, ccs};
8✔
840
};
8✔
841

842

843
} // namespace data_flow
844
} // 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