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

daisytuner / docc / 27004850786

05 Jun 2026 08:40AM UTC coverage: 61.275%. First build
27004850786

Pull #736

github

web-flow
Merge a056669a3 into 75707c995
Pull Request #736: Improve Quantization support on TensorNodes

10 of 43 new or added lines in 8 files covered. (23.26%)

35592 of 58086 relevant lines covered (61.27%)

11015.05 hits per line

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

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

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

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

38
const data_flow::Memlet* DataFlowGraph::in_edge_for_connector(const data_flow::CodeNode& node, const std::string& conn)
39
    const {
2,956✔
40
    for (const auto& edge : this->in_edges(node)) {
5,331✔
41
        if (edge.dst_conn() == conn) {
5,331✔
42
            return &edge;
2,956✔
43
        }
2,956✔
44
    }
5,331✔
45
    return nullptr;
×
46
}
2,956✔
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) {
5✔
62
    std::vector<data_flow::Memlet*> in_edges(node.inputs().size(), nullptr);
5✔
63
    for (auto& iedge : this->in_edges(node)) {
15✔
64
        for (size_t i = 0; i < node.inputs().size(); i++) {
30✔
65
            if (iedge.dst_conn() == node.input(i)) {
30✔
66
                in_edges[i] = &iedge;
15✔
67
                break;
15✔
68
            }
15✔
69
        }
30✔
70
    }
15✔
71
    return in_edges;
5✔
72
}
5✔
73

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

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

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

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

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

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

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

137
    for (auto& edge : this->edges_) {
604✔
138
        edge.second->replace(old_expression, new_expression);
604✔
139
    }
604✔
140
};
221✔
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,342✔
156
    std::unordered_set<data_flow::Tasklet*> ts;
1,342✔
157
    for (auto& node : this->nodes_) {
5,112✔
158
        if (auto tasklet = dynamic_cast<data_flow::Tasklet*>(node.second.get())) {
5,112✔
159
            ts.insert(tasklet);
778✔
160
        }
778✔
161
    }
5,112✔
162

163
    return ts;
1,342✔
164
};
1,342✔
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() {
2,146✔
178
    std::unordered_set<data_flow::LibraryNode*> ls;
2,146✔
179
    for (auto& node : this->nodes_) {
7,613✔
180
        if (auto lib_node = dynamic_cast<data_flow::LibraryNode*>(node.second.get())) {
7,613✔
181
            ls.insert(lib_node);
1,404✔
182
        }
1,404✔
183
    }
7,613✔
184

185
    return ls;
2,146✔
186
};
2,146✔
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() {
643✔
200
    std::unordered_set<data_flow::AccessNode*> dnodes;
643✔
201
    for (auto& node : this->nodes_) {
2,363✔
202
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node.second.get())) {
2,363✔
203
            dnodes.insert(access_node);
1,708✔
204
        }
1,708✔
205
    }
2,363✔
206

207
    return dnodes;
643✔
208
};
643✔
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() {
2✔
248
    std::unordered_set<data_flow::DataFlowNode*> ss;
2✔
249
    for (auto& node : this->nodes_) {
10✔
250
        if (this->in_degree(*node.second) == 0) {
10✔
251
            ss.insert(node.second.get());
6✔
252
        }
6✔
253
    }
10✔
254

255
    return ss;
2✔
256
};
2✔
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 {
45✔
301
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
45✔
302

303
    // Build deterministic topological sort for each weakly connected component
304
    std::vector<std::list<const DataFlowNode*>> components(num_components);
45✔
305
    for (size_t i = 0; i < num_components; i++) {
91✔
306
        // Get all sinks of the current component
307
        std::vector<const DataFlowNode*> sinks;
46✔
308
        bool component_empty = true;
46✔
309
        for (auto [v, comp] : components_map) {
139✔
310
            if (comp == i) {
139✔
311
                component_empty = false;
133✔
312
                if (boost::out_degree(v, this->graph_) == 0) {
133✔
313
                    sinks.push_back(this->nodes_.at(v).get());
46✔
314
                }
46✔
315
            }
133✔
316
        }
139✔
317
        if (sinks.size() == 0) {
46✔
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;
46✔
327
        queue.insert(queue.end(), sinks.begin(), sinks.end());
46✔
328

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

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

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

351
                // If multiple out edges, add to queue, add dependency, and skip
352
                if (current != start && this->out_degree(*current) > 1) {
134✔
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)) {
133✔
409
                    throw boost::not_a_dag();
×
410
                }
×
411
                visited.insert(current);
133✔
412
                lists.at(start).push_front(current);
133✔
413

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

465
        // Sort sinks if necessary
466
        if (sinks.size() > 1) {
46✔
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();
46✔
499
        for (const auto* sink : sinks) {
46✔
500
            if (visited.contains(sink)) {
46✔
501
                continue;
×
502
            }
×
503

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

509
                bool all_resolved = true;
48✔
510
                for (auto [node, dependency_id] : dependencies.at(current)) {
48✔
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) {
48✔
533
                    continue;
1✔
534
                }
1✔
535

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

542
    // Sort components
543
    std::sort(components.begin(), components.end(), [](const auto& a, const auto& b) {
45✔
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;
45✔
550
    for (auto& component : components) {
46✔
551
        order.insert(order.end(), component.begin(), component.end());
46✔
552
    }
46✔
553

554
    return order;
45✔
555
}
45✔
556

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

560
    // Build deterministic topological sort for each weakly connected component
561
    std::vector<std::list<DataFlowNode*>> components(num_components);
3,079✔
562
    for (size_t i = 0; i < num_components; i++) {
5,724✔
563
        // Get all sinks of the current component
564
        std::vector<DataFlowNode*> sinks;
2,645✔
565
        bool component_empty = true;
2,645✔
566
        for (auto [v, comp] : components_map) {
9,863✔
567
            if (comp == i) {
9,863✔
568
                component_empty = false;
9,665✔
569
                if (boost::out_degree(v, this->graph_) == 0) {
9,665✔
570
                    sinks.push_back(this->nodes_.at(v).get());
2,687✔
571
                }
2,687✔
572
            }
9,665✔
573
        }
9,863✔
574
        if (sinks.size() == 0) {
2,645✔
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,645✔
584
        queue.insert(queue.end(), sinks.begin(), sinks.end());
2,645✔
585

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

595
            if (visited.contains(start)) {
3,102✔
596
                continue;
95✔
597
            }
95✔
598

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

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

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

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

722
        // Sort sinks if necessary
723
        if (sinks.size() > 1) {
2,645✔
724
            std::sort(sinks.begin(), sinks.end(), [](const DataFlowNode* a, const DataFlowNode* b) {
76✔
725
                const auto* a_tasklet = dynamic_cast<const Tasklet*>(a);
76✔
726
                const auto* b_tasklet = dynamic_cast<const Tasklet*>(b);
76✔
727
                const auto* a_libnode = dynamic_cast<const LibraryNode*>(a);
76✔
728
                const auto* b_libnode = dynamic_cast<const LibraryNode*>(b);
76✔
729
                const auto* a_access_node = dynamic_cast<const AccessNode*>(a);
76✔
730
                const auto* b_access_node = dynamic_cast<const AccessNode*>(b);
76✔
731
                if (a_tasklet && b_tasklet) {
76✔
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) {
76✔
735
                    return true;
×
736
                } else if (a_tasklet && b_access_node) {
76✔
737
                    return true;
×
738
                } else if (a_libnode && b_libnode) {
76✔
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) {
76✔
743
                    return true;
×
744
                } else if (a_access_node && b_access_node) {
76✔
745
                    return a_access_node->data() < b_access_node->data() ||
76✔
746
                           (a_access_node->data() == b_access_node->data() &&
76✔
747
                            a_access_node->element_id() < b_access_node->element_id());
68✔
748
                } else {
76✔
749
                    return false;
×
750
                }
×
751
            });
76✔
752
        }
40✔
753

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

761
            std::stack<DataFlowNode*> stack({sink});
2,657✔
762
            while (!stack.empty()) {
6,014✔
763
                auto* current = stack.top();
3,357✔
764
                visited.insert(current);
3,357✔
765

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

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

793
                components.at(i).insert(components.at(i).end(), lists.at(current).begin(), lists.at(current).end());
3,007✔
794
                stack.pop();
3,007✔
795
            }
3,007✔
796
        }
2,657✔
797
    }
2,645✔
798

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

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

811
    return order;
3,079✔
812
}
3,079✔
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() {
2✔
839
    std::unordered_map<std::string, data_flow::AccessNode*> frontier;
2✔
840
    for (auto& node : this->topological_sort()) {
10✔
841
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node)) {
10✔
842
            frontier[access_node->data()] = access_node;
8✔
843
        }
8✔
844
    }
10✔
845

846
    return frontier;
2✔
847
};
2✔
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 {
5✔
866
    auto ccs_vertex = graph::weakly_connected_components(this->graph_);
5✔
867

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

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

876
const AccessNode* DataFlowGraph::find_standalone_entry(const Memlet* input_edge) const {
1,355✔
877
    if (!input_edge) {
1,355✔
878
        return nullptr;
×
879
    }
×
880
    auto* access_node = dynamic_cast<const data_flow::AccessNode*>(&input_edge->src());
1,355✔
881
    if (access_node && in_degree(*access_node) == 0) {
1,355✔
882
        return access_node;
1,355✔
883
    } else {
1,355✔
884
        return nullptr;
×
885
    }
×
886
}
1,355✔
887

NEW
888
const AccessNode* DataFlowGraph::find_standalone_exit(const Memlet* output_edge) const {
×
NEW
889
    if (!output_edge) {
×
NEW
890
        return nullptr;
×
NEW
891
    }
×
NEW
892
    auto* access_node = dynamic_cast<const data_flow::AccessNode*>(&output_edge->dst());
×
NEW
893
    if (access_node && out_degree(*access_node) == 0) {
×
NEW
894
        return access_node;
×
NEW
895
    } else {
×
NEW
896
        return nullptr;
×
NEW
897
    }
×
NEW
898
}
×
899

900

901
} // namespace data_flow
902
} // 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