Jlm
Loading...
Searching...
No Matches
dot-hls.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 David Metz <david.c.metz@ntnu.no>
3 * See COPYING for terms of redistribution.
4 */
5
9
10#include <algorithm>
11#include <set>
12
13namespace jlm::hls
14{
15
16std::string
18{
19 return ".dot";
20}
21
22std::string
27
28std::string
30{
31 auto name = get_port_name(port);
32
33 auto dot =
34 name
35 + " [shape=plaintext label=<\n"
36 " <TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n"
37 " <TR>\n"
38 " <TD BORDER=\"1\" CELLPADDING=\"1\"><FONT POINT-SIZE=\"10\">"
39 + name
40 + "</FONT></TD>\n"
41 " </TR>\n"
42 " </TABLE>\n"
43 ">];\n";
44 return dot;
45}
46
47std::string
49{
50 auto name = get_port_name(port);
51
52 auto dot =
53 "{rank=sink; " + name
54 + " [shape=plaintext label=<\n"
55 " <TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n"
56 " <TR>\n"
57 " <TD BORDER=\"1\" CELLPADDING=\"1\"><FONT POINT-SIZE=\"10\">"
58 + name
59 + "</FONT></TD>\n"
60 " </TR>\n"
61 " </TABLE>\n"
62 ">];}\n";
63 return dot;
64}
65
66std::string
68{
69 auto SPACER = " <TD WIDTH=\"10\"></TD>\n";
70 auto name = get_node_name(node);
71 auto opname = node->DebugString();
72 std::replace_if(opname.begin(), opname.end(), isForbiddenChar, '_');
73
74 std::string inputs;
75 for (size_t i = 0; i < node->ninputs(); ++i)
76 {
77 if (i != 0)
78 {
79 inputs += SPACER;
80 }
81 auto in = get_port_name(node->input(i));
82 inputs += " <TD PORT=\"" + in
83 + "\" BORDER=\"1\" CELLPADDING=\"1\"><FONT POINT-SIZE=\"10\">" + in + "</FONT></TD>\n";
84 }
85
86 std::string outputs;
87 for (size_t i = 0; i < node->noutputs(); ++i)
88 {
89 if (i != 0)
90 {
91 outputs += SPACER;
92 }
93 auto out = get_port_name(node->output(i));
94 outputs += " <TD PORT=\"" + out
95 + "\" BORDER=\"1\" CELLPADDING=\"1\"><FONT POINT-SIZE=\"10\">" + out
96 + "</FONT></TD>\n";
97 }
98
99 std::string color = "black";
101 {
102 color = "blue";
103 }
104 else if (jlm::rvsdg::is<ForkOperation>(node))
105 {
106 color = "grey";
107 }
108 else if (jlm::rvsdg::is<SinkOperation>(node))
109 {
110 color = "grey";
111 }
112 else if (jlm::rvsdg::is<BranchOperation>(node))
113 {
114 color = "green";
115 }
116 else if (jlm::rvsdg::is<MuxOperation>(node))
117 {
118 color = "darkred";
119 }
120 else if (jlm::rvsdg::is<TriggerOperation>(node) || is_constant(node))
121 {
122 color = "orange";
123 }
124
125 // dot inspired by
126 // https://stackoverflow.com/questions/42157650/moving-graphviz-edge-out-of-the-way
127 auto dot =
128 name
129 + " [shape=plaintext label=<\n"
130 "<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n"
131 // inputs
132 " <TR>\n"
133 " <TD BORDER=\"0\">\n"
134 " <TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n"
135 " <TR>\n"
136 " <TD WIDTH=\"20\"></TD>\n"
137 + inputs
138 + " <TD WIDTH=\"20\"></TD>\n"
139 " </TR>\n"
140 " </TABLE>\n"
141 " </TD>\n"
142 " </TR>\n"
143 " <TR>\n"
144 " <TD BORDER=\"3\" STYLE=\"ROUNDED\" CELLPADDING=\"4\">"
145 + opname + "<BR/><FONT POINT-SIZE=\"10\">" + name
146 + "</FONT></TD>\n"
147 " </TR>\n"
148 " <TR>\n"
149 " <TD BORDER=\"0\">\n"
150 " <TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n"
151 " <TR>\n"
152 " <TD WIDTH=\"20\"></TD>\n"
153 + outputs
154 + " <TD WIDTH=\"20\"></TD>\n"
155 " </TR>\n"
156 " </TABLE>\n"
157 " </TD>\n"
158 " </TR>\n"
159 "</TABLE>\n"
160 "> fontcolor="
161 + color + " color=" + color + "];\n";
162 return dot;
163}
164
165std::string
166DotHLS::edge(std::string src, std::string snk, const jlm::rvsdg::Type & type, bool back)
167{
168 auto color = "black";
169 JLM_ASSERT(src != "" && snk != "");
170 if (dynamic_cast<const rvsdg::ControlType *>(&type))
171 {
172 color = "green";
173 }
174 else if (dynamic_cast<const llvm::MemoryStateType *>(&type))
175 {
176 color = "blue";
177 }
178 if (!back)
179 {
180 return src + " -> " + snk + " [style=\"\", arrowhead=\"normal\", color=" + color
181 + ", headlabel=<>, fontsize=10, labelangle=45, labeldistance=2.0, "
182 "labelfontcolor=black, penwidth=3];\n";
183 }
184 // implement back edges by setting constraint to false
185 return src + " -> " + snk + " [style=\"\", arrowhead=\"normal\", color=" + color
186 + ", headlabel=<>, fontsize=10, labelangle=45, labeldistance=2.0, labelfontcolor=black, "
187 "penwidth=3, constraint=false];\n";
188 // return snk + " -> " + src + " [style=\"\", arrowhead=\"normal\", color=" + color +
189 // ", headlabel=<>, fontsize=10, labelangle=45, labeldistance=2.0, labelfontcolor=black,
190 // dir=back];\n";
191}
192
193std::string
195{
196 auto sr = ln->subregion();
197 std::ostringstream dot;
198 dot << "subgraph cluster_loop_" << loop_ctr++ << " {\n";
199 dot << "color=\"#ff8080\"\n";
200
201 std::set<jlm::rvsdg::Output *> back_outputs;
202 std::set<rvsdg::Node *> top_nodes; // no artificial top nodes for now
203 for (size_t i = 0; i < sr->narguments(); ++i)
204 {
205 auto arg = sr->argument(i);
206 // arguments without an input produce a cycle
207 if (arg->input() == nullptr)
208 {
209 back_outputs.insert(arg);
210 }
211 }
212
213 for (auto node : rvsdg::TopDownTraverser(sr))
214 {
215 if (dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
216 {
217 auto node_dot = node_to_dot(node);
218 if (top_nodes.count(node))
219 {
220 dot << "{rank=source; \n";
221 dot << node_dot;
222 dot << "}\n";
223 }
224 else
225 {
226 dot << node_dot;
227 }
228 }
229 else if (auto ln = dynamic_cast<LoopNode *>(node))
230 {
231 // need to prepare output here again, because inputs might not have been resolved yet, because
232 // nodes in outer loop were not yet processed.
234 dot << loop_to_dot(ln);
235 }
236 else
237 {
238 throw util::Error("Unimplemented op (unexpected structural node) : " + node->DebugString());
239 }
240 }
241
242 // all loop muxes at one level
243 dot << "{rank=same ";
244 for (auto node : rvsdg::TopDownTraverser(sr))
245 {
246 auto mx = dynamic_cast<const MuxOperation *>(&node->GetOperation());
247 auto lc = dynamic_cast<const LoopConstantBufferOperation *>(&node->GetOperation());
248 if ((mx && !mx->discarding && mx->loop) || lc)
249 {
250 dot << get_node_name(node) << " ";
251 }
252 }
253 dot << "}\n";
254 // all loop branches at one level
255 dot << "{rank=same ";
256 for (auto node : rvsdg::TopDownTraverser(sr))
257 {
258 auto br = dynamic_cast<const BranchOperation *>(&node->GetOperation());
259 if (br && br->loop)
260 {
261 dot << get_node_name(node) << " ";
262 }
263 }
264 dot << "}\n";
265
266 dot << "}\n";
267 // do edges outside in order not to pull other nodes into the cluster
268 for (auto node : rvsdg::TopDownTraverser(sr))
269 {
270 if (dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
271 {
272 auto mx = dynamic_cast<const MuxOperation *>(&node->GetOperation());
273 auto node_name = get_node_name(node);
274 for (size_t i = 0; i < node->ninputs(); ++i)
275 {
276 auto in_name = node_name + ":" + get_port_name(node->input(i));
277 JLM_ASSERT(output_map.count(node->input(i)->origin()));
278 auto origin = output_map[node->input(i)->origin()];
279 // implement edge as back edge when it produces a cycle
280 bool back = mx && !mx->discarding && mx->loop
281 && (/*i==0||*/ i == 2); // back_outputs.count(node->input(i)->origin());
282 auto origin_out_node = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*node->input(i)->origin());
284 && dynamic_cast<const PredicateBufferOperation *>(&origin_out_node->GetOperation()))
285 {
286 //
287 back = true;
288 }
289 dot << edge(origin, in_name, *node->input(i)->Type(), back);
290 }
291 }
292 }
293 return dot.str();
294}
295
296void
298{
299 // make sure all outputs are translated and available (necessary for argument/result cycles)
300
301 auto sr = ln->subregion();
302 // just translate outputs
303 for (auto node : rvsdg::TopDownTraverser(sr))
304 {
305 if (dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
306 {
307 auto node_name = get_node_name(node);
308 for (size_t i = 0; i < node->noutputs(); ++i)
309 {
310 output_map[node->output(i)] = node_name + ":" + get_port_name(node->output(i));
311 }
312 }
313 else if (auto oln = dynamic_cast<LoopNode *>(node))
314 {
316 }
317 else
318 {
319 throw util::Error("Unimplemented op (unexpected structural node) : " + node->DebugString());
320 }
321 }
322 for (size_t i = 0; i < sr->narguments(); ++i)
323 {
324 auto arg = sr->argument(i);
325 auto ba = dynamic_cast<BackEdgeArgument *>(arg);
326 if (!ba)
327 {
328 JLM_ASSERT(arg->input() != nullptr);
329 // map to input of loop
330 output_map[arg] = output_map[arg->input()->origin()];
331 }
332 else
333 {
334 auto result = ba->result();
335 JLM_ASSERT(*result->Type() == *arg->Type());
336 // map to end of loop (origin of associated result)
337 output_map[arg] = output_map[result->origin()];
338 }
339 }
340 for (size_t i = 0; i < ln->noutputs(); ++i)
341 {
342 auto out = ln->output(i);
343 JLM_ASSERT(out->results.size() == 1);
344 output_map[out] = output_map[out->results.begin()->origin()];
345 }
346}
347
348std::string
350{
351 std::ostringstream dot;
352 dot << "digraph G {\n";
353 for (size_t i = 0; i < sr->narguments(); ++i)
354 {
355 dot << argument_to_dot(sr->argument(i));
356 }
357 // order arguments horizontally
358 dot << "{rank=source; ";
359 for (size_t i = 0; i < sr->narguments(); ++i)
360 {
361 if (i > 0)
362 {
363 dot << " -> ";
364 }
365 dot << get_port_name(sr->argument(i));
366 }
367 dot << "[style = invis]}";
368
369 for (size_t i = 0; i < sr->nresults(); ++i)
370 {
371 dot << result_to_dot(sr->result(i));
372 }
373 dot << "subgraph cluster_sub {\n";
374 dot << "color=\"#80b3ff\"\n";
375 dot << "penwidth=6\n";
376
377 // process arguments
378 for (size_t i = 0; i < sr->narguments(); ++i)
379 {
380 output_map[sr->argument(i)] = get_port_name(sr->argument(i));
381 }
382 // process nodes
383 for (auto node : rvsdg::TopDownTraverser(sr))
384 {
385 if (dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
386 {
387 auto node_dot = node_to_dot(node);
388 dot << node_dot;
389 auto node_name = get_node_name(node);
390 for (size_t i = 0; i < node->ninputs(); ++i)
391 {
392 auto in_name = node_name + ":" + get_port_name(node->input(i));
393 JLM_ASSERT(output_map.count(node->input(i)->origin()));
394 auto origin = output_map[node->input(i)->origin()];
395 dot << edge(origin, in_name, *node->input(i)->Type());
396 }
397 for (size_t i = 0; i < node->noutputs(); ++i)
398 {
399 output_map[node->output(i)] = node_name + ":" + get_port_name(node->output(i));
400 }
401 }
402 else if (auto ln = dynamic_cast<LoopNode *>(node))
403 {
404 // the only structural nodes left are loop nodes
406 dot << loop_to_dot(ln);
407 }
408 else
409 {
410 throw util::Error("Unimplemented op (unexpected structural node) : " + node->DebugString());
411 }
412 }
413 // process results
414 for (size_t i = 0; i < sr->nresults(); ++i)
415 {
416 auto origin = output_map[sr->result(i)->origin()];
417 auto result = get_port_name(sr->result(i));
418 dot << edge(origin, result, *sr->result(i)->Type());
419 }
420 dot << "}\n";
421 dot << "}\n";
422 return dot.str();
423}
424
425}
static std::string get_port_name(jlm::rvsdg::Input *port)
Definition base-hls.cpp:62
std::unordered_map< jlm::rvsdg::Output *, std::string > output_map
Definition base-hls.hpp:45
const rvsdg::LambdaNode * get_hls_lambda(llvm::LlvmRvsdgModule &rm)
Definition base-hls.cpp:136
std::string get_node_name(const rvsdg::Node *node)
Definition base-hls.cpp:28
std::string edge(std::string src, std::string snk, const jlm::rvsdg::Type &type, bool back=false)
Definition dot-hls.cpp:166
std::string loop_to_dot(LoopNode *ln)
Definition dot-hls.cpp:194
void prepare_loop_out_port(LoopNode *ln)
Definition dot-hls.cpp:297
std::string result_to_dot(rvsdg::RegionResult *port)
Definition dot-hls.cpp:48
std::string node_to_dot(const rvsdg::Node *node)
Definition dot-hls.cpp:67
std::string argument_to_dot(rvsdg::RegionArgument *port)
Definition dot-hls.cpp:29
std::string subregion_to_dot(rvsdg::Region *sr)
Definition dot-hls.cpp:349
std::string GetText(llvm::LlvmRvsdgModule &rm) override
Definition dot-hls.cpp:23
std::string extension() override
Definition dot-hls.cpp:17
Memory state type class.
Definition types.hpp:477
NodeInput * input(size_t index) const noexcept
Definition node.hpp:615
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
virtual std::string DebugString() const =0
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
Represents the argument of a region.
Definition region.hpp:41
Represents the result of a region.
Definition region.hpp:120
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
#define JLM_ASSERT(x)
Definition common.hpp:16
static bool is_constant(const rvsdg::Node *node)
bool isForbiddenChar(char c)
Definition base-hls.cpp:16
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition node.hpp:1058
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
detail::TopDownTraverserGeneric< false > TopDownTraverser
Traverser for visiting every node in a region in a top down order.