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

daisytuner / docc / 28806128926

06 Jul 2026 04:16PM UTC coverage: 62.96%. First build
28806128926

push

github

web-flow
New LibNodeExpansion pass (#740)

* Switched expansion "pipeline" to new LibNodeExpansionPass that can recursively expand using a LibNodeExpander impl.
- removed access to analysis_manager from expansion methods, as those would currently not be appropriately maintained between nodes
+ New expansion API handles more of the boilerplate code (checking for standalone, creating boundary access nodes, removing old elements)
* Also migrated sdfg-json-to-c.cpp to not using the expansion pipeline anymore
* toStr() for ReduceNodes and giving PyStructuredSDFG access to the output_dir for additional dumping
~ DotVisualizer : Fix on nested sequences.
+ DotVisualizer: visualize empty sequences
* DotVisualizer default-enabled show block/loop ids
 * while MathNodes still have an expand-method, the new infrastructure is based upon "Expander" classes. The MathNodeExpander just redirects to the method for now
 ~ Broadcast node still used old ptr-output semantics
 * updated tensor & blas node expand to new expand API, that handles more of the boilerplate code (checking, removing old nodes, creating standalone-replacement nodes)
 - removed Transpose Node. Was unused and on old ptr-semantics
 + StructuredSDFGBuilder.add_sequence_at, add_for_at, add_map_at
 * switched StructuredSDFGBuilder internally to use ptr of Assignments to express using default assignments when needed
 * updated tests to use the new expand_single_math_node helper function, instead of the method directly
 + pass and expand-single helper functions are ready to use other expanders
 + EinsumExpansionPass is basically the legacy ExpansionPass, only restricted to EinsumNodes. It still needs to run in a pipeline to ensure finding multiple EinsumNodes per block. This is temporary, because Einsum is the only node that already supported splitting a block, which the new expansion disallows, as it should handle it itself when needed (but that part is not yet implemented)

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

40554 of 64412 relevant lines covered (62.96%)

963.11 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