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 var = ln->mapArgument(*arg);
326 if (auto entry = std::get_if<LoopNode::EntryVar>(&var))
327 {
328 JLM_ASSERT(entry->input != nullptr);
329 // map to input of loop
330 output_map[arg] = output_map[entry->input->origin()];
331 }
332 else if (auto ba = std::get_if<LoopNode::BackEdgeVar>(&var))
333 {
334 auto result = ba->post;
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 else
340 {
341 throw std::logic_error("LoopNode argument is neither backedge nor entry");
342 }
343 }
344 for (size_t i = 0; i < ln->noutputs(); ++i)
345 {
346 auto out = ln->output(i);
347 JLM_ASSERT(out->results.size() == 1);
348 output_map[out] = output_map[out->results.begin()->origin()];
349 }
350}
351
352std::string
354{
355 std::ostringstream dot;
356 dot << "digraph G {\n";
357 for (size_t i = 0; i < sr->narguments(); ++i)
358 {
359 dot << argument_to_dot(sr->argument(i));
360 }
361 // order arguments horizontally
362 dot << "{rank=source; ";
363 for (size_t i = 0; i < sr->narguments(); ++i)
364 {
365 if (i > 0)
366 {
367 dot << " -> ";
368 }
369 dot << get_port_name(sr->argument(i));
370 }
371 dot << "[style = invis]}";
372
373 for (size_t i = 0; i < sr->nresults(); ++i)
374 {
375 dot << result_to_dot(sr->result(i));
376 }
377 dot << "subgraph cluster_sub {\n";
378 dot << "color=\"#80b3ff\"\n";
379 dot << "penwidth=6\n";
380
381 // process arguments
382 for (size_t i = 0; i < sr->narguments(); ++i)
383 {
384 output_map[sr->argument(i)] = get_port_name(sr->argument(i));
385 }
386 // process nodes
387 for (auto node : rvsdg::TopDownTraverser(sr))
388 {
389 if (dynamic_cast<jlm::rvsdg::SimpleNode *>(node))
390 {
391 auto node_dot = node_to_dot(node);
392 dot << node_dot;
393 auto node_name = get_node_name(node);
394 for (size_t i = 0; i < node->ninputs(); ++i)
395 {
396 auto in_name = node_name + ":" + get_port_name(node->input(i));
397 JLM_ASSERT(output_map.count(node->input(i)->origin()));
398 auto origin = output_map[node->input(i)->origin()];
399 dot << edge(origin, in_name, *node->input(i)->Type());
400 }
401 for (size_t i = 0; i < node->noutputs(); ++i)
402 {
403 output_map[node->output(i)] = node_name + ":" + get_port_name(node->output(i));
404 }
405 }
406 else if (auto ln = dynamic_cast<LoopNode *>(node))
407 {
408 // the only structural nodes left are loop nodes
410 dot << loop_to_dot(ln);
411 }
412 else
413 {
414 throw util::Error("Unimplemented op (unexpected structural node) : " + node->DebugString());
415 }
416 }
417 // process results
418 for (size_t i = 0; i < sr->nresults(); ++i)
419 {
420 auto origin = output_map[sr->result(i)->origin()];
421 auto result = get_port_name(sr->result(i));
422 dot << edge(origin, result, *sr->result(i)->Type());
423 }
424 dot << "}\n";
425 dot << "}\n";
426 return dot.str();
427}
428
429}
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:353
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.