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

daisytuner / docc / 28793626242

06 Jul 2026 01:04PM UTC coverage: 62.962% (+0.2%) from 62.801%
28793626242

Pull #740

github

web-flow
Merge b7e03d389 into 23e67a4ab
Pull Request #740: Expand pass

594 of 962 new or added lines in 31 files covered. (61.75%)

36 existing lines in 10 files now uncovered.

40557 of 64415 relevant lines covered (62.96%)

972.09 hits per line

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

81.05
/sdfg/src/visualizer/dot_visualizer.cpp
1
#include "sdfg/visualizer/dot_visualizer.h"
2

3
#include <cstddef>
4
#include <string>
5
#include <utility>
6

7
#include <regex>
8
#include "sdfg/data_flow/access_node.h"
9
#include "sdfg/data_flow/memlet.h"
10
#include "sdfg/structured_control_flow/control_flow_node.h"
11
#include "sdfg/structured_control_flow/sequence.h"
12
#include "sdfg/structured_sdfg.h"
13
#include "sdfg/symbolic/symbolic.h"
14

15
namespace sdfg {
16
namespace visualizer {
17

18
static std::regex dotIdBadChars("[^a-zA-Z0-9_]+");
19

20
static std::string escapeDotId(size_t id, const std::string& prefix = "") { return prefix + std::to_string(id); }
134✔
21

22
static std::string escapeDotId(const std::string& id, const std::string& prefix = "") {
26✔
23
    return prefix + std::regex_replace(id, dotIdBadChars, "_");
26✔
24
}
26✔
25

26
void DotVisualizer::register_chain_elem(const Element& element, const std::string& node_id, const std::string& cluster_id) {
39✔
27
    auto& scope = seq_scope_stack_.back();
39✔
28
    scope.last2_chain_elem = scope.last_chain_elem;
39✔
29
    scope.last_chain_elem = SeqChainElem{&element, node_id, cluster_id};
39✔
30
}
39✔
31

32
void DotVisualizer::enter_scope() { seq_scope_stack_.emplace_back(); }
15✔
33

34
void DotVisualizer::exit_scope() { seq_scope_stack_.pop_back(); }
15✔
35

36
void DotVisualizer::visualizeSDFG(const SDFG& sdfg) {
1✔
37
    this->stream_.clear();
1✔
38
    this->stream_ << "digraph SDFG {\n";
1✔
39
    this->stream_.setIndent(4);
1✔
40
    this->stream_ << "graph [compound=true];" << std::endl << "node [style=filled,fillcolor=white];" << std::endl;
1✔
41

42
    // State identifier in DOT
43
    std::unordered_map<size_t, std::string> node_ids;
1✔
44

45
    // States as nodes
46
    for (auto& state : sdfg.states()) {
4✔
47
        auto id = escapeDotId(state.element_id(), "state_");
4✔
48
        this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
4✔
49
        this->stream_.setIndent(this->stream_.indent() + 4);
4✔
50
        this->stream_ << "style=filled;fillcolor=white;color=black;label=\"State " << state.element_id() << "\";"
4✔
51
                      << std::endl;
4✔
52
        if (auto* return_state = dynamic_cast<const control_flow::ReturnState*>(&state)) {
4✔
53
            this->stream_ << id << " [shape=cds,label=\" return " << return_state->data() << " \"];" << std::endl;
×
54
        } else {
4✔
55
            this->stream_ << id << " [shape=point,style=invis;label=\"\"];" << std::endl;
4✔
56
            this->visualizeDataFlowGraph(id, state.dataflow());
4✔
57
        }
4✔
58
        this->stream_.setIndent(this->stream_.indent() - 4);
4✔
59
        this->stream_ << "}" << std::endl;
4✔
60
        node_ids.insert({state.element_id(), id});
4✔
61
    }
4✔
62

63
    // Edges
64
    for (auto& edge : sdfg.edges()) {
4✔
65
        auto& src_id = node_ids.at(edge.src().element_id());
4✔
66
        auto& dst_id = node_ids.at(edge.dst().element_id());
4✔
67
        this->stream_ << src_id << " -> " << dst_id << " [ltail=cluster_" << src_id << ",lhead=cluster_" << dst_id
4✔
68
                      << ",label=\"";
4✔
69

70
        // Condition
71
        bool print_condition = !symbolic::eq(edge.condition(), symbolic::__true__());
4✔
72
        if (print_condition) {
4✔
73
            this->stream_ << edge.condition()->__str__();
2✔
74
        }
2✔
75

76
        // Assignments
77
        if (!edge.assignments().empty()) {
4✔
78
            if (print_condition) {
1✔
79
                this->stream_ << ",\\n";
×
80
            }
×
81
            this->stream_ << "{";
1✔
82
            bool first = true;
1✔
83
            for (auto& [var, expr] : edge.assignments()) {
1✔
84
                if (!first) {
1✔
85
                    this->stream_ << "; ";
×
86
                }
×
87
                this->stream_ << var->get_name() << " = " << expr->__str__();
1✔
88
                first = false;
1✔
89
            }
1✔
90
            this->stream_ << "}";
1✔
91
        }
1✔
92
        this->stream_ << "\"];" << std::endl;
4✔
93
    }
4✔
94

95
    this->stream_.setIndent(0);
1✔
96
    this->stream_ << "}" << std::endl;
1✔
97
}
1✔
98

99
void DotVisualizer::visualizeStructuredSDFG(const StructuredSDFG& sdfg) {
13✔
100
    this->stream_.clear();
13✔
101
    this->stream_ << "digraph " << escapeDotId(sdfg.name()) << " {" << std::endl;
13✔
102
    this->stream_.setIndent(4);
13✔
103
    this->stream_ << "graph [compound=true];" << std::endl;
13✔
104
    this->stream_ << "subgraph cluster_" << escapeDotId(sdfg.name()) << " {" << std::endl;
13✔
105
    this->stream_.setIndent(8);
13✔
106
    this->stream_ << "node [style=filled,fillcolor=white];" << std::endl
13✔
107
                  << "style=filled;color=lightblue;label=\"\";" << std::endl;
13✔
108
    this->visualizeSequence(sdfg, sdfg.root());
13✔
109
    this->stream_.setIndent(4);
13✔
110
    this->stream_ << "}" << std::endl;
13✔
111
    this->stream_.setIndent(0);
13✔
112
    this->stream_ << "}" << std::endl;
13✔
113
}
13✔
114

115
void DotVisualizer::visualizeBlock(const StructuredSDFG& sdfg, const structured_control_flow::Block& block) {
19✔
116
    auto id = escapeDotId(block.element_id(), "block_");
19✔
117
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
19✔
118
    this->stream_.setIndent(this->stream_.indent() + 4);
19✔
119
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
19✔
120
    if (show_block_ids) {
19✔
121
        this->stream_ << "#" << block.element_id() << " ";
19✔
122
    }
19✔
123
    this->stream_ << "\";" << std::endl;
19✔
124
    this->visualizeDataFlowGraph(id, block.dataflow());
19✔
125
    this->stream_.setIndent(this->stream_.indent() - 4);
19✔
126
    this->stream_ << "}" << std::endl;
19✔
127
}
19✔
128

129
void DotVisualizer::visualizeSequence(const StructuredSDFG& sdfg, const structured_control_flow::Sequence& sequence) {
28✔
130
    if (sequence.size() == 0) {
28✔
NEW
131
        auto id = escapeDotId(sequence.element_id(), "empty_seq_");
×
NEW
132
        this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
×
NEW
133
        this->stream_.setIndent(this->stream_.indent() + 4);
×
NEW
134
        this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
×
NEW
135
        if (show_block_ids) {
×
NEW
136
            this->stream_ << "Seq #" << sequence.element_id() << " ";
×
NEW
137
        }
×
NEW
138
        this->stream_ << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
×
NEW
139
        register_chain_elem(sequence, id, "cluster_" + id);
×
NEW
140
        this->stream_ << "\";" << std::endl;
×
NEW
141
        this->stream_.setIndent(this->stream_.indent() - 4);
×
NEW
142
        this->stream_ << "}" << std::endl;
×
NEW
143
        return;
×
NEW
144
    }
×
145

146
    auto& seq_scope = this->seq_scope_stack_.back();
28✔
147

148
    for (size_t i = 0; i < sequence.size(); ++i) {
63✔
149
        std::pair<const structured_control_flow::ControlFlowNode&, const structured_control_flow::Transition&> child =
35✔
150
            sequence.at(i);
35✔
151

152
        this->visualizeNode(sdfg, child.first);
35✔
153

154
        if (seq_scope.last_chain_elem && seq_scope.last2_chain_elem) {
35✔
155
            this->stream_ << seq_scope.last2_chain_elem->node_id << " -> " << seq_scope.last_chain_elem->node_id
7✔
156
                          << " [";
7✔
157
            auto last2_cluster = seq_scope.last2_chain_elem->cluster_id;
7✔
158
            if (!last2_cluster.empty()) {
7✔
159
                this->stream_ << "ltail=\"" << last2_cluster << "\",";
7✔
160
            }
7✔
161
            auto last_cluster = seq_scope.last_chain_elem->cluster_id;
7✔
162
            if (!last_cluster.empty()) {
7✔
163
                this->stream_ << "lhead=\"" << last_cluster << "\",";
4✔
164
            }
4✔
165
            this->stream_ << "minlen=3";
7✔
166

167
            // visualize assignments on edge
168
            if (i > 0 && !sequence.at(i - 1).second.empty()) {
7✔
169
                this->stream_ << ",label=\"{";
2✔
170
                bool comma_sep = false;
2✔
171
                for (auto& [sym, expr] : sequence.at(i - 1).second.assignments()) {
2✔
172
                    if (comma_sep) {
2✔
173
                        this->stream_ << ",";
×
174
                        comma_sep = true;
×
175
                    }
×
176
                    this->stream_ << sym->get_name() << " = " << expr->__str__();
2✔
177
                }
2✔
178
                this->stream_ << "}\"";
2✔
179
            }
2✔
180

181
            this->stream_ << "];" << std::endl;
7✔
182
        }
7✔
183
    }
35✔
184

185
    seq_scope.last2_chain_elem = std::nullopt;
28✔
186
}
28✔
187

188
void DotVisualizer::visualizeIfElse(const StructuredSDFG& sdfg, const structured_control_flow::IfElse& if_else) {
3✔
189
    auto id = escapeDotId(if_else.element_id(), "if_");
3✔
190
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
3✔
191
    this->stream_.setIndent(this->stream_.indent() + 4);
3✔
192
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
3✔
193
    if (show_block_ids) {
3✔
194
        this->stream_ << "#" << if_else.element_id() << " ";
3✔
195
    }
3✔
196
    this->stream_ << "if:\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
3✔
197
    for (size_t i = 0; i < if_else.size(); ++i) {
9✔
198
        this->stream_ << "subgraph cluster_" << id << "_" << std::to_string(i) << " {" << std::endl;
6✔
199
        this->stream_.setIndent(this->stream_.indent() + 4);
6✔
200
        this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\""
6✔
201
                      << this->expression(if_else.at(i).second->__str__()) << "\";" << std::endl;
6✔
202
        enter_scope();
6✔
203
        this->visualizeSequence(sdfg, if_else.at(i).first);
6✔
204
        exit_scope();
6✔
205
        this->stream_.setIndent(this->stream_.indent() - 4);
6✔
206
        this->stream_ << "}" << std::endl;
6✔
207
    }
6✔
208
    this->stream_.setIndent(this->stream_.indent() - 4);
3✔
209
    this->stream_ << "}" << std::endl;
3✔
210
    register_chain_elem(if_else, id, "cluster_" + id);
3✔
211
}
3✔
212

213
void DotVisualizer::visualizeWhile(const StructuredSDFG& sdfg, const structured_control_flow::While& while_loop) {
2✔
214
    auto id = escapeDotId(while_loop.element_id(), "while_");
2✔
215
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
2✔
216
    this->stream_.setIndent(this->stream_.indent() + 4);
2✔
217
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
2✔
218
    if (show_block_ids) {
2✔
219
        this->stream_ << "#" << while_loop.element_id() << " ";
2✔
220
    }
2✔
221
    this->stream_ << "while:\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
2✔
222
    enter_scope();
2✔
223
    this->visualizeSequence(sdfg, while_loop.root());
2✔
224
    exit_scope();
2✔
225
    this->stream_.setIndent(this->stream_.indent() - 4);
2✔
226
    this->stream_ << "}" << std::endl;
2✔
227
    register_chain_elem(while_loop, id, "cluster_" + id);
2✔
228
}
2✔
229

230
void DotVisualizer::visualizeFor(const StructuredSDFG& sdfg, const structured_control_flow::For& loop) {
6✔
231
    auto id = escapeDotId(loop.element_id(), "for_");
6✔
232
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
6✔
233
    this->stream_.setIndent(this->stream_.indent() + 4);
6✔
234
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
6✔
235
    if (show_block_ids) {
6✔
236
        this->stream_ << "#" << loop.element_id() << " ";
6✔
237
    }
6✔
238
    this->stream_ << "for: ";
6✔
239
    this->visualizeForBounds(loop.indvar(), loop.init(), loop.condition(), loop.update());
6✔
240
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
6✔
241
    enter_scope();
6✔
242
    this->visualizeSequence(sdfg, loop.root());
6✔
243
    exit_scope();
6✔
244
    this->stream_.setIndent(this->stream_.indent() - 4);
6✔
245
    this->stream_ << "}" << std::endl;
6✔
246
    register_chain_elem(loop, id, "cluster_" + id);
6✔
247
}
6✔
248

249
void DotVisualizer::visualizeReturn(const StructuredSDFG& sdfg, const structured_control_flow::Return& return_node) {
2✔
250
    auto id = escapeDotId(return_node.element_id(), "return_");
2✔
251
    this->stream_ << id << " [shape=cds,label=\" return " << return_node.data() << "\"];" << std::endl;
2✔
252
    register_chain_elem(return_node, id, "");
2✔
253
}
2✔
254
void DotVisualizer::visualizeBreak(const StructuredSDFG& sdfg, const structured_control_flow::Break& break_node) {
1✔
255
    auto id = escapeDotId(break_node.element_id(), "break_");
1✔
256
    this->stream_ << id << " [shape=cds,label=\" break  \"];" << std::endl;
1✔
257
    register_chain_elem(break_node, id, "");
1✔
258
}
1✔
259

260
void DotVisualizer::visualizeContinue(const StructuredSDFG& sdfg, const structured_control_flow::Continue& continue_node) {
1✔
261
    auto id = escapeDotId(continue_node.element_id(), "cont_");
1✔
262
    this->stream_ << id << " [shape=cds,label=\" continue  \"];" << std::endl;
1✔
263
    register_chain_elem(continue_node, id, "");
1✔
264
}
1✔
265

266
void DotVisualizer::visualizeMap(const StructuredSDFG& sdfg, const structured_control_flow::Map& map_node) {
×
267
    auto id = escapeDotId(map_node.element_id(), "map_");
×
268
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
×
269
    this->stream_.setIndent(this->stream_.indent() + 4);
×
270
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
×
271
    if (show_block_ids) {
×
272
        this->stream_ << "#" << map_node.element_id() << " ";
×
273
    }
×
274
    this->stream_ << "map [" << map_node.schedule_type().value() << "]: ";
×
275
    this->visualizeForBounds(map_node.indvar(), map_node.init(), map_node.condition(), map_node.update());
×
276

277
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
×
NEW
278
    enter_scope();
×
279
    this->visualizeSequence(sdfg, map_node.root());
×
NEW
280
    exit_scope();
×
281
    this->stream_.setIndent(this->stream_.indent() - 4);
×
282
    this->stream_ << "}" << std::endl;
×
NEW
283
    register_chain_elem(map_node, id, "cluster_" + id);
×
284
}
×
285

286
void DotVisualizer::visualizeReduce(const StructuredSDFG& sdfg, const structured_control_flow::Reduce& reduce_node) {
1✔
287
    auto id = escapeDotId(reduce_node.element_id(), "reduce_");
1✔
288
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
1✔
289
    this->stream_.setIndent(this->stream_.indent() + 4);
1✔
290
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
1✔
291
    if (show_block_ids) {
1✔
292
        this->stream_ << "#" << reduce_node.element_id() << " ";
1✔
293
    }
1✔
294
    this->stream_ << "reduce [" << reduce_node.schedule_type().value() << "]";
1✔
295
    bool comma_sep = false;
1✔
296
    this->stream_ << " {";
1✔
297
    for (auto& reduction : reduce_node.reductions()) {
1✔
298
        if (comma_sep) {
1✔
299
            this->stream_ << ", ";
×
300
        }
×
301
        comma_sep = true;
1✔
302
        this->stream_ << structured_control_flow::reduction_operation_to_string(reduction.operation) << ": "
1✔
303
                      << reduction.container;
1✔
304
    }
1✔
305
    this->stream_ << "}: ";
1✔
306
    this->visualizeForBounds(reduce_node.indvar(), reduce_node.init(), reduce_node.condition(), reduce_node.update());
1✔
307

308
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
1✔
309
    enter_scope();
1✔
310
    this->visualizeSequence(sdfg, reduce_node.root());
1✔
311
    exit_scope();
1✔
312
    this->stream_.setIndent(this->stream_.indent() - 4);
1✔
313
    this->stream_ << "}" << std::endl;
1✔
314
    register_chain_elem(reduce_node, id, "cluster_" + id);
1✔
315
}
1✔
316

317
void DotVisualizer::visualizeDataFlowGraph(const std::string& id, const data_flow::DataFlowGraph& dfg) {
23✔
318
    auto cluster_id = "cluster_" + id;
23✔
319
    if (dfg.nodes().empty()) {
23✔
320
        this->stream_ << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
5✔
321
        register_chain_elem(*dfg.get_parent(), id, cluster_id);
5✔
322
        return;
5✔
323
    }
5✔
324
    std::string chain_node_id;
18✔
325
    std::list<const data_flow::DataFlowNode*> nodes = dfg.topological_sort();
18✔
326
    for (const data_flow::DataFlowNode* node : nodes) {
56✔
327
        std::vector<std::string> in_connectors;
56✔
328
        bool is_access_node = false;
56✔
329
        bool node_will_show_literal_connectors = false;
56✔
330
        auto nodeId = escapeDotId(node->element_id(), "id");
56✔
331
        if (chain_node_id.empty()) {
56✔
332
            chain_node_id = nodeId;
18✔
333
        }
18✔
334
        if (const data_flow::Tasklet* tasklet = dynamic_cast<const data_flow::Tasklet*>(node)) {
56✔
335
            this->stream_ << nodeId << " [shape=octagon,label=\"" << tasklet->output() << " = ";
18✔
336
            this->visualizeTasklet(*tasklet);
18✔
337
            this->stream_ << "\"];" << std::endl;
18✔
338

339
            in_connectors = tasklet->inputs();
18✔
340
            node_will_show_literal_connectors = true;
18✔
341
        } else if (const data_flow::ConstantNode* constant_node = dynamic_cast<const data_flow::ConstantNode*>(node)) {
38✔
342
            this->stream_ << nodeId << " [";
3✔
343
            this->stream_ << "penwidth=3.0,";
3✔
344
            if (this->sdfg_.is_transient(constant_node->data())) this->stream_ << "style=\"dashed,filled\",";
3✔
345
            this->stream_ << "label=\"" << constant_node->data() << "\"];" << std::endl;
3✔
346
            is_access_node = true;
3✔
347
        } else if (const data_flow::AccessNode* access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
35✔
348
            this->stream_ << nodeId << " [";
34✔
349
            this->stream_ << "penwidth=3.0,";
34✔
350
            if (this->sdfg_.is_transient(access_node->data())) this->stream_ << "style=\"dashed,filled\",";
34✔
351
            this->stream_ << "label=\"" << access_node->data() << "\"];" << std::endl;
34✔
352
            is_access_node = true;
34✔
353
        } else if (const data_flow::LibraryNode* libnode = dynamic_cast<const data_flow::LibraryNode*>(node)) {
34✔
354
            this->stream_ << nodeId << " [shape=doubleoctagon,label=\"" << libnode->toStr() << "\"];" << std::endl;
1✔
355
            in_connectors = libnode->inputs();
1✔
356
        }
1✔
357

358
        std::unordered_set<std::string> unused_connectors(in_connectors.begin(), in_connectors.end());
56✔
359
        for (const data_flow::Memlet& iedge : dfg.in_edges(*node)) {
56✔
360
            auto& src = iedge.src();
39✔
361
            auto& dst_conn = iedge.dst_conn();
39✔
362
            bool nonexistent_conn = false;
39✔
363

364
            if (!is_access_node) {
39✔
365
                auto it = unused_connectors.find(dst_conn);
20✔
366
                if (it != unused_connectors.end()) {
20✔
367
                    unused_connectors.erase(it); // remove connector from in_connectors, so it is not used again
20✔
368
                } else {
20✔
369
                    nonexistent_conn = true;
×
370
                }
×
371
            }
20✔
372

373
            this->stream_ << escapeDotId(src.element_id(), "id") << " -> " << nodeId << " [label=\"   ";
39✔
374
            bool dstIsVoid = dst_conn == "void";
39✔
375
            bool dstIsRef = dst_conn == "ref";
39✔
376
            bool dstIsDeref = dst_conn == "deref";
39✔
377
            auto& src_conn = iedge.src_conn();
39✔
378
            bool srcIsVoid = src_conn == "void";
39✔
379
            bool srcIsDeref = src_conn == "deref";
39✔
380

381
            if (nonexistent_conn) {
39✔
382
                this->stream_ << "!!"; // this should not happen, but if it does, we can still visualize the memlet
×
383
            }
×
384

385
            if (dstIsVoid || dstIsRef || dstIsDeref) { // subset applies to dst
39✔
386
                auto& dstVar = dynamic_cast<data_flow::AccessNode const&>(iedge.dst()).data();
19✔
387
                bool subsetOnDst = false;
19✔
388
                if (srcIsDeref && dstIsVoid) { // Pure Store by Memlet definition (Dereference Memlet Store)
19✔
389
                    auto& subset = iedge.subset();
×
390
                    if (subset.size() == 1 && symbolic::eq(subset[0], symbolic::integer(0))) {
×
391
                        this->stream_ << "*" << dstVar; // store to pointer without further address calc
×
392
                    } else { // fallback, this should not be allowed to happen
×
393
                        this->stream_ << dstVar; // use access node name instead of connector-name
×
394
                        subsetOnDst = true;
×
395
                    }
×
396
                } else if (dstIsVoid) { // computational memlet / output from tasklet / memory store
19✔
397
                    this->stream_ << dstVar; // use access node name instead of connector-name
19✔
398
                    subsetOnDst = true;
19✔
399
                } else {
19✔
400
                    this->stream_ << dstVar; // use access node name instead of connector-name
×
401
                }
×
402
                if (subsetOnDst) {
19✔
403
                    this->visualizeSubset(iedge.subset(), &iedge.base_type());
19✔
404
                }
19✔
405
            } else { // dst is a tasklet/library node
20✔
406
                this->stream_ << dst_conn;
20✔
407
            }
20✔
408

409
            this->stream_ << " = ";
39✔
410

411
            if (srcIsVoid || srcIsDeref) { // subset applies to src, could be computational, reference or dereference
39✔
412
                                           // memlet
413
                auto& srcVar = dynamic_cast<data_flow::AccessNode const&>(src).data();
20✔
414
                bool subsetOnSrc = false;
20✔
415
                if (srcIsVoid && dstIsRef) { // reference memlet / address-of / get-element-ptr equivalent
20✔
416
                    this->stream_ << "&";
×
417
                    subsetOnSrc = true;
×
418
                } else if (srcIsVoid && dstIsDeref) { // Dereference memlet / load from address
20✔
419
                    this->stream_ << "*";
×
420
                    auto& subset = iedge.subset();
×
421
                    if (subset.size() != 1 && symbolic::eq(subset[0], symbolic::integer(0))) { // does not match memlet
×
422
                                                                                               // definition -> fallback
423
                        subsetOnSrc = true;
×
424
                    }
×
425
                } else if (srcIsVoid) {
20✔
426
                    subsetOnSrc = true;
20✔
427
                }
20✔
428
                this->stream_ << srcVar;
20✔
429
                if (subsetOnSrc) {
20✔
430
                    this->visualizeSubset(iedge.subset(), &iedge.base_type());
20✔
431
                }
20✔
432
            } else {
20✔
433
                this->stream_ << src_conn;
19✔
434
            }
19✔
435
            this->stream_ << "   \"];" << std::endl;
39✔
436
        }
39✔
437

438
        if (!node_will_show_literal_connectors) {
56✔
439
            for (uint64_t i = 0; i < in_connectors.size(); ++i) {
38✔
440
                auto& in_conn = in_connectors[i];
×
441
                auto it = unused_connectors.find(in_conn);
×
442
                if (it != unused_connectors.end()) {
×
443
                    auto literal_id = escapeDotId(node->element_id(), "id") + "_" + escapeDotId(i, "in");
×
444
                    this->stream_ << literal_id << " [style=\"invis\", label=\"\"];" << std::endl;
×
445
                    this->stream_ << literal_id << " -> " << nodeId << " [style=\"dotted\", label=\"" << i << ":"
×
446
                                  << in_conn << "\"]" << ";" << std::endl;
×
447
                }
×
448
            }
×
449
        }
38✔
450
    }
56✔
451
    register_chain_elem(*dfg.get_parent(), chain_node_id, cluster_id);
18✔
452
}
18✔
453

454
void DotVisualizer::writeToFile(const Function& sdfg, const std::filesystem::path& file) { writeToFile(sdfg, &file); }
×
455

456
void DotVisualizer::writeToFile(const Function& sdfg, const std::filesystem::path* file) {
1✔
457
    DotVisualizer viz(sdfg);
1✔
458
    viz.visualize();
1✔
459

460
    std::filesystem::path fileName = file ? *file : std::filesystem::path(sdfg.name() + ".dot");
1✔
461

462
    auto parent_path = fileName.parent_path();
1✔
463
    if (!parent_path.empty()) {
1✔
464
        std::filesystem::create_directories(fileName.parent_path());
×
465
    }
×
466

467
    std::ofstream dotOutput(fileName, std::ofstream::out);
1✔
468
    if (!dotOutput.is_open()) {
1✔
469
        std::cerr << "Could not open file " << fileName << " for writing DOT output." << std::endl;
×
470
    }
×
471

472
    dotOutput << viz.getStream().str();
1✔
473
    dotOutput.close();
1✔
474
}
1✔
475

476
} // namespace visualizer
477
} // 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