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

daisytuner / docc / 28755846449

05 Jul 2026 09:42PM UTC coverage: 62.469% (-0.006%) from 62.475%
28755846449

push

github

web-flow
Merge pull request #834 from daisytuner/check-topo-sort

adds topo-sort validity check in debug mode

38 of 62 new or added lines in 1 file covered. (61.29%)

2 existing lines in 1 file now uncovered.

39791 of 63697 relevant lines covered (62.47%)

979.72 hits per line

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

65.79
/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 <set>
9
#include <stdexcept>
10
#include <string>
11
#include <unordered_map>
12
#include <unordered_set>
13
#include <vector>
14

15
#include "sdfg/data_flow/access_node.h"
16
#include "sdfg/data_flow/code_node.h"
17
#include "sdfg/data_flow/data_flow_node.h"
18
#include "sdfg/data_flow/library_node.h"
19
#include "sdfg/data_flow/memlet.h"
20
#include "sdfg/graph/graph.h"
21

22
namespace sdfg {
23
namespace data_flow {
24

25
DataFlowGraph::DataFlowGraph(Element* parent) : parent_(parent) {}
3,933✔
26

27
void DataFlowGraph::validate(const Function& function) const {
6,478✔
28
    for (auto& node : this->nodes_) {
20,458✔
29
        node.second->validate(function);
20,458✔
30
        if (&node.second->get_parent() != this) {
20,458✔
31
            throw InvalidSDFGException("DataFlowGraph: Node parent mismatch.");
×
32
        }
×
33
    }
20,458✔
34
    for (auto& edge : this->edges_) {
14,841✔
35
        edge.second->validate(function);
14,841✔
36
    }
14,841✔
37
};
6,478✔
38

39
const Element* DataFlowGraph::get_parent() const { return this->parent_; };
6,471✔
40

41
Element* DataFlowGraph::get_parent() { return this->parent_; };
7,245✔
42

43
const data_flow::Memlet* DataFlowGraph::in_edge_for_connector(const data_flow::CodeNode& node, const std::string& conn)
44
    const {
3,013✔
45
    for (const auto& edge : this->in_edges(node)) {
5,453✔
46
        if (edge.dst_conn() == conn) {
5,453✔
47
            return &edge;
3,013✔
48
        }
3,013✔
49
    }
5,453✔
50
    return nullptr;
×
51
}
3,013✔
52

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

66
const data_flow::Memlet* DataFlowGraph::in_edge_if_single(const data_flow::AccessNode& node) const {
15✔
67
    auto edges = in_edges(node);
15✔
68
    auto it = edges.begin();
15✔
69
    if (it == edges.end()) {
15✔
70
        return nullptr;
3✔
71
    }
3✔
72
    const auto& edge = *it;
12✔
73
    if (++it != edges.end()) {
12✔
74
        return nullptr;
1✔
75
    }
1✔
76
    return &edge;
11✔
77
}
12✔
78

79
std::vector<data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) {
15✔
80
    std::vector<data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
15✔
81
    for (auto& iedge : this->in_edges(node)) {
65✔
82
        for (size_t i = 0; i < node.inputs().size(); i++) {
180✔
83
            if (iedge.dst_conn() == node.input(i)) {
180✔
84
                in_edges[i] = &iedge;
65✔
85
                break;
65✔
86
            }
65✔
87
        }
180✔
88
    }
65✔
89
    return in_edges;
15✔
90
}
15✔
91

92
std::vector<const data_flow::Memlet*> DataFlowGraph::in_edges_by_connector(const data_flow::CodeNode& node) const {
26✔
93
    std::vector<const data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
26✔
94
    for (const auto& iedge : this->in_edges(node)) {
38✔
95
        for (size_t i = 0; i < node.inputs().size(); i++) {
62✔
96
            if (iedge.dst_conn() == node.input(i)) {
62✔
97
                in_edges[i] = &iedge;
38✔
98
                break;
38✔
99
            }
38✔
100
        }
62✔
101
    }
38✔
102
    return in_edges;
26✔
103
}
26✔
104

105
std::vector<data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) {
×
106
    std::vector<data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
×
107
    for (auto& oedge : this->out_edges(node)) {
×
108
        for (size_t i = 0; i < node.outputs().size(); i++) {
×
109
            if (oedge.src_conn() == node.output(i)) {
×
110
                out_edges[i] = &oedge;
×
111
                break;
×
112
            }
×
113
        }
×
114
    }
×
115
    return out_edges;
×
116
}
×
117

118
std::vector<const data_flow::Memlet*> DataFlowGraph::out_edges_by_connector(const data_flow::CodeNode& node) const {
8✔
119
    std::vector<const data_flow::Memlet*> out_edges(node.outputs().size(), nullptr);
8✔
120
    for (const auto& oedge : this->out_edges(node)) {
8✔
121
        for (size_t i = 0; i < node.outputs().size(); i++) {
8✔
122
            if (oedge.src_conn() == node.output(i)) {
8✔
123
                out_edges[i] = &oedge;
8✔
124
                break;
8✔
125
            }
8✔
126
        }
8✔
127
    }
8✔
128
    return out_edges;
8✔
129
}
8✔
130

131
std::vector<const data_flow::Memlet*> DataFlowGraph::
132
    out_edges_for_connector(const data_flow::CodeNode& node, const std::string& conn) const {
50✔
133
    std::vector<const data_flow::Memlet*> outs;
50✔
134
    for (auto& edge : out_edges(node)) {
52✔
135
        if (edge.src_conn() == conn) {
52✔
136
            outs.push_back(&edge);
50✔
137
        }
50✔
138
    }
52✔
139
    return outs;
50✔
140
}
50✔
141

142
size_t DataFlowGraph::in_degree(const data_flow::DataFlowNode& node) const {
29,106✔
143
    return boost::in_degree(node.vertex(), this->graph_);
29,106✔
144
};
29,106✔
145

146
size_t DataFlowGraph::out_degree(const data_flow::DataFlowNode& node) const {
34,934✔
147
    return boost::out_degree(node.vertex(), this->graph_);
34,934✔
148
};
34,934✔
149

150
void DataFlowGraph::replace(const symbolic::Expression old_expression, const symbolic::Expression new_expression) {
417✔
151
    for (auto& node : this->nodes_) {
1,253✔
152
        node.second->replace(old_expression, new_expression);
1,253✔
153
    }
1,253✔
154

155
    for (auto& edge : this->edges_) {
873✔
156
        edge.second->replace(old_expression, new_expression);
873✔
157
    }
873✔
158
}
417✔
159

160
void DataFlowGraph::replace(const symbolic::ExpressionMapping& replacements) {
4✔
161
    for (auto& node : this->nodes_) {
14✔
162
        node.second->replace(replacements);
14✔
163
    }
14✔
164

165
    for (auto& edge : this->edges_) {
10✔
166
        edge.second->replace(replacements);
10✔
167
    }
10✔
168
}
4✔
169

170
/***** Section: Analysis *****/
171

172
std::unordered_set<const data_flow::Tasklet*> DataFlowGraph::tasklets() const {
×
173
    std::unordered_set<const data_flow::Tasklet*> ts;
×
174
    for (auto& node : this->nodes_) {
×
175
        if (auto tasklet = dynamic_cast<const data_flow::Tasklet*>(node.second.get())) {
×
176
            ts.insert(tasklet);
×
177
        }
×
178
    }
×
179

180
    return ts;
×
181
};
×
182

183
std::unordered_set<data_flow::Tasklet*> DataFlowGraph::tasklets() {
1,361✔
184
    std::unordered_set<data_flow::Tasklet*> ts;
1,361✔
185
    for (auto& node : this->nodes_) {
5,182✔
186
        if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node.second.get())) {
5,182✔
187
            ts.insert(tasklet);
801✔
188
        }
801✔
189
    }
5,182✔
190

191
    return ts;
1,361✔
192
};
1,361✔
193

194
std::unordered_set<const data_flow::LibraryNode*> DataFlowGraph::library_nodes() const {
×
195
    std::unordered_set<const data_flow::LibraryNode*> ls;
×
196
    for (auto& node : this->nodes_) {
×
197
        if (auto lib_node = dynamic_cast<const data_flow::LibraryNode*>(node.second.get())) {
×
198
            ls.insert(lib_node);
×
199
        }
×
200
    }
×
201

202
    return ls;
×
203
};
×
204

205
std::unordered_set<data_flow::LibraryNode*> DataFlowGraph::library_nodes() {
2,251✔
206
    std::unordered_set<data_flow::LibraryNode*> ls;
2,251✔
207
    for (auto& node : this->nodes_) {
7,957✔
208
        if (auto lib_node = dynamic_cast<data_flow::LibraryNode*>(node.second.get())) {
7,957✔
209
            ls.insert(lib_node);
1,449✔
210
        }
1,449✔
211
    }
7,957✔
212

213
    return ls;
2,251✔
214
};
2,251✔
215

216
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::data_nodes() const {
×
217
    std::unordered_set<const data_flow::AccessNode*> dnodes;
×
218
    for (auto& node : this->nodes_) {
×
219
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
220
            dnodes.insert(access_node);
×
221
        }
×
222
    }
×
223

224
    return dnodes;
×
225
};
×
226

227
std::unordered_set<data_flow::AccessNode*> DataFlowGraph::data_nodes() {
695✔
228
    std::unordered_set<data_flow::AccessNode*> dnodes;
695✔
229
    for (auto& node : this->nodes_) {
2,554✔
230
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node.second.get())) {
2,554✔
231
            dnodes.insert(access_node);
1,836✔
232
        }
1,836✔
233
    }
2,554✔
234

235
    return dnodes;
695✔
236
};
695✔
237

238
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::reads() const {
×
239
    std::unordered_set<const data_flow::AccessNode*> rs;
×
240
    for (auto& node : this->nodes_) {
×
241
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
242
            if (this->out_degree(*access_node) > 0) {
×
243
                rs.insert(access_node);
×
244
            }
×
245
        }
×
246
    }
×
247

248
    return rs;
×
249
};
×
250

251
std::unordered_set<const data_flow::AccessNode*> DataFlowGraph::writes() const {
×
252
    std::unordered_set<const data_flow::AccessNode*> ws;
×
253
    for (auto& node : this->nodes_) {
×
254
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node.second.get())) {
×
255
            if (this->in_degree(*access_node) > 0) {
×
256
                ws.insert(access_node);
×
257
            }
×
258
        }
×
259
    }
×
260

261
    return ws;
×
262
};
×
263

264
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sources() const {
×
265
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
266
    for (auto& node : this->nodes_) {
×
267
        if (this->in_degree(*node.second) == 0) {
×
268
            ss.insert(node.second.get());
×
269
        }
×
270
    }
×
271

272
    return ss;
×
273
};
×
274

275
std::unordered_set<data_flow::DataFlowNode*> DataFlowGraph::sources() {
4✔
276
    std::unordered_set<data_flow::DataFlowNode*> ss;
4✔
277
    for (auto& node : this->nodes_) {
17✔
278
        if (this->in_degree(*node.second) == 0) {
17✔
279
            ss.insert(node.second.get());
9✔
280
        }
9✔
281
    }
17✔
282

283
    return ss;
4✔
284
};
4✔
285

286
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::sinks() const {
×
287
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
288
    for (auto& node : this->nodes_) {
×
289
        if (this->out_degree(*node.second) == 0) {
×
290
            ss.insert(node.second.get());
×
291
        }
×
292
    }
×
293

294
    return ss;
×
295
};
×
296

297
std::unordered_set<data_flow::DataFlowNode*> DataFlowGraph::sinks() {
10✔
298
    std::unordered_set<data_flow::DataFlowNode*> ss;
10✔
299
    for (auto& node : this->nodes_) {
67✔
300
        if (this->out_degree(*node.second) == 0) {
67✔
301
            ss.insert(node.second.get());
16✔
302
        }
16✔
303
    }
67✔
304

305
    return ss;
10✔
306
};
10✔
307

308
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::predecessors(const data_flow::DataFlowNode& node
309
) const {
×
310
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
311
    for (auto& edge : this->in_edges(node)) {
×
312
        ss.insert(&edge.src());
×
313
    }
×
314

315
    return ss;
×
316
};
×
317

318
std::unordered_set<const data_flow::DataFlowNode*> DataFlowGraph::successors(const data_flow::DataFlowNode& node
319
) const {
×
320
    std::unordered_set<const data_flow::DataFlowNode*> ss;
×
321
    for (auto& edge : this->out_edges(node)) {
×
322
        ss.insert(&edge.dst());
×
323
    }
×
324

325
    return ss;
×
326
};
×
327

328
bool DataFlowGraph::is_valid_topological_order(const std::list<const DataFlowNode*>& order) const {
37✔
329
    std::unordered_map<const DataFlowNode*, size_t> pos;
37✔
330
    size_t idx = 0;
37✔
331
    for (const auto* node : order) {
106✔
332
        pos[node] = idx++;
106✔
333
    }
106✔
334
    for (const auto& edge : this->edges()) {
70✔
335
        auto src_it = pos.find(&edge.src());
70✔
336
        auto dst_it = pos.find(&edge.dst());
70✔
337
        if (src_it == pos.end() || dst_it == pos.end() || src_it->second >= dst_it->second) {
70✔
NEW
338
            return false;
×
NEW
339
        }
×
340
    }
70✔
341
    return true;
37✔
342
}
37✔
343

344
bool DataFlowGraph::is_valid_topological_order(const std::list<DataFlowNode*>& order) const {
3,622✔
345
    std::unordered_map<const DataFlowNode*, size_t> pos;
3,622✔
346
    size_t idx = 0;
3,622✔
347
    for (const auto* node : order) {
11,118✔
348
        pos[node] = idx++;
11,118✔
349
    }
11,118✔
350
    for (const auto& edge : this->edges()) {
8,241✔
351
        auto src_it = pos.find(&edge.src());
8,241✔
352
        auto dst_it = pos.find(&edge.dst());
8,241✔
353
        if (src_it == pos.end() || dst_it == pos.end() || src_it->second >= dst_it->second) {
8,241✔
NEW
354
            return false;
×
NEW
355
        }
×
356
    }
8,241✔
357
    return true;
3,622✔
358
}
3,622✔
359

NEW
360
std::list<DataFlowNode*> DataFlowGraph::boost_topological_sort() {
×
NEW
361
    auto order_vertices = graph::topological_sort(this->graph_);
×
362

NEW
363
    std::list<DataFlowNode*> order;
×
NEW
364
    for (const auto& v : order_vertices) {
×
NEW
365
        order.push_back(this->nodes_.at(v).get());
×
NEW
366
    }
×
NEW
367
    return order;
×
NEW
368
}
×
369

NEW
370
std::list<const DataFlowNode*> DataFlowGraph::boost_topological_sort() const {
×
NEW
371
    auto order_vertices = graph::topological_sort(this->graph_);
×
372

NEW
373
    std::list<const DataFlowNode*> order;
×
NEW
374
    for (const auto& v : order_vertices) {
×
NEW
375
        order.push_back(this->nodes_.at(v).get());
×
NEW
376
    }
×
NEW
377
    return order;
×
NEW
378
}
×
379

380
std::list<const DataFlowNode*> DataFlowGraph::semantic_topological_sort() const {
37✔
381
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
37✔
382

383
    // Build deterministic topological sort for each weakly connected component
384
    std::vector<std::list<const DataFlowNode*>> components(num_components);
37✔
385
    for (size_t i = 0; i < num_components; i++) {
74✔
386
        // Get all sinks of the current component
387
        std::vector<const DataFlowNode*> sinks;
37✔
388
        bool component_empty = true;
37✔
389
        for (auto [v, comp] : components_map) {
106✔
390
            if (comp == i) {
106✔
391
                component_empty = false;
106✔
392
                if (boost::out_degree(v, this->graph_) == 0) {
106✔
393
                    sinks.push_back(this->nodes_.at(v).get());
37✔
394
                }
37✔
395
            }
106✔
396
        }
106✔
397
        if (sinks.size() == 0) {
37✔
398
            if (component_empty) {
×
399
                continue;
×
400
            } else {
×
401
                throw boost::not_a_dag();
×
402
            }
×
403
        }
×
404

405
        // Create a queue with all sinks
406
        std::list<const DataFlowNode*> queue;
37✔
407
        queue.insert(queue.end(), sinks.begin(), sinks.end());
37✔
408

409
        // Perform a reversed DFS for each element in the queue
410
        std::unordered_map<const DataFlowNode*, std::list<const DataFlowNode*>> lists;
37✔
411
        std::unordered_map<const DataFlowNode*, std::list<std::pair<const DataFlowNode*, long long>>> dependencies;
37✔
412
        std::map<std::pair<const DataFlowNode*, long long>, const DataFlowNode*> backward_dependencies;
37✔
413
        std::unordered_set<const DataFlowNode*> visited;
37✔
414
        while (!queue.empty()) {
75✔
415
            const auto* start = queue.front();
38✔
416
            queue.pop_front();
38✔
417

418
            if (visited.contains(start)) {
38✔
419
                continue;
×
420
            }
×
421

422
            // Reversed DFS
423
            lists.insert({start, {}});
38✔
424
            dependencies.insert({start, {}});
38✔
425
            std::stack<std::pair<const DataFlowNode*, const DataFlowNode*>> stack({{start, nullptr}});
38✔
426
            while (!stack.empty()) {
145✔
427
                const auto* current = stack.top().first;
107✔
428
                const auto* successor = stack.top().second;
107✔
429
                stack.pop();
107✔
430

431
                // If multiple out edges, add to queue, add dependency, and skip
432
                if (current != start && this->out_degree(*current) > 1) {
107✔
433
                    queue.push_back(current);
1✔
434
                    long long dependency_id = -1;
1✔
435
                    if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
1✔
436
                        for (const auto& oedge : this->out_edges(*current)) {
×
437
                            const auto* dst = &oedge.dst();
×
438
                            if (dst == successor) {
×
439
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
×
440
                                    if (oedge.src_conn() == code_node->output(j)) {
×
441
                                        dependency_id = j;
×
442
                                        break;
×
443
                                    }
×
444
                                }
×
445
                                break;
×
446
                            }
×
447
                        }
×
448
                    } else {
1✔
449
                        std::vector<std::pair<const DataFlowNode*, size_t>> tmp_outputs;
1✔
450
                        std::unordered_set<const DataFlowNode*> local_visited;
1✔
451
                        for (const auto& oedge : this->out_edges(*current)) {
2✔
452
                            const auto* dst = &oedge.dst();
2✔
453
                            if (local_visited.contains(dst)) {
2✔
454
                                continue;
1✔
455
                            }
1✔
456
                            local_visited.insert(dst);
1✔
457
                            size_t value = 0;
1✔
458
                            if (const auto* tasklet = dynamic_cast<const Tasklet*>(dst)) {
1✔
459
                                value = tasklet->code();
1✔
460
                            } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(dst)) {
1✔
461
                                value = 52;
×
462
                                for (char c : libnode->code().value()) {
×
463
                                    value += c;
×
464
                                }
×
465
                            }
×
466
                            tmp_outputs.push_back({dst, value});
1✔
467
                        }
1✔
468
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
1✔
469
                            return a.second > b.second ||
×
470
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
×
471
                        });
×
472
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
1✔
473
                            if (tmp_outputs.at(j).first == successor) {
1✔
474
                                dependency_id = j;
1✔
475
                                break;
1✔
476
                            }
1✔
477
                        }
1✔
478
                    }
1✔
479
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
1✔
480
                        throw std::runtime_error("Could not create dependency in topological sort");
×
481
                    }
×
482
                    dependencies.at(start).push_front({current, dependency_id});
1✔
483
                    backward_dependencies.insert({{current, dependency_id}, start});
1✔
484
                    continue;
1✔
485
                }
1✔
486

487
                // Put the current element in the list
488
                if (visited.contains(current)) {
106✔
489
                    throw boost::not_a_dag();
×
490
                }
×
491
                visited.insert(current);
106✔
492
                lists.at(start).push_front(current);
106✔
493

494
                // Put all predecessors on the stack
495
                if (const auto* code_node = dynamic_cast<const CodeNode*>(current)) {
106✔
496
                    std::unordered_set<const DataFlowNode*> local_visited;
32✔
497
                    for (const auto& input : code_node->inputs()) {
43✔
498
                        const Memlet* iedge = nullptr;
43✔
499
                        for (const auto& in_edge : this->in_edges(*code_node)) {
45✔
500
                            if (in_edge.dst_conn() == input) {
45✔
501
                                iedge = &in_edge;
33✔
502
                                break;
33✔
503
                            }
33✔
504
                        }
45✔
505
                        if (!iedge) {
43✔
506
                            continue;
10✔
507
                        }
10✔
508
                        const auto* src = &iedge->src();
33✔
509
                        if (!local_visited.contains(src)) {
33✔
510
                            local_visited.insert(src);
32✔
511
                            stack.push({src, current});
32✔
512
                        }
32✔
513
                    }
33✔
514
                } else {
74✔
515
                    std::vector<std::pair<const DataFlowNode*, size_t>> tmp_inputs;
74✔
516
                    std::unordered_set<const DataFlowNode*> local_visited;
74✔
517
                    for (const auto& iedge : this->in_edges(*current)) {
74✔
518
                        const auto* src = &iedge.src();
37✔
519
                        if (local_visited.contains(src)) {
37✔
520
                            continue;
×
521
                        }
×
522
                        local_visited.insert(src);
37✔
523
                        size_t value = 0;
37✔
524
                        if (const auto* tasklet = dynamic_cast<const Tasklet*>(src)) {
37✔
525
                            value = tasklet->code();
26✔
526
                        } else if (const auto* libnode = dynamic_cast<const LibraryNode*>(src)) {
26✔
527
                            value = 52;
4✔
528
                            for (char c : libnode->code().value()) {
38✔
529
                                value += c;
38✔
530
                            }
38✔
531
                        }
4✔
532
                        tmp_inputs.push_back({src, value});
37✔
533
                    }
37✔
534
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
74✔
535
                        return a.second > b.second ||
×
536
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
×
537
                    });
×
538
                    for (auto& tmp_input : tmp_inputs) {
74✔
539
                        stack.push({tmp_input.first, current});
37✔
540
                    }
37✔
541
                }
74✔
542
            }
106✔
543
        }
38✔
544

545
        // Sort sinks if necessary
546
        if (sinks.size() > 1) {
37✔
547
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
×
548
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
×
549
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
×
550
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
×
551
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
×
552
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
×
553
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
×
554
                if (a_tasklet && b_tasklet) {
×
555
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
556
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
557
                } else if (a_tasklet && b_libnode) {
×
558
                    return true;
×
559
                } else if (a_tasklet && b_access_node) {
×
560
                    return true;
×
561
                } else if (a_libnode && b_libnode) {
×
562
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
563
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
564
                            a_libnode->element_id() < b_libnode->element_id());
×
565
                } else if (a_libnode && b_access_node) {
×
566
                    return true;
×
567
                } else if (a_access_node && b_access_node) {
×
568
                    return a_access_node->data() < b_access_node->data() ||
×
569
                           (a_access_node->data() == b_access_node->data() &&
×
570
                            a_access_node->element_id() < b_access_node->element_id());
×
571
                } else {
×
572
                    return false;
×
573
                }
×
574
            });
×
575
        }
×
576

577
        // Stich together by resolving dependencies
578
        visited.clear();
37✔
579
        for (const auto* sink : sinks) {
37✔
580
            if (visited.contains(sink)) {
37✔
581
                continue;
×
582
            }
×
583

584
            std::stack<const DataFlowNode*> stack({sink});
37✔
585
            while (!stack.empty()) {
76✔
586
                const auto* current = stack.top();
39✔
587
                visited.insert(current);
39✔
588

589
                bool all_resolved = true;
39✔
590
                for (auto [node, dependency_id] : dependencies.at(current)) {
39✔
591
                    if (!visited.contains(node)) {
2✔
592
                        stack.push(node);
1✔
593
                        all_resolved = false;
1✔
594
                        break;
1✔
595
                    }
1✔
596

597
                    for (long long j = 0; j < dependency_id; j++) {
1✔
598
                        if (!backward_dependencies.contains({node, j})) {
×
599
                            continue;
×
600
                        }
×
601
                        const auto* node2 = backward_dependencies.at({node, j});
×
602
                        if (!visited.contains(node2)) {
×
603
                            stack.push(node2);
×
604
                            all_resolved = false;
×
605
                            break;
×
606
                        }
×
607
                    }
×
608
                    if (!all_resolved) {
1✔
609
                        break;
×
610
                    }
×
611
                }
1✔
612
                if (!all_resolved) {
39✔
613
                    continue;
1✔
614
                }
1✔
615

616
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
38✔
617
                stack.pop();
38✔
618
            }
38✔
619
        }
37✔
620
    }
37✔
621

622
    // Sort components
623
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
37✔
624
        return a.size() > b.size() ||
×
625
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
×
626
    });
×
627

628
    // Resulting data structure
629
    std::list<const DataFlowNode*> order;
37✔
630
    for (auto& component : components) {
37✔
631
        order.insert(order.end(), component.begin(), component.end());
37✔
632
    }
37✔
633

634
    return order;
37✔
635
}
37✔
636

637
std::list<DataFlowNode*> DataFlowGraph::semantic_topological_sort() {
3,622✔
638
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
3,622✔
639

640
    // Build deterministic topological sort for each weakly connected component
641
    std::vector<std::list<DataFlowNode*>> components(num_components);
3,622✔
642
    for (size_t i = 0; i < num_components; i++) {
6,783✔
643
        // Get all sinks of the current component
644
        std::vector<DataFlowNode*> sinks;
3,161✔
645
        bool component_empty = true;
3,161✔
646
        for (auto [v, comp] : components_map) {
11,316✔
647
            if (comp == i) {
11,316✔
648
                component_empty = false;
11,118✔
649
                if (boost::out_degree(v, this->graph_) == 0) {
11,118✔
650
                    sinks.push_back(this->nodes_.at(v).get());
3,211✔
651
                }
3,211✔
652
            }
11,118✔
653
        }
11,316✔
654
        if (sinks.size() == 0) {
3,161✔
655
            if (component_empty) {
×
656
                continue;
×
657
            } else {
×
658
                throw boost::not_a_dag();
×
659
            }
×
660
        }
×
661

662
        // Create a queue with all sinks
663
        std::list<DataFlowNode*> queue;
3,161✔
664
        queue.insert(queue.end(), sinks.begin(), sinks.end());
3,161✔
665

666
        // Perform a reversed DFS for each element in the queue
667
        std::unordered_map<DataFlowNode*, std::list<DataFlowNode*>> lists;
3,161✔
668
        std::unordered_map<DataFlowNode*, std::list<std::pair<DataFlowNode*, long long>>> dependencies;
3,161✔
669
        std::map<std::pair<DataFlowNode*, long long>, DataFlowNode*> backward_dependencies;
3,161✔
670
        std::unordered_set<DataFlowNode*> visited;
3,161✔
671
        while (!queue.empty()) {
6,805✔
672
            auto* start = queue.front();
3,644✔
673
            queue.pop_front();
3,644✔
674

675
            if (visited.contains(start)) {
3,644✔
676
                continue;
103✔
677
            }
103✔
678

679
            // Reversed DFS
680
            lists.insert({start, {}});
3,541✔
681
            dependencies.insert({start, {}});
3,541✔
682
            std::stack<std::pair<DataFlowNode*, DataFlowNode*>> stack({{start, nullptr}});
3,541✔
683
            while (!stack.empty()) {
15,092✔
684
                auto* current = stack.top().first;
11,551✔
685
                auto* successor = stack.top().second;
11,551✔
686
                stack.pop();
11,551✔
687

688
                // If multiple out edges, add to queue, add dependency, and skip
689
                if (current != start && this->out_degree(*current) > 1) {
11,551✔
690
                    queue.push_back(current);
433✔
691
                    long long dependency_id = -1;
433✔
692
                    if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
433✔
693
                        for (auto& oedge : this->out_edges(*current)) {
21✔
694
                            auto* dst = &oedge.dst();
21✔
695
                            if (dst == successor) {
21✔
696
                                for (long long j = 0; j < code_node->outputs().size(); j++) {
21✔
697
                                    if (oedge.src_conn() == code_node->output(j)) {
21✔
698
                                        dependency_id = j;
14✔
699
                                        break;
14✔
700
                                    }
14✔
701
                                }
21✔
702
                                break;
14✔
703
                            }
14✔
704
                        }
21✔
705
                    } else {
419✔
706
                        std::vector<std::pair<DataFlowNode*, size_t>> tmp_outputs;
419✔
707
                        std::unordered_set<DataFlowNode*> local_visited;
419✔
708
                        for (auto& oedge : this->out_edges(*current)) {
848✔
709
                            auto* dst = &oedge.dst();
848✔
710
                            if (local_visited.contains(dst)) {
848✔
711
                                continue;
231✔
712
                            }
231✔
713
                            local_visited.insert(dst);
617✔
714
                            size_t value = 0;
617✔
715
                            if (auto* tasklet = dynamic_cast<Tasklet*>(dst)) {
617✔
716
                                value = tasklet->code();
600✔
717
                            } else if (auto* libnode = dynamic_cast<LibraryNode*>(dst)) {
600✔
718
                                value = 52;
17✔
719
                                for (char c : libnode->code().value()) {
72✔
720
                                    value += c;
72✔
721
                                }
72✔
722
                            }
17✔
723
                            tmp_outputs.push_back({dst, value});
617✔
724
                        }
617✔
725
                        std::sort(tmp_outputs.begin(), tmp_outputs.end(), [](const auto& a, const auto& b) {
419✔
726
                            return a.second > b.second ||
356✔
727
                                   (a.second == b.second && a.first->element_id() < b.first->element_id());
356✔
728
                        });
356✔
729
                        for (long long j = 0; j < tmp_outputs.size(); j++) {
518✔
730
                            if (tmp_outputs.at(j).first == successor) {
518✔
731
                                dependency_id = j;
419✔
732
                                break;
419✔
733
                            }
419✔
734
                        }
518✔
735
                    }
419✔
736
                    if (dependency_id == -1 || backward_dependencies.contains({current, dependency_id})) {
433✔
737
                        throw std::runtime_error("Could not create dependency in topological sort");
×
738
                    }
×
739
                    dependencies.at(start).push_front({current, dependency_id});
433✔
740
                    backward_dependencies.insert({{current, dependency_id}, start});
433✔
741
                    continue;
433✔
742
                }
433✔
743

744
                // Put the current element in the list
745
                if (visited.contains(current)) {
11,118✔
746
                    throw boost::not_a_dag();
×
747
                }
×
748
                visited.insert(current);
11,118✔
749
                lists.at(start).push_front(current);
11,118✔
750

751
                // Put all predecessors on the stack
752
                if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
11,118✔
753
                    std::unordered_set<DataFlowNode*> local_visited;
3,302✔
754
                    for (auto& input : code_node->inputs()) {
5,140✔
755
                        Memlet* iedge = nullptr;
5,140✔
756
                        for (auto& in_edge : this->in_edges(*code_node)) {
7,881✔
757
                            if (in_edge.dst_conn() == input) {
7,881✔
758
                                iedge = &in_edge;
5,103✔
759
                                break;
5,103✔
760
                            }
5,103✔
761
                        }
7,881✔
762
                        if (!iedge) {
5,140✔
763
                            continue;
37✔
764
                        }
37✔
765
                        auto* src = &iedge->src();
5,103✔
766
                        if (!local_visited.contains(src)) {
5,103✔
767
                            local_visited.insert(src);
4,872✔
768
                            stack.push({src, current});
4,872✔
769
                        }
4,872✔
770
                    }
5,103✔
771
                } else {
7,816✔
772
                    std::vector<std::pair<DataFlowNode*, size_t>> tmp_inputs;
7,816✔
773
                    std::unordered_set<DataFlowNode*> local_visited;
7,816✔
774
                    for (auto& iedge : this->in_edges(*current)) {
7,816✔
775
                        auto* src = &iedge.src();
3,138✔
776
                        if (local_visited.contains(src)) {
3,138✔
777
                            continue;
×
778
                        }
×
779
                        local_visited.insert(src);
3,138✔
780
                        size_t value = 0;
3,138✔
781
                        if (auto* tasklet = dynamic_cast<Tasklet*>(src)) {
3,138✔
782
                            value = tasklet->code();
2,734✔
783
                        } else if (auto* libnode = dynamic_cast<LibraryNode*>(src)) {
2,734✔
784
                            value = 52;
280✔
785
                            for (char c : libnode->code().value()) {
2,649✔
786
                                value += c;
2,649✔
787
                            }
2,649✔
788
                        }
280✔
789
                        tmp_inputs.push_back({src, value});
3,138✔
790
                    }
3,138✔
791
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
7,816✔
792
                        return a.second > b.second ||
30✔
793
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
30✔
794
                    });
30✔
795
                    for (auto& tmp_input : tmp_inputs) {
7,816✔
796
                        stack.push({tmp_input.first, current});
3,138✔
797
                    }
3,138✔
798
                }
7,816✔
799
            }
11,118✔
800
        }
3,541✔
801

802
        // Sort sinks if necessary
803
        if (sinks.size() > 1) {
3,161✔
804
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
86✔
805
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
86✔
806
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
86✔
807
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
86✔
808
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
86✔
809
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
86✔
810
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
86✔
811
                if (a_tasklet && b_tasklet) {
86✔
812
                    return a_tasklet->code() < b_tasklet->code() || (a_tasklet->code() == b_tasklet->code() &&
×
813
                                                                     a_tasklet->element_id() < b_tasklet->element_id());
×
814
                } else if (a_tasklet && b_libnode) {
86✔
815
                    return true;
×
816
                } else if (a_tasklet && b_access_node) {
86✔
817
                    return true;
×
818
                } else if (a_libnode && b_libnode) {
86✔
819
                    return a_libnode->code().value() < b_libnode->code().value() ||
×
820
                           (a_libnode->code().value() == b_libnode->code().value() &&
×
821
                            a_libnode->element_id() < b_libnode->element_id());
×
822
                } else if (a_libnode && b_access_node) {
86✔
823
                    return true;
×
824
                } else if (a_access_node && b_access_node) {
86✔
825
                    return a_access_node->data() < b_access_node->data() ||
86✔
826
                           (a_access_node->data() == b_access_node->data() &&
86✔
827
                            a_access_node->element_id() < b_access_node->element_id());
68✔
828
                } else {
86✔
829
                    return false;
×
830
                }
×
831
            });
86✔
832
        }
44✔
833

834
        // Stich together by resolving dependencies
835
        visited.clear();
3,161✔
836
        for (auto* sink : sinks) {
3,211✔
837
            if (visited.contains(sink)) {
3,211✔
838
                continue;
34✔
839
            }
34✔
840

841
            std::stack<DataFlowNode*> stack({sink});
3,177✔
842
            while (!stack.empty()) {
7,082✔
843
                auto* current = stack.top();
3,905✔
844
                visited.insert(current);
3,905✔
845

846
                bool all_resolved = true;
3,905✔
847
                for (auto [node, dependency_id] : dependencies.at(current)) {
3,905✔
848
                    if (!visited.contains(node)) {
834✔
849
                        stack.push(node);
330✔
850
                        all_resolved = false;
330✔
851
                        break;
330✔
852
                    }
330✔
853

854
                    for (long long j = 0; j < dependency_id; j++) {
614✔
855
                        if (!backward_dependencies.contains({node, j})) {
144✔
856
                            continue;
×
857
                        }
×
858
                        auto* node2 = backward_dependencies.at({node, j});
144✔
859
                        if (!visited.contains(node2)) {
144✔
860
                            stack.push(node2);
34✔
861
                            all_resolved = false;
34✔
862
                            break;
34✔
863
                        }
34✔
864
                    }
144✔
865
                    if (!all_resolved) {
504✔
866
                        break;
34✔
867
                    }
34✔
868
                }
504✔
869
                if (!all_resolved) {
3,905✔
870
                    continue;
364✔
871
                }
364✔
872

873
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
3,541✔
874
                stack.pop();
3,541✔
875
            }
3,541✔
876
        }
3,177✔
877
    }
3,161✔
878

879
    // Sort components
880
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
3,622✔
881
        return a.size() > b.size() ||
44✔
882
               (a.size() == b.size() && a.size() > 0 && a.front()->element_id() < b.front()->element_id());
44✔
883
    });
44✔
884

885
    // Resulting data structure
886
    std::list<DataFlowNode*> order;
3,622✔
887
    for (auto& component : components) {
3,622✔
888
        order.insert(order.end(), component.begin(), component.end());
3,161✔
889
    }
3,161✔
890

891
    return order;
3,622✔
892
}
3,622✔
893

894
std::list<const DataFlowNode*> DataFlowGraph::topological_sort() const {
37✔
895
    auto order = semantic_topological_sort();
37✔
896
    if (!is_valid_topological_order(order)) {
37✔
NEW
897
        order = boost_topological_sort();
×
NEW
898
    }
×
899
    return order;
37✔
900
}
37✔
901

902
std::list<DataFlowNode*> DataFlowGraph::topological_sort() {
3,622✔
903
    auto order = semantic_topological_sort();
3,622✔
904
    if (!is_valid_topological_order(order)) {
3,622✔
NEW
905
        order = boost_topological_sort();
×
NEW
906
    }
×
907
    return order;
3,622✔
908
}
3,622✔
909

910
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::dominators() const {
×
911
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
912
    for (auto& node : this->topological_sort()) {
×
913
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
914
            if (frontier.find(access_node->data()) == frontier.end()) {
×
915
                frontier[access_node->data()] = access_node;
×
916
            }
×
917
        }
×
918
    }
×
919

920
    return frontier;
×
921
};
×
922

923
std::unordered_map<std::string, const data_flow::AccessNode*> DataFlowGraph::post_dominators() const {
×
924
    std::unordered_map<std::string, const data_flow::AccessNode*> frontier;
×
925
    for (auto& node : this->topological_sort()) {
×
926
        if (auto access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
×
927
            frontier[access_node->data()] = access_node;
×
928
        }
×
929
    }
×
930

931
    return frontier;
×
932
};
×
933

934
std::unordered_map<std::string, data_flow::AccessNode*> DataFlowGraph::post_dominators() {
4✔
935
    std::unordered_map<std::string, data_flow::AccessNode*> frontier;
4✔
936
    for (auto& node : this->topological_sort()) {
20✔
937
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
20✔
938
            frontier[access_node->data()] = access_node;
15✔
939
        }
15✔
940
    }
20✔
941

942
    return frontier;
4✔
943
};
4✔
944

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

948
    std::list<std::list<std::reference_wrapper<data_flow::Memlet>>> all_paths;
×
949
    for (auto& path_raw : all_paths_raw) {
×
950
        std::list<std::reference_wrapper<data_flow::Memlet>> path;
×
951
        for (auto& edge : path_raw) {
×
952
            path.push_back(*this->edges_.at(edge));
×
953
        }
×
954
        all_paths.push_back(path);
×
955
    }
×
956

957
    return all_paths;
×
958
};
×
959

960
const std::pair<size_t, const std::unordered_map<const data_flow::DataFlowNode*, size_t>> DataFlowGraph::
961
    weakly_connected_components() const {
9✔
962
    auto ccs_vertex = graph::weakly_connected_components(this->graph_);
9✔
963

964
    std::unordered_map<const data_flow::DataFlowNode*, size_t> ccs;
9✔
965
    for (auto& entry : ccs_vertex.second) {
47✔
966
        ccs[this->nodes_.at(entry.first).get()] = entry.second;
47✔
967
    }
47✔
968

969
    return {ccs_vertex.first, ccs};
9✔
970
}
9✔
971

972
const AccessNode* DataFlowGraph::find_standalone_entry(const Memlet* input_edge) const {
1,367✔
973
    if (!input_edge) {
1,367✔
974
        return nullptr;
×
975
    }
×
976
    auto* access_node = dynamic_cast<const data_flow::AccessNode*>(&input_edge->src());
1,367✔
977
    if (access_node && in_degree(*access_node) == 0) {
1,367✔
978
        return access_node;
1,367✔
979
    } else {
1,367✔
980
        return nullptr;
×
981
    }
×
982
}
1,367✔
983

984
const AccessNode* DataFlowGraph::find_standalone_exit(const Memlet* output_edge) const {
×
985
    if (!output_edge) {
×
986
        return nullptr;
×
987
    }
×
988
    auto* access_node = dynamic_cast<const data_flow::AccessNode*>(&output_edge->dst());
×
989
    if (access_node && out_degree(*access_node) == 0) {
×
990
        return access_node;
×
991
    } else {
×
992
        return nullptr;
×
993
    }
×
994
}
×
995

996

997
} // namespace data_flow
998
} // 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