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

daisytuner / docc / 27234721677

09 Jun 2026 08:45PM UTC coverage: 61.393% (+0.1%) from 61.275%
27234721677

Pull #741

github

web-flow
Merge cbcd8499f into aacd50c09
Pull Request #741: replaces MemAccessRangeAnalysis with MemoryLayoutAnalysis

476 of 519 new or added lines in 12 files covered. (91.71%)

36 existing lines in 9 files now uncovered.

35749 of 58230 relevant lines covered (61.39%)

762.3 hits per line

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

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

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

36
Element* DataFlowGraph::get_parent() { return this->parent_; };
7,425✔
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,069✔
125
    return boost::in_degree(node.vertex(), this->graph_);
25,069✔
126
};
25,069✔
127

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

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

137
    for (auto& edge : this->edges_) {
612✔
138
        edge.second->replace(old_expression, new_expression);
612✔
139
    }
612✔
140
};
225✔
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() {
644✔
200
    std::unordered_set<data_flow::AccessNode*> dnodes;
644✔
201
    for (auto& node : this->nodes_) {
2,368✔
202
        if (auto access_node = dynamic_cast<data_flow::AccessNode*>(node.second.get())) {
2,368✔
203
            dnodes.insert(access_node);
1,712✔
204
        }
1,712✔
205
    }
2,368✔
206

207
    return dnodes;
644✔
208
};
644✔
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 {
36✔
301
    auto [num_components, components_map] = graph::weakly_connected_components(this->graph_);
36✔
302

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

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

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

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

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

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

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

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

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

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

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

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

554
    return order;
36✔
555
}
36✔
556

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

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

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

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

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

608
                // If multiple out edges, add to queue, add dependency, and skip
609
                if (current != start && this->out_degree(*current) > 1) {
10,119✔
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,704✔
666
                    throw boost::not_a_dag();
×
667
                }
×
668
                visited.insert(current);
9,704✔
669
                lists.at(start).push_front(current);
9,704✔
670

671
                // Put all predecessors on the stack
672
                if (auto* code_node = dynamic_cast<CodeNode*>(current)) {
9,704✔
673
                    std::unordered_set<DataFlowNode*> local_visited;
2,778✔
674
                    for (auto& input : code_node->inputs()) {
4,636✔
675
                        Memlet* iedge = nullptr;
4,636✔
676
                        for (auto& in_edge : this->in_edges(*code_node)) {
7,296✔
677
                            if (in_edge.dst_conn() == input) {
7,296✔
678
                                iedge = &in_edge;
4,599✔
679
                                break;
4,599✔
680
                            }
4,599✔
681
                        }
7,296✔
682
                        if (!iedge) {
4,636✔
683
                            continue;
37✔
684
                        }
37✔
685
                        auto* src = &iedge->src();
4,599✔
686
                        if (!local_visited.contains(src)) {
4,599✔
687
                            local_visited.insert(src);
4,370✔
688
                            stack.push({src, current});
4,370✔
689
                        }
4,370✔
690
                    }
4,599✔
691
                } else {
6,926✔
692
                    std::vector<std::pair<DataFlowNode*, size_t>> tmp_inputs;
6,926✔
693
                    std::unordered_set<DataFlowNode*> local_visited;
6,926✔
694
                    for (auto& iedge : this->in_edges(*current)) {
6,926✔
695
                        auto* src = &iedge.src();
2,729✔
696
                        if (local_visited.contains(src)) {
2,729✔
697
                            continue;
×
698
                        }
×
699
                        local_visited.insert(src);
2,729✔
700
                        size_t value = 0;
2,729✔
701
                        if (auto* tasklet = dynamic_cast<Tasklet*>(src)) {
2,729✔
702
                            value = tasklet->code();
2,445✔
703
                        } else if (auto* libnode = dynamic_cast<LibraryNode*>(src)) {
2,445✔
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,729✔
710
                    }
2,729✔
711
                    std::sort(tmp_inputs.begin(), tmp_inputs.end(), [](const auto& a, const auto& b) {
6,926✔
712
                        return a.second > b.second ||
27✔
713
                               (a.second == b.second && a.first->element_id() < b.first->element_id());
27✔
714
                    });
27✔
715
                    for (auto& tmp_input : tmp_inputs) {
6,926✔
716
                        stack.push({tmp_input.first, current});
2,729✔
717
                    }
2,729✔
718
                }
6,926✔
719
            }
9,704✔
720
        }
3,020✔
721

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

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

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

766
                bool all_resolved = true;
3,370✔
767
                for (auto [node, dependency_id] : dependencies.at(current)) {
3,370✔
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,370✔
790
                    continue;
350✔
791
                }
350✔
792

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

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

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

811
    return order;
3,093✔
812
}
3,093✔
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

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