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

daisytuner / docc / 24348757857

13 Apr 2026 02:25PM UTC coverage: 64.469% (+0.09%) from 64.382%
24348757857

push

github

web-flow
Merge pull request #676 from daisytuner/ellide-host-mem-mgmt

Ellides H2D copies in case the host data was freshly allocated and not yet initialized before the offload transfer. In that case, offloaded Malloc is enough.

(will leave the host malloc itself in the graph, as that is a task for DDE to remove)

104 of 125 new or added lines in 5 files covered. (83.2%)

1 existing line in 1 file now uncovered.

30553 of 47392 relevant lines covered (64.47%)

584.02 hits per line

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

83.57
/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✔
31
        this->stream_ << "#" << block.element_id() << " ";
×
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");
×
NEW
160
                    this->stream_ << literal_id << " [style=\"invis\", label=\"\"];" << std::endl;
×
NEW
161
                    this->stream_ << literal_id << " -> " << nodeId << " [style=\"dotted\", label=\"" << i << ":"
×
NEW
162
                                  << in_conn << "\"]" << ";" << std::endl;
×
163
                }
×
164
            }
×
165
        }
53✔
166
    }
79✔
167
    this->stream_.setIndent(this->stream_.indent() - 4);
25✔
168
    this->stream_ << "}" << std::endl;
25✔
169
}
25✔
170

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

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

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

231
void DotVisualizer::visualizeFor(const StructuredSDFG& sdfg, const structured_control_flow::For& loop) {
10✔
232
    auto id = escapeDotId(loop.element_id(), "for_");
10✔
233
    this->stream_ << "subgraph cluster_" << id << " {" << std::endl;
10✔
234
    this->stream_.setIndent(this->stream_.indent() + 4);
10✔
235
    this->stream_ << "style=filled;shape=box;fillcolor=white;color=black;label=\"";
10✔
236
    if (show_block_ids) {
10✔
237
        this->stream_ << "#" << loop.element_id() << " ";
×
238
    }
×
239
    this->stream_ << "for: ";
10✔
240
    this->visualizeForBounds(loop.indvar(), loop.init(), loop.condition(), loop.update());
10✔
241
    this->stream_ << "\";" << std::endl << id << " [shape=point,style=invis,label=\"\"];" << std::endl;
10✔
242
    this->visualizeSequence(sdfg, loop.root());
10✔
243
    this->stream_.setIndent(this->stream_.indent() - 4);
10✔
244
    this->stream_ << "}" << std::endl;
10✔
245
    this->last_comp_name_ = id;
10✔
246
    this->last_comp_name_cluster_ = "cluster_" + id;
10✔
247
}
10✔
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
    this->last_comp_name_ = id;
2✔
253
    this->last_comp_name_cluster_.clear();
2✔
254
}
2✔
255
void DotVisualizer::visualizeBreak(const StructuredSDFG& sdfg, const structured_control_flow::Break& break_node) {
1✔
256
    auto id = escapeDotId(break_node.element_id(), "break_");
1✔
257
    this->stream_ << id << " [shape=cds,label=\" break  \"];" << std::endl;
1✔
258
    this->last_comp_name_ = id;
1✔
259
    this->last_comp_name_cluster_.clear();
1✔
260
}
1✔
261

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

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

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

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

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

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

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

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

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

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

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