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

daisytuner / docc / 24251733303

10 Apr 2026 03:55PM UTC coverage: 64.159% (-0.2%) from 64.376%
24251733303

Pull #673

github

web-flow
Merge b555ef1c7 into ccfd1d376
Pull Request #673: Data flow transfer elimination

381 of 542 new or added lines in 12 files covered. (70.3%)

112 existing lines in 3 files now uncovered.

30169 of 47022 relevant lines covered (64.16%)

578.46 hits per line

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

83.87
/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

14
namespace sdfg {
15
namespace visualizer {
16

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

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

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

25
void DotVisualizer::visualizeBlock(const StructuredSDFG& sdfg, const structured_control_flow::Block& block) {
27✔
26
    auto id = escapeDotId(block.element_id(), "block_");
27✔
27
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
27✔
28
    this->stream_.setIndent(this->stream_.indent() + 4);
27✔
29
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
27✔
30
    if (show_block_ids) {
27✔
NEW
31
        this->stream_ << "#" << block.element_id() << " ";
×
NEW
32
    }
×
33
    this->stream_ << "\";" << std::endl;
27✔
34
    this->last_comp_name_cluster_ = "cluster_" + id;
27✔
35
    if (block.dataflow().nodes().empty()) {
27✔
36
        this->stream_ << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
2✔
37
        this->stream_.setIndent(this->stream_.indent() - 4);
2✔
38
        this->stream_ << "}" << std::endl;
2✔
39
        this->last_comp_name_ = id;
2✔
40
        return;
2✔
41
    }
2✔
42
    this->last_comp_name_.clear();
25✔
43
    std::list<const data_flow::DataFlowNode*> nodes = block.dataflow().topological_sort();
25✔
44
    for (const data_flow::DataFlowNode* node : nodes) {
79✔
45
        std::vector<std::string> in_connectors;
79✔
46
        bool is_access_node = false;
79✔
47
        bool node_will_show_literal_connectors = false;
79✔
48
        auto nodeId = escapeDotId(node->element_id(), "id");
79✔
49
        if (this->last_comp_name_.empty()) this->last_comp_name_ = nodeId;
79✔
50
        if (const data_flow::Tasklet* tasklet = dynamic_cast<const data_flow::Tasklet*>(node)) {
79✔
51
            this->stream_ << nodeId << " [shape=octagon,label=\"" << tasklet->output() << " = ";
26✔
52
            this->visualizeTasklet(*tasklet);
26✔
53
            this->stream_ << "\"];" << std::endl;
26✔
54

55
            in_connectors = tasklet->inputs();
26✔
56
            node_will_show_literal_connectors = true;
26✔
57
        } else if (const data_flow::ConstantNode* constant_node = dynamic_cast<const data_flow::ConstantNode*>(node)) {
53✔
58
            this->stream_ << nodeId << " [";
7✔
59
            this->stream_ << "penwidth=3.0,";
7✔
60
            if (sdfg.is_transient(constant_node->data())) this->stream_ << "style=\"dashed,filled\",";
7✔
61
            this->stream_ << "label=\"" << constant_node->data() << "\"];" << std::endl;
7✔
62
            is_access_node = true;
7✔
63
        } else if (const data_flow::AccessNode* access_node = dynamic_cast<const data_flow::AccessNode*>(node)) {
46✔
64
            this->stream_ << nodeId << " [";
45✔
65
            this->stream_ << "penwidth=3.0,";
45✔
66
            if (sdfg.is_transient(access_node->data())) this->stream_ << "style=\"dashed,filled\",";
45✔
67
            this->stream_ << "label=\"" << access_node->data() << "\"];" << std::endl;
45✔
68
            is_access_node = true;
45✔
69
        } else if (const data_flow::LibraryNode* libnode = dynamic_cast<const data_flow::LibraryNode*>(node)) {
45✔
70
            this->stream_ << nodeId << " [shape=doubleoctagon,label=\"" << libnode->toStr() << "\"];" << std::endl;
1✔
71
            in_connectors = libnode->inputs();
1✔
72
        }
1✔
73

74
        std::unordered_set<std::string> unused_connectors(in_connectors.begin(), in_connectors.end());
79✔
75
        for (const data_flow::Memlet& iedge : block.dataflow().in_edges(*node)) {
79✔
76
            auto& src = iedge.src();
54✔
77
            auto& dst_conn = iedge.dst_conn();
54✔
78
            bool nonexistent_conn = false;
54✔
79

80
            if (!is_access_node) {
54✔
81
                auto it = unused_connectors.find(dst_conn);
27✔
82
                if (it != unused_connectors.end()) {
27✔
83
                    unused_connectors.erase(it); // remove connector from in_connectors, so it is not used again
27✔
84
                } else {
27✔
85
                    nonexistent_conn = true;
×
86
                }
×
87
            }
27✔
88

89
            this->stream_ << escapeDotId(src.element_id(), "id") << " -> " << nodeId << " [label=\"   ";
54✔
90
            bool dstIsVoid = dst_conn == "void";
54✔
91
            bool dstIsRef = dst_conn == "ref";
54✔
92
            bool dstIsDeref = dst_conn == "deref";
54✔
93
            auto& src_conn = iedge.src_conn();
54✔
94
            bool srcIsVoid = src_conn == "void";
54✔
95
            bool srcIsDeref = src_conn == "deref";
54✔
96

97
            if (nonexistent_conn) {
54✔
98
                this->stream_ << "!!"; // this should not happen, but if it does, we can still visualize the memlet
×
99
            }
×
100

101
            if (dstIsVoid || dstIsRef || dstIsDeref) { // subset applies to dst
54✔
102
                auto& dstVar = dynamic_cast<data_flow::AccessNode const&>(iedge.dst()).data();
27✔
103
                bool subsetOnDst = false;
27✔
104
                if (srcIsDeref && dstIsVoid) { // Pure Store by Memlet definition (Dereference Memlet Store)
27✔
105
                    auto& subset = iedge.subset();
×
106
                    if (subset.size() == 1 && symbolic::eq(subset[0], symbolic::integer(0))) {
×
107
                        this->stream_ << "*" << dstVar; // store to pointer without further address calc
×
108
                    } else { // fallback, this should not be allowed to happen
×
109
                        this->stream_ << dstVar; // use access node name instead of connector-name
×
110
                        subsetOnDst = true;
×
111
                    }
×
112
                } else if (dstIsVoid) { // computational memlet / output from tasklet / memory store
27✔
113
                    this->stream_ << dstVar; // use access node name instead of connector-name
27✔
114
                    subsetOnDst = true;
27✔
115
                } else {
27✔
116
                    this->stream_ << dstVar; // use access node name instead of connector-name
×
117
                }
×
118
                if (subsetOnDst) {
27✔
119
                    this->visualizeSubset(sdfg, iedge.subset(), &iedge.base_type());
27✔
120
                }
27✔
121
            } else { // dst is a tasklet/library node
27✔
122
                this->stream_ << dst_conn;
27✔
123
            }
27✔
124

125
            this->stream_ << " = ";
54✔
126

127
            if (srcIsVoid || srcIsDeref) { // subset applies to src, could be computational, reference or dereference
54✔
128
                                           // memlet
129
                auto& srcVar = dynamic_cast<data_flow::AccessNode const&>(src).data();
27✔
130
                bool subsetOnSrc = false;
27✔
131
                if (srcIsVoid && dstIsRef) { // reference memlet / address-of / get-element-ptr equivalent
27✔
132
                    this->stream_ << "&";
×
133
                    subsetOnSrc = true;
×
134
                } else if (srcIsVoid && dstIsDeref) { // Dereference memlet / load from address
27✔
135
                    this->stream_ << "*";
×
136
                    auto& subset = iedge.subset();
×
137
                    if (subset.size() != 1 && symbolic::eq(subset[0], symbolic::integer(0))) { // does not match memlet
×
138
                                                                                               // definition -> fallback
139
                        subsetOnSrc = true;
×
140
                    }
×
141
                } else if (srcIsVoid) {
27✔
142
                    subsetOnSrc = true;
27✔
143
                }
27✔
144
                this->stream_ << srcVar;
27✔
145
                if (subsetOnSrc) {
27✔
146
                    this->visualizeSubset(sdfg, iedge.subset(), &iedge.base_type());
27✔
147
                }
27✔
148
            } else {
27✔
149
                this->stream_ << src_conn;
27✔
150
            }
27✔
151
            this->stream_ << "   \"];" << std::endl;
54✔
152
        }
54✔
153

154
        if (!node_will_show_literal_connectors) {
79✔
155
            for (uint64_t i = 0; i < in_connectors.size(); ++i) {
53✔
156
                auto& in_conn = in_connectors[i];
×
157
                auto it = unused_connectors.find(in_conn);
×
158
                if (it != unused_connectors.end()) {
×
159
                    auto literal_id = escapeDotId(node->element_id(), "id") + "_" + escapeDotId(i, "in");
×
160
                    this->stream_ << literal_id << " [style=\"dotted\", label=\"" << in_conn << "\"];" << std::endl;
×
161
                    this->stream_ << literal_id << " -> " << nodeId << " [label=\"" << i << "\"]" << ";" << std::endl;
×
162
                }
×
163
            }
×
164
        }
53✔
165
    }
79✔
166
    this->stream_.setIndent(this->stream_.indent() - 4);
25✔
167
    this->stream_ << "}" << std::endl;
25✔
168
}
25✔
169

170
void DotVisualizer::visualizeSequence(const StructuredSDFG& sdfg, const structured_control_flow::Sequence& sequence) {
40✔
171
    std::string last_comp_name_tmp, last_comp_name_cluster_tmp;
40✔
172
    for (size_t i = 0; i < sequence.size(); ++i) {
89✔
173
        std::pair<const structured_control_flow::ControlFlowNode&, const structured_control_flow::Transition&> child =
49✔
174
            sequence.at(i);
49✔
175
        this->visualizeNode(sdfg, child.first);
49✔
176
        if ((i > 0) && !last_comp_name_tmp.empty() && !this->last_comp_name_.empty() &&
49✔
177
            last_comp_name_tmp != this->last_comp_name_) {
49✔
178
            this->stream_ << last_comp_name_tmp << " -> " << this->last_comp_name_ << " [";
9✔
179
            if (!last_comp_name_cluster_tmp.empty()) this->stream_ << "ltail=\"" << last_comp_name_cluster_tmp << "\",";
9✔
180
            if (!this->last_comp_name_cluster_.empty())
9✔
181
                this->stream_ << "lhead=\"" << this->last_comp_name_cluster_ << "\",";
6✔
182
            this->stream_ << "minlen=3]"
9✔
183
                          << ";" << std::endl;
9✔
184
        }
9✔
185
        last_comp_name_tmp = this->last_comp_name_;
49✔
186
        last_comp_name_cluster_tmp = this->last_comp_name_cluster_;
49✔
187
    }
49✔
188
}
40✔
189

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

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

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

248
void DotVisualizer::visualizeReturn(const StructuredSDFG& sdfg, const structured_control_flow::Return& return_node) {
2✔
249
    auto id = escapeDotId(return_node.element_id(), "return_");
2✔
250
    this->stream_ << id << " [shape=cds,label=\" return " << return_node.data() << "\"];" << std::endl;
2✔
251
    this->last_comp_name_ = id;
2✔
252
    this->last_comp_name_cluster_.clear();
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
    this->last_comp_name_ = id;
1✔
258
    this->last_comp_name_cluster_.clear();
1✔
259
}
1✔
260

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

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

279
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
3✔
280
    this->visualizeSequence(sdfg, map_node.root());
3✔
281
    this->stream_.setIndent(this->stream_.indent() - 4);
3✔
282
    this->stream_ << "}" << std::endl;
3✔
283
    this->last_comp_name_ = id;
3✔
284
    this->last_comp_name_cluster_ = "cluster_" + id;
3✔
285
}
3✔
286

287
void DotVisualizer::visualize() {
19✔
288
    this->stream_.clear();
19✔
289
    this->stream_ << "digraph " << escapeDotId(this->sdfg_.name()) << " {" << std::endl;
19✔
290
    this->stream_.setIndent(4);
19✔
291
    this->stream_ << "graph [compound=true];" << std::endl;
19✔
292
    this->stream_ << "subgraph cluster_" << escapeDotId(this->sdfg_.name()) << " {" << std::endl;
19✔
293
    this->stream_.setIndent(8);
19✔
294
    this->stream_ << "node [style=filled,fillcolor=white];" << std::endl
19✔
295
                  << "style=filled;color=lightblue;label=\"\";" << std::endl;
19✔
296
    this->visualizeSequence(this->sdfg_, this->sdfg_.root());
19✔
297
    this->stream_.setIndent(4);
19✔
298
    this->stream_ << "}" << std::endl;
19✔
299
    this->stream_.setIndent(0);
19✔
300
    this->stream_ << "}" << std::endl;
19✔
301
}
19✔
302

303
void DotVisualizer::writeToFile(const StructuredSDFG& sdfg, const std::filesystem::path& file) {
×
304
    writeToFile(sdfg, &file);
×
305
}
×
306

307
void DotVisualizer::writeToFile(const StructuredSDFG& sdfg, const std::filesystem::path* file) {
1✔
308
    DotVisualizer viz(sdfg);
1✔
309
    viz.visualize();
1✔
310

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

313
    auto parent_path = fileName.parent_path();
1✔
314
    if (!parent_path.empty()) {
1✔
315
        std::filesystem::create_directories(fileName.parent_path());
×
316
    }
×
317

318
    std::ofstream dotOutput(fileName, std::ofstream::out);
1✔
319
    if (!dotOutput.is_open()) {
1✔
320
        std::cerr << "Could not open file " << fileName << " for writing DOT output." << std::endl;
×
321
    }
×
322

323
    dotOutput << viz.getStream().str();
1✔
324
    dotOutput.close();
1✔
325
}
1✔
326

327
} // namespace visualizer
328
} // 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