Jlm
GraphWriter.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
7 #include <jlm/util/strfmt.hpp>
8 
9 #include <ostream>
10 #include <string_view>
11 
12 namespace jlm::util::graph
13 {
14 // All GraphElements with an associated ProgramObject get this attribute added
15 static const char * const DOT_TOOLTIP_ATTRIBUTE = "tooltip";
16 // Edges are not named in dot, so use an attribute to assign id instead
17 static const char * const DOT_EDGE_ID_ATTRIBUTE = "id";
18 // For setting the background color of cells in dot html tables
19 static const char * const DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE = "BGCOLOR";
20 
21 // The json field containing the label of a graph element
22 static const char * const JSON_LABEL_FIELD = "label";
23 // The map of attributes in a graph element json object
24 static const char * const JSON_ATTRIBUTE_FIELD = "attr";
25 // The address of the program object represented by a graph element json object
26 static const char * const JSON_OBJECT_POINTER_FIELD = "obj";
27 // Field specifying the type of node, for special nodes like InOutNodes
28 static const char * const JSON_NODE_TYPE_FIELD = "type";
29 
30 // Fields in json objects representing InOutNodes
31 static const char * const JSON_IN_PORTS_FIELD = "ins";
32 static const char * const JSON_OUT_PORTS_FIELD = "outs";
33 static const char * const JSON_SUBGRAPHS_FIELD = "subgraphs";
34 static const char * const JSON_HTML_TABLE_ATTRIBUTES_FIELD = "htmlTableAttr";
35 
36 // Fields in Graph objects
37 static const char * const JSON_PARENT_NODE_FIELD = "parentNode";
38 static const char * const JSON_PARENT_GRAPH_FIELD = "parentGraph";
39 static const char * const JSON_ARGUMENTS_FIELD = "arguments";
40 static const char * const JSON_NODES_FIELD = "nodes";
41 static const char * const JSON_RESULTS_FIELD = "results";
42 static const char * const JSON_EDGES_FIELD = "edges";
43 
49 static bool
50 looksLikeIdentifier(std::string_view string)
51 {
52  if (string.empty())
53  return false;
54 
55  // We avoid C's isalpha, as it is locale dependent
56  auto isAlpha = [](char c)
57  {
58  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
59  };
60  auto isDigit = [](char c)
61  {
62  return (c >= '0' && c <= '9');
63  };
64 
65  char firstChar = string[0];
66  if (!isAlpha(firstChar) && firstChar != '_')
67  return false;
68 
69  for (char c : string)
70  if (!isAlpha(c) && !isDigit(c) && c != '_')
71  return false;
72 
73  return true;
74 }
75 
80 static void
81 printIdentifierSafe(std::ostream & out, std::string_view string)
82 {
83  bool quoted = !looksLikeIdentifier(string);
84 
85  if (quoted)
86  out << '"';
87  for (char c : string)
88  {
89  if (c == '"')
90  out << "\\\"";
91  else if (c == '\\')
92  out << "\\\\";
93  else if (c == '\n')
94  out << "\\n";
95  else if (c == '\r')
96  out << "\\r";
97  else if (c == '\t')
98  out << "\\t";
99  else if (c < ' ' || c >= 127)
100  {
101  // Print all other special chars as \x escaped hex.
102  char tmpStr[3];
103  snprintf(tmpStr, sizeof(tmpStr), "%02X", c);
104  out << "\\x" << tmpStr;
105  }
106  else
107  out << c;
108  }
109  if (quoted)
110  out << '"';
111 }
112 
118 static void
119 printStringAsHtmlText(std::ostream & out, std::string_view string, bool replaceNewlines)
120 {
121  for (char c : string)
122  {
123  if (c == '&')
124  out << "&amp;";
125  else if (c == '"')
126  out << "&quot;";
127  else if (c == '<')
128  out << "&lt;";
129  else if (c == '>')
130  out << "&gt;";
131  else if (c == '\n' && replaceNewlines)
132  out << "<BR/>";
133  else
134  out << c;
135  }
136 }
137 
142 static void
143 printStringAsHtmlAttributeName(std::ostream & out, std::string_view string)
144 {
145  for (char c : string)
146  {
147  if (c <= ' ' || c >= 127 || c == '<' || c == '>' || c == '"' || c == '\'' || c == '/'
148  || c == '=')
149  out << '-';
150  else
151  out << c;
152  }
153 }
154 
159 static void
160 printJsonString(std::ostream & out, std::string_view string)
161 {
162  out << '"';
163  for (char c : string)
164  {
165  if (c == '"')
166  out << "\\\"";
167  else if (c == '\\')
168  out << "\\\\";
169  else if (c == '\n')
170  out << "\\n";
171  else if (c == '\t')
172  out << "\\t";
173  else
174  out << c;
175  }
176  out << '"';
177 }
178 
183 std::ostream &
184 withIndent(std::ostream & out, size_t indent)
185 {
186  for (size_t i = 0; i < indent; i++)
187  out << ' ';
188  return out;
189 }
190 
192  : Label_(),
193  UniqueIdSuffix_(std::nullopt),
194  ProgramObject_(0),
195  AttributeMap_()
196 {}
197 
198 void
199 GraphElement::PrintFullId(std::ostream & out) const
200 {
202  out << GetIdPrefix() << GetUniqueIdSuffix();
203 }
204 
205 std::string
207 {
208  std::ostringstream ss;
209  PrintFullId(ss);
210  return ss.str();
211 }
212 
213 const Graph &
215 {
216  return const_cast<GraphElement *>(this)->GetGraph();
217 }
218 
219 void
220 GraphElement::SetLabel(std::string label)
221 {
222  Label_ = std::move(label);
223 }
224 
225 void
226 GraphElement::AppendToLabel(std::string_view text, std::string_view sep)
227 {
228  if (HasLabel())
229  {
230  Label_.append(sep).append(text);
231  }
232  else
233  {
234  Label_ = text;
235  }
236 }
237 
238 bool
240 {
241  return !Label_.empty();
242 }
243 
244 const std::string &
246 {
247  return Label_;
248 }
249 
250 std::string_view
251 GraphElement::GetLabelOr(std::string_view otherwise) const
252 {
253  if (HasLabel())
254  return Label_;
255  return otherwise;
256 }
257 
258 size_t
260 {
262  return UniqueIdSuffix_.value();
263 }
264 
265 void
267 {
268  JLM_ASSERT(object);
269  if (ProgramObject_ != 0)
271  ProgramObject_ = object;
272  if (ProgramObject_ != 0)
274 }
275 
276 void
278 {
280 }
281 
282 bool
284 {
285  return ProgramObject_ != 0;
286 }
287 
288 uintptr_t
290 {
291  return ProgramObject_;
292 }
293 
294 void
295 GraphElement::SetAttribute(const std::string & attribute, std::string value)
296 {
297  AttributeMap_[attribute] = std::move(value);
298 }
299 
300 void
301 GraphElement::SetAttributeObject(const std::string & attribute, uintptr_t object)
302 {
303  JLM_ASSERT(object);
304  AttributeMap_[attribute] = object;
305 }
306 
307 void
308 GraphElement::SetAttributeGraphElement(const std::string & attribute, const GraphElement & element)
309 {
310  JLM_ASSERT(&GetGraph().GetWriter() == &element.GetGraph().GetWriter());
311  AttributeMap_[attribute] = &element;
312 }
313 
314 bool
315 GraphElement::HasAttribute(const std::string & attribute) const
316 {
317  return AttributeMap_.find(attribute) != AttributeMap_.end();
318 }
319 
320 std::optional<std::string_view>
321 GraphElement::GetAttributeString(const std::string & attribute) const
322 {
323  if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
324  {
325  if (auto stringValue = std::get_if<std::string>(&it->second))
326  {
327  return *stringValue;
328  }
329  }
330  return std::nullopt;
331 }
332 
333 std::optional<uintptr_t>
334 GraphElement::GetAttributeObject(const std::string & attribute) const
335 {
336  if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
337  {
338  if (auto uintptrValue = std::get_if<uintptr_t>(&it->second))
339  {
340  return *uintptrValue;
341  }
342  }
343  return std::nullopt;
344 }
345 
346 const GraphElement *
347 GraphElement::GetAttributeGraphElement(const std::string & attribute) const
348 {
349  if (auto it = AttributeMap_.find(attribute); it != AttributeMap_.end())
350  {
351  if (auto graphElementValue = std::get_if<const GraphElement *>(&it->second))
352  {
353  return *graphElementValue;
354  }
355 
356  // Otherwise, check if this attribute holds a program object that is represented by a
357  // GraphElement in this graph, or in any graph in the GraphWriter.
358  if (auto ptr = std::get_if<uintptr_t>(&it->second))
359  {
360  if (auto gElement = GetGraph().GetElementFromProgramObject(*ptr))
361  {
362  return gElement;
363  }
364  if (auto gwElement = GetGraph().GetWriter().GetElementFromProgramObject(*ptr))
365  {
366  return gwElement;
367  }
368  }
369  }
370  return nullptr;
371 }
372 
373 bool
374 GraphElement::RemoveAttribute(const std::string & attribute)
375 {
376  return AttributeMap_.erase(attribute);
377 }
378 
379 void
381 {
382  if (IsFinalized())
383  return;
384 
385  auto & writer = GetGraph().GetWriter();
386  UniqueIdSuffix_ = writer.GetNextUniqueIdStubSuffix(GetIdPrefix());
387 }
388 
389 bool
391 {
392  return UniqueIdSuffix_.has_value();
393 }
394 
398 static void
400  std::ostream & out,
401  std::string_view name,
402  std::string_view value,
403  AttributeOutputFormat format)
404 {
406  {
407  printIdentifierSafe(out, name);
408  out << "=";
409  printIdentifierSafe(out, value);
410  out << " "; // space separation
411  }
412  else if (format == AttributeOutputFormat::HTMLAttributes)
413  {
415  out << "=\""; // HTML attributes must be quoted
416  printStringAsHtmlText(out, value, false);
417  out << "\" "; // Closing quote and separating space
418  }
419  else if (format == AttributeOutputFormat::JSON)
420  {
421  printJsonString(out, name);
422  out << ':';
423  printJsonString(out, value);
424  }
425  else
426  {
427  JLM_UNREACHABLE("Unknown AttributeOutputFormat");
428  }
429 }
430 
431 void
433  std::ostream & out,
434  const std::string & name,
435  AttributeOutputFormat format) const
436 {
437  if (auto string = GetAttributeString(name))
438  {
439  outputKeyValuePair(out, name, *string, format);
440  }
441  else if (auto graphElement = GetAttributeGraphElement(name))
442  {
443  outputKeyValuePair(out, name, graphElement->GetFullId(), format);
444  }
445  else if (auto object = GetAttributeObject(name))
446  {
447  outputKeyValuePair(out, name, strfmt("ptr", reinterpret_cast<void *>(*object)), format);
448  }
449  else
450  {
451  JLM_UNREACHABLE("Unknown attribute type");
452  }
453 }
454 
455 void
456 GraphElement::OutputAttributes(std::ostream & out, AttributeOutputFormat format) const
457 {
458  bool first = true;
459  const auto next = [&]()
460  {
461  if (first)
462  first = false;
463  else if (format == AttributeOutputFormat::JSON)
464  out << ", ";
465  else
466  out << ' ';
467  };
468 
469  for (const auto & [name, _] : AttributeMap_)
470  {
471  next();
472  OutputAttribute(out, name, format);
473  }
474 
475  // If no tooltip attribute is specified, use the program object pointer.
476  // This is not done in JSON, as the program object is included in a separate field.
478  && HasProgramObject())
479  {
480  next();
482  out,
484  strfmt(reinterpret_cast<void *>(GetProgramObject())),
485  format);
486  }
487 }
488 
494 static std::ostream &
495 printNextJsonField(std::ostream & out, std::string_view name, size_t indent, bool & firstField)
496 {
497  if (firstField)
498  firstField = false;
499  else
500  out << ',';
501  out << std::endl;
502  withIndent(out, indent) << '"' << name << "\": ";
503 
504  return out;
505 };
506 
507 template<typename T>
508 static void
509 printJsonElementMap(std::ostream & out, size_t indent, const T & elements)
510 {
511  out << "{";
512  bool first = true;
513  for (auto & element : elements)
514  {
515  if (first)
516  first = false;
517  else
518  out << ",";
519  out << std::endl;
520  element->outputJson(out, indent + 1);
521  }
522 
523  if (first)
524  out << "}";
525  else
526  {
527  out << std::endl;
528  withIndent(out, indent) << "}";
529  }
530 };
531 
532 void
533 GraphElement::outputJsonObjectOpening(std::ostream & out, size_t indent, bool & firstField) const
534 {
535  withIndent(out, indent) << "\"";
536  PrintFullId(out); // The full id does not include quotes or special characters
537  out << "\": {";
538 
539  indent++;
540  if (HasLabel())
541  {
542  printNextJsonField(out, JSON_LABEL_FIELD, indent, firstField);
543  printJsonString(out, GetLabel());
544  }
545 
546  if (HasProgramObject())
547  {
549  out << '"' << reinterpret_cast<void *>(GetProgramObject()) << '"';
550  }
551 
552  if (!AttributeMap_.empty())
553  {
555  out << '{';
557  out << '}';
558  }
559 }
560 
561 static void
562 outputJsonObjectClosing(std::ostream & out, size_t indent, bool firstField)
563 {
564  // The object contains at least one field, close on the next line
565  if (!firstField)
566  {
567  out << std::endl;
568  withIndent(out, indent);
569  }
570  out << "}";
571 }
572 
574  : GraphElement()
575 {}
576 
577 Graph &
579 {
580  return GetNode().GetGraph();
581 }
582 
583 bool
585 {
586  return true;
587 }
588 
589 bool
591 {
592  return true;
593 }
594 
595 const std::vector<Edge *> &
597 {
598  return Connections_;
599 }
600 
601 void
603 {
604  if (this == &edge.GetFrom())
605  JLM_ASSERT(CanBeEdgeTail() || !edge.IsDirected());
606  else if (this == &edge.GetTo())
607  JLM_ASSERT(CanBeEdgeHead() || !edge.IsDirected());
608  else
609  JLM_UNREACHABLE("Port was informed about unrelated edge");
610 
611  Connections_.push_back(&edge);
612 }
613 
614 bool
616 {
617  for (auto & edge : Connections_)
618  {
619  if (&edge->GetFrom() == this || !edge->IsDirected())
620  return true;
621  }
622  return false;
623 }
624 
625 bool
627 {
628  for (auto & edge : Connections_)
629  {
630  if (&edge->GetTo() == this || !edge->IsDirected())
631  return true;
632  }
633  return false;
634 }
635 
636 void
637 Port::OutputIncomingEdgesASCII(std::ostream & out) const
638 {
639  std::ostringstream text;
640  size_t numIncomingEdges = 0;
641 
642  for (auto & edge : Connections_)
643  {
644  if (&edge->GetTo() != this && edge->IsDirected())
645  continue;
646 
647  Port & otherEnd = edge->GetOtherEnd(*this);
648  if (numIncomingEdges == 0)
649  text << otherEnd.GetFullId();
650  else
651  text << ", " << otherEnd.GetFullId();
652 
653  numIncomingEdges++;
654  }
655 
656  if (numIncomingEdges == 1)
657  out << text.str();
658  else
659  out << "[" << text.str() << "]";
660 }
661 
663  : Port(),
664  Graph_(graph)
665 {}
666 
667 const char *
669 {
670  return "node";
671 }
672 
673 Node &
675 {
676  return *this;
677 }
678 
679 Graph &
681 {
682  return Graph_;
683 }
684 
685 void
686 Node::SetShape(std::string shape)
687 {
688  SetAttribute("shape", std::move(shape));
689 }
690 
691 void
692 Node::SetFillColor(std::string color)
693 {
694  // The dot output gives all nodes style=filled by default, so we only need to set the color
695  SetAttribute("fillcolor", std::move(color));
696 }
697 
698 void
699 Node::OutputDotPortId(std::ostream & out) const
700 {
701  out << GetFullId();
702 }
703 
704 void
705 Node::OutputASCII(std::ostream & out, size_t indent) const
706 {
707  withIndent(out, indent);
708  if (HasOutgoingEdges())
709  {
710  out << GetFullId() << ":";
711  }
712  printIdentifierSafe(out, GetLabelOr("NODE"));
713  if (HasIncomingEdges())
714  {
715  out << "<-";
717  }
718  out << std::endl;
719 }
720 
721 void
722 Node::OutputDot(std::ostream & out, size_t indent) const
723 {
724  withIndent(out, indent) << GetFullId() << " [";
725  out << "label=";
726  if (HasLabel())
727  {
729  }
730  else
731  {
733  }
734  out << " ";
736  out << "];" << std::endl;
737 }
738 
739 void
740 Node::outputJson(std::ostream & out, size_t indent) const
741 {
742  bool firstField = true;
743  outputJsonObjectOpening(out, indent, firstField);
744  outputJsonObjectClosing(out, indent, firstField);
745 }
746 
747 void
748 Node::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
749 {
750  // Regular nodes do not have sub graphs
751 }
752 
754  : Node_(node)
755 {}
756 
757 const char *
759 {
760  return "in";
761 }
762 
763 Node &
765 {
766  return Node_;
767 }
768 
769 bool
771 {
772  return false;
773 }
774 
775 void
776 InputPort::SetFillColor(std::string color)
777 {
778  // Attribute on the <TD> tag used by the dot output
780 }
781 
782 void
783 InputPort::OutputDotPortId(std::ostream & out) const
784 {
785  out << Node_.GetFullId() << ":" << GetFullId() << ":n";
786 }
787 
788 void
789 InputPort::outputJson(std::ostream & out, size_t indent) const
790 {
791  bool firstField = true;
792  outputJsonObjectOpening(out, indent, firstField);
793  outputJsonObjectClosing(out, indent, firstField);
794 }
795 
797  : Node_(node)
798 {}
799 
800 const char *
802 {
803  return "out";
804 }
805 
806 Node &
808 {
809  return Node_;
810 }
811 
812 bool
814 {
815  return false;
816 }
817 
818 void
819 OutputPort::SetFillColor(std::string color)
820 {
821  // Attribute on the <TD> tag used by the dot output
823 }
824 
825 void
826 OutputPort::OutputDotPortId(std::ostream & out) const
827 {
828  out << Node_.GetFullId() << ":" << GetFullId() << ":s";
829 }
830 
831 void
832 OutputPort::outputJson(std::ostream & out, size_t indent) const
833 {
834  bool firstField = true;
835  outputJsonObjectOpening(out, indent, firstField);
836  outputJsonObjectClosing(out, indent, firstField);
837 }
838 
839 InOutNode::InOutNode(Graph & graph, size_t inputPorts, size_t outputPorts)
840  : Node(graph)
841 {
842  for (size_t i = 0; i < inputPorts; i++)
843  CreateInputPort();
844 
845  for (size_t i = 0; i < outputPorts; i++)
847 }
848 
849 void
851 {
852  throw Error("InOutNodes can not have custom shapes set");
853 }
854 
855 InputPort &
857 {
858  auto inputPort = new InputPort(*this);
859  InputPorts_.emplace_back(inputPort);
860  return *inputPort;
861 }
862 
863 size_t
865 {
866  return InputPorts_.size();
867 }
868 
869 InputPort &
871 {
872  JLM_ASSERT(index < InputPorts_.size());
873  return *InputPorts_[index];
874 }
875 
876 OutputPort &
878 {
879  auto outputPort = new OutputPort(*this);
880  OutputPorts_.emplace_back(outputPort);
881  return *outputPort;
882 }
883 
884 size_t
886 {
887  return OutputPorts_.size();
888 }
889 
890 OutputPort &
892 {
893  JLM_ASSERT(index < OutputPorts_.size());
894  return *OutputPorts_[index];
895 }
896 
897 Graph &
899 {
900  auto & graph = GetGraph().GetWriter().CreateSubGraph(*this);
901  SubGraphs_.push_back(&graph);
902  return graph;
903 }
904 
905 size_t
907 {
908  return SubGraphs_.size();
909 }
910 
911 Graph &
913 {
914  JLM_ASSERT(index < SubGraphs_.size());
915  return *SubGraphs_[index];
916 }
917 
918 void
919 InOutNode::SetHtmlTableAttribute(std::string name, std::string value)
920 {
921  HtmlTableAttributes_[name] = std::move(value);
922 }
923 
924 void
925 InOutNode::SetFillColor(std::string color)
926 {
928 }
929 
930 void
932 {
933  Node::Finalize();
934 
935  for (auto & inputPort : InputPorts_)
936  inputPort->Finalize();
937  for (auto & outputPort : OutputPorts_)
938  outputPort->Finalize();
939  for (auto & graph : SubGraphs_)
940  graph->Finalize();
941 }
942 
943 void
944 InOutNode::OutputSubgraphs(std::ostream & out, OutputFormat format, size_t indent) const
945 {
946  // Only the ASCII format prints subgraphs inside the nodes themselves
947  JLM_ASSERT(format == OutputFormat::ASCII);
948 
949  for (auto & graph : SubGraphs_)
950  graph->Output(out, format, indent);
951 }
952 
953 void
954 InOutNode::OutputASCII(std::ostream & out, size_t indent) const
955 {
956  withIndent(out, indent);
957 
958  // output the names of all output ports
959  for (size_t i = 0; i < NumOutputPorts(); i++)
960  {
961  if (i != 0)
962  out << ", ";
963  out << OutputPorts_[i]->GetFullId();
964  }
965  if (NumOutputPorts() != 0)
966  out << " := ";
967 
968  // If the node itself is used as a tail port, we must include its name
970  {
971  out << GetFullId() << ":";
972  }
973  printIdentifierSafe(out, GetLabelOr("NODE"));
975  {
976  out << "<-";
978  }
979  out << " ";
980 
981  // Now output the origins of all input ports
982  for (size_t i = 0; i < NumInputPorts(); i++)
983  {
984  if (i != 0)
985  out << ", ";
986  InputPorts_[i]->OutputIncomingEdgesASCII(out);
987  }
988  out << std::endl;
989 
990  // Output all sub graphs, if we have any
992 }
993 
994 void
995 InOutNode::OutputDot(std::ostream & out, size_t indent) const
996 {
997  withIndent(out, indent) << GetFullId() << " [shape=plain style=solid ";
998  out << "label=<" << std::endl;
999 
1000  // InOutNodes are printed as html tables
1001  out << "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">" << std::endl;
1002 
1003  // Used to create rows of boxes above and below the node
1004  auto PrintPortList = [&out](auto & ports)
1005  {
1006  if (ports.empty())
1007  return;
1008 
1009  out << "\t<TR><TD>" << std::endl;
1010  out << "\t\t<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\"><TR>" << std::endl;
1011  out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1012  for (size_t i = 0; i < ports.size(); i++)
1013  {
1014  // Spacing
1015  if (i != 0)
1016  out << "\t\t\t<TD WIDTH=\"10\"></TD>" << std::endl;
1017 
1018  auto & port = *ports[i];
1019  out << "\t\t\t<TD BORDER=\"1\" CELLPADDING=\"1\" ";
1020  out << "PORT=\"" << port.GetFullId() << "\" ";
1021  port.OutputAttributes(out, AttributeOutputFormat::HTMLAttributes);
1022  // Unless a different color is specified, fill the port cell with white
1023  if (!port.HasAttribute(DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE))
1024  {
1025  out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1026  }
1027  if (port.HasLabel())
1028  {
1029  out << "><FONT POINT-SIZE=\"10\">";
1030  printStringAsHtmlText(out, port.GetLabel(), true);
1031  out << "</FONT>";
1032  }
1033  else
1034  {
1035  // ports without labels have a fixed size
1036  out << " WIDTH=\"8\" HEIGHT=\"5\" FIXEDSIZE=\"true\">";
1037  }
1038  out << "</TD>" << std::endl;
1039  }
1040  out << "\t\t\t<TD WIDTH=\"20\"></TD>" << std::endl;
1041  out << "\t\t</TR></TABLE>" << std::endl;
1042  out << "\t</TD></TR>" << std::endl;
1043  };
1044 
1045  // Inputs
1046  PrintPortList(InputPorts_);
1047 
1048  // The main body of the node: a rounded rectangle
1049  out << "\t<TR><TD>" << std::endl;
1050  out << "\t\t<TABLE BORDER=\"1\" STYLE=\"ROUNDED\" CELLBORDER=\"0\" ";
1051  out << "CELLSPACING=\"0\" CELLPADDING=\"0\" ";
1052  for (auto & [name, value] : HtmlTableAttributes_)
1053  {
1054  printStringAsHtmlAttributeName(out, name);
1055  out << "=\"";
1056  printStringAsHtmlText(out, value, false);
1057  out << "\" ";
1058  }
1059  // Unless a different color is specified, fill the table cell with white
1061  {
1062  out << DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE << "=\"white\" ";
1063  }
1064  out << ">" << std::endl;
1065  out << "\t\t\t<TR><TD CELLPADDING=\"1\">";
1067  out << "</TD></TR>" << std::endl;
1068 
1069  // Subgraphs
1070  if (!SubGraphs_.empty())
1071  {
1072  out << "\t\t\t<TR><TD>" << std::endl;
1073  out << "\t\t\t\t<TABLE BORDER=\"0\" CELLSPACING=\"4\" CELLPADDING=\"2\"><TR>" << std::endl;
1074  for (auto & graph : SubGraphs_)
1075  {
1076  out << "\t\t\t\t\t<TD BORDER=\"1\" STYLE=\"ROUNDED\" WIDTH=\"40\" BGCOLOR=\"white\" ";
1077  out << "_SUBGRAPH=\"" << graph->GetFullId() << "\">";
1078  printStringAsHtmlText(out, graph->GetFullId(), true);
1079  out << "</TD>" << std::endl;
1080  }
1081  out << "\t\t\t\t</TR></TABLE>" << std::endl;
1082  out << "\t\t\t</TD></TR>" << std::endl;
1083  }
1084 
1085  // End of the rounded rectangle
1086  out << "\t\t</TABLE>" << std::endl;
1087  out << "\t</TD></TR>" << std::endl;
1088 
1089  PrintPortList(OutputPorts_);
1090 
1091  out << "</TABLE>" << std::endl;
1092  withIndent(out, indent) << "> ";
1094  out << "];" << std::endl;
1095 }
1096 
1097 void
1098 InOutNode::outputJson(std::ostream & out, size_t indent) const
1099 {
1100  bool firstField = true;
1101  outputJsonObjectOpening(out, indent, firstField);
1102  indent++;
1103 
1104  printNextJsonField(out, JSON_NODE_TYPE_FIELD, indent, firstField) << "\"inout\"";
1105 
1106  // Input ports
1107  if (NumInputPorts())
1108  {
1109  printNextJsonField(out, JSON_IN_PORTS_FIELD, indent, firstField);
1111  }
1112 
1113  // Output ports
1114  if (NumOutputPorts())
1115  {
1116  printNextJsonField(out, JSON_OUT_PORTS_FIELD, indent, firstField);
1118  }
1119 
1120  // Subgraphs
1121  if (NumSubgraphs())
1122  {
1123  printNextJsonField(out, JSON_SUBGRAPHS_FIELD, indent, firstField) << "[";
1124  bool first = true;
1125  for (const auto & subgraph : SubGraphs_)
1126  {
1127  if (first)
1128  first = false;
1129  else
1130  out << ", ";
1131  out << '"';
1132  subgraph->PrintFullId(out);
1133  out << '"';
1134  }
1135  out << "]";
1136  }
1137 
1138  // HTML Table attributes
1139  if (!HtmlTableAttributes_.empty())
1140  {
1142  bool first = true;
1143  for (const auto & [key, value] : HtmlTableAttributes_)
1144  {
1145  if (first)
1146  first = false;
1147  else
1148  out << ", ";
1150  }
1151  out << "}";
1152  }
1153 
1154  indent--;
1155  outputJsonObjectClosing(out, indent, firstField);
1156 }
1157 
1159  : Node(graph),
1160  OutsideSource_(nullptr)
1161 {}
1162 
1163 const char *
1165 {
1166  return "arg";
1167 }
1168 
1169 bool
1171 {
1172  return false;
1173 }
1174 
1175 void
1176 ArgumentNode::SetOutsideSource(const Port & outsideSource)
1177 {
1178  OutsideSource_ = &outsideSource;
1179  SetAttributeGraphElement("outsideSource", outsideSource);
1180 }
1181 
1182 void
1183 ArgumentNode::OutputASCII(std::ostream & out, size_t) const
1184 {
1185  // In ASCII the argument is printed as part of an ARG line
1186  out << GetFullId();
1187  if (HasLabel())
1188  {
1189  out << ":";
1190  printIdentifierSafe(out, GetLabel());
1191  }
1192  if (OutsideSource_ != nullptr)
1193  {
1194  out << " <= ";
1196  }
1197 }
1198 
1200  : Node(graph),
1201  OutsideDestination_(nullptr)
1202 {}
1203 
1204 const char *
1206 {
1207  return "res";
1208 }
1209 
1210 bool
1212 {
1213  return false;
1214 }
1215 
1216 void
1217 ResultNode::SetOutsideDestination(const Port & outsideDestination)
1218 {
1219  OutsideDestination_ = &outsideDestination;
1220  SetAttributeGraphElement("outsideDest", outsideDestination);
1221 }
1222 
1223 void
1224 ResultNode::OutputASCII(std::ostream & out, size_t) const
1225 {
1226  // In ASCII the result is printed as part of an RES line
1228  if (HasLabel())
1229  {
1230  out << ":";
1231  printIdentifierSafe(out, GetLabel());
1232  }
1233  if (OutsideDestination_ != nullptr)
1234  out << " => " << OutsideDestination_->GetFullId();
1235 }
1236 
1237 Edge::Edge(Port & from, Port & to, bool directed)
1238  : From_(from),
1239  To_(to),
1240  Directed_(directed)
1241 {
1242  from.OnEdgeAdded(*this);
1243  to.OnEdgeAdded(*this);
1244 }
1245 
1246 const char *
1248 {
1249  return "edge";
1250 }
1251 
1252 Graph &
1254 {
1255  // from and to have the same graph, return either
1256  return From_.GetGraph();
1257 }
1258 
1259 Port &
1261 {
1262  return From_;
1263 }
1264 
1265 Port &
1267 {
1268  return To_;
1269 }
1270 
1271 bool
1273 {
1274  return Directed_;
1275 }
1276 
1277 Port &
1279 {
1280  if (&end == &From_)
1281  return To_;
1282  else if (&end == &To_)
1283  return From_;
1284 
1285  JLM_UNREACHABLE("GetOtherEnd called with neither end");
1286 }
1287 
1288 void
1289 Edge::SetStyle(std::string style)
1290 {
1291  SetAttribute("style", std::move(style));
1292 }
1293 
1294 void
1295 Edge::SetArrowHead(std::string arrow)
1296 {
1297  SetAttribute("arrowhead", std::move(arrow));
1298 }
1299 
1300 void
1301 Edge::SetArrowTail(std::string arrow)
1302 {
1303  // When outputting dot, the "dir" attribute will be automatically changed to make the tail visible
1304  SetAttribute("arrowtail", std::move(arrow));
1305 }
1306 
1307 std::string_view
1309 {
1310  const bool hasHeadArrow = HasAttribute("arrowhead") || Directed_;
1311  const bool hasTailArrow = HasAttribute("arrowtail");
1312  if (hasHeadArrow && hasTailArrow)
1313  return "both";
1314  else if (hasHeadArrow)
1315  return "forward";
1316  else if (hasTailArrow)
1317  return "back";
1318  else
1319  return "none";
1320 }
1321 
1322 void
1323 Edge::OutputDot(std::ostream & out, size_t indent) const
1324 {
1325  withIndent(out, indent);
1326  From_.OutputDotPortId(out);
1327  out << " -> ";
1328  To_.OutputDotPortId(out);
1329  out << "[";
1330 
1331  out << "dir=" << getDirection() << " ";
1332 
1333  if (HasLabel())
1334  {
1335  out << "label=";
1336  printIdentifierSafe(out, GetLabel());
1337  out << " ";
1338  }
1339 
1340  // Edges are not normally named, so use the id attribute to include the edge's id
1342  {
1343  out << DOT_EDGE_ID_ATTRIBUTE << "=";
1345  out << " ";
1346  }
1347 
1349  out << "];" << std::endl;
1350 }
1351 
1352 void
1353 Edge::outputJson(std::ostream & out, size_t indent) const
1354 {
1355  bool firstField = true;
1356  outputJsonObjectOpening(out, indent, firstField);
1357  indent++;
1358 
1359  printNextJsonField(out, "from", indent, firstField);
1360  out << '"';
1361  From_.OutputDotPortId(out);
1362  out << '"';
1363 
1364  printNextJsonField(out, "to", indent, firstField);
1365  out << '"';
1366  To_.OutputDotPortId(out);
1367  out << '"';
1368 
1369  // Direction
1370  printNextJsonField(out, "dir", indent, firstField);
1371  out << '"' << getDirection() << '"';
1372 
1373  indent--;
1374  outputJsonObjectClosing(out, indent, firstField);
1375 }
1376 
1378  : GraphElement(),
1379  Writer_(writer),
1380  ParentNode_(nullptr)
1381 {}
1382 
1383 Graph::Graph(Writer & writer, Node & parentNode)
1384  : GraphElement(),
1385  Writer_(writer),
1386  ParentNode_(&parentNode)
1387 {}
1388 
1389 const char *
1391 {
1392  return "graph";
1393 }
1394 
1395 Graph &
1397 {
1398  return *this;
1399 }
1400 
1401 Writer &
1403 {
1404  return Writer_;
1405 }
1406 
1407 const Writer &
1409 {
1410  return Writer_;
1411 }
1412 
1413 bool
1415 {
1416  return ParentNode_ != nullptr;
1417 }
1418 
1419 Node &
1421 {
1422  auto node = new Node(*this);
1423  Nodes_.emplace_back(node);
1424  return *node;
1425 }
1426 
1427 InOutNode &
1428 Graph::CreateInOutNode(size_t inputPorts, size_t outputPorts)
1429 {
1430  auto node = new InOutNode(*this, inputPorts, outputPorts);
1431  Nodes_.emplace_back(node);
1432  return *node;
1433 }
1434 
1435 size_t
1436 Graph::NumNodes() const noexcept
1437 {
1438  return Nodes_.size();
1439 }
1440 
1441 Node &
1442 Graph::GetNode(size_t index)
1443 {
1444  JLM_ASSERT(index < NumNodes());
1445  return *Nodes_[index];
1446 }
1447 
1448 ArgumentNode &
1450 {
1451  auto node = new ArgumentNode(*this);
1452  ArgumentNodes_.emplace_back(node);
1453  return *node;
1454 }
1455 
1456 size_t
1457 Graph::NumArgumentNodes() const noexcept
1458 {
1459  return ArgumentNodes_.size();
1460 }
1461 
1462 Node &
1464 {
1465  JLM_ASSERT(index < NumArgumentNodes());
1466  return *ArgumentNodes_[index];
1467 }
1468 
1469 ResultNode &
1471 {
1472  auto node = new ResultNode(*this);
1473  ResultNodes_.emplace_back(node);
1474  return *node;
1475 }
1476 
1477 size_t
1478 Graph::NumResultNodes() const noexcept
1479 {
1480  return ResultNodes_.size();
1481 }
1482 
1483 Node &
1485 {
1486  JLM_ASSERT(index < NumResultNodes());
1487  return *ResultNodes_[index];
1488 }
1489 
1490 Edge &
1491 Graph::CreateEdge(Port & from, Port & to, bool directed)
1492 {
1493  // Edges must be between ports in this graph
1494  JLM_ASSERT(&from.GetGraph() == this);
1495  JLM_ASSERT(&to.GetGraph() == this);
1496 
1497  // Edge's constructor informs the ports about the edge
1498  auto edge = new Edge(from, to, directed);
1499  Edges_.emplace_back(edge);
1500  return *edge;
1501 }
1502 
1503 size_t
1504 Graph::NumEdges() const noexcept
1505 {
1506  return Edges_.size();
1507 }
1508 
1509 Edge &
1510 Graph::GetEdge(size_t index)
1511 {
1512  JLM_ASSERT(index < NumEdges());
1513  return *Edges_[index];
1514 }
1515 
1516 Edge *
1518 {
1519  for (auto edge : a.GetConnections())
1520  {
1521  if (edge->IsDirected() && &edge->GetFrom() != &a)
1522  continue;
1523  if (&edge->GetOtherEnd(a) == &b)
1524  return edge;
1525  }
1526  return nullptr;
1527 }
1528 
1529 GraphElement *
1530 Graph::GetElementFromProgramObject(uintptr_t object) const
1531 {
1532  if (auto it = ProgramObjectMapping_.find(object); it != ProgramObjectMapping_.end())
1533  return it->second;
1534  return nullptr;
1535 }
1536 
1537 void
1539 {
1540  JLM_ASSERT(&element.GetGraph() == this);
1541 
1542  uintptr_t object = element.GetProgramObject();
1543  JLM_ASSERT(object != 0);
1544 
1545  auto & slot = ProgramObjectMapping_[object];
1546  JLM_ASSERT(slot == nullptr && "Trying to map a GraphElement to an already mapped program object");
1547  slot = &element;
1548 }
1549 
1550 void
1552 {
1553  size_t erased = ProgramObjectMapping_.erase(object);
1554  JLM_ASSERT(erased == 1);
1555 }
1556 
1557 void
1559 {
1561 
1562  for (auto & arg : ArgumentNodes_)
1563  arg->Finalize();
1564  // Nodes with sub graphs also finalize them
1565  for (auto & node : Nodes_)
1566  node->Finalize();
1567  for (auto & res : ResultNodes_)
1568  res->Finalize();
1569  for (auto & edge : Edges_)
1570  edge->Finalize();
1571 }
1572 
1573 void
1574 Graph::OutputASCII(std::ostream & out, size_t indent) const
1575 {
1576  withIndent(out, indent) << "{" << std::endl;
1577  indent++;
1578 
1579  // Use a single ARG line for all graph arguments
1580  bool anyArguments = false;
1581  for (auto & arg : ArgumentNodes_)
1582  {
1583  if (!anyArguments)
1584  withIndent(out, indent) << "ARG ";
1585  else
1586  out << ", ";
1587  anyArguments = true;
1588  arg->OutputASCII(out, indent);
1589  }
1590  if (anyArguments)
1591  out << std::endl;
1592 
1593  // Print all other nodes in order
1594  for (auto & node : Nodes_)
1595  {
1596  // Will also print sub graphs recursively
1597  node->OutputASCII(out, indent);
1598  }
1599 
1600  // Use a single RES line for all graph results
1601  bool anyResults = false;
1602  for (auto & res : ResultNodes_)
1603  {
1604  if (!anyResults)
1605  withIndent(out, indent) << "RES ";
1606  else
1607  out << ", ";
1608  anyResults = true;
1609  res->OutputASCII(out, indent);
1610  }
1611  if (anyResults)
1612  out << std::endl;
1613 
1614  indent--;
1615  withIndent(out, indent) << "}" << std::endl;
1616 }
1617 
1618 void
1619 Graph::OutputDot(std::ostream & out, size_t indent) const
1620 {
1621  withIndent(out, indent) << "digraph " << GetFullId() << " {" << std::endl;
1622  indent++;
1623 
1624  // Default node attributes. Filling nodes by default makes them easier to click
1625  withIndent(out, indent)
1626  << "node[shape=box style=filled fillcolor=white width=0.1 height=0.1 margin=0.05];"
1627  << std::endl;
1628  withIndent(out, indent) << "penwidth=6;" << std::endl;
1629  if (HasLabel())
1630  {
1631  withIndent(out, indent) << "label=";
1632  printIdentifierSafe(out, GetLabel());
1633  out << std::endl;
1634  }
1635  withIndent(out, indent);
1637  out << std::endl;
1638 
1639  // Helper function used to print argument nodes and result nodes
1640  auto PrintOrderedSubgraph = [&out](auto & nodes, const char * rank, size_t indent)
1641  {
1642  if (nodes.empty())
1643  return;
1644  withIndent(out, indent++) << "{" << std::endl;
1645  withIndent(out, indent) << "rank=" << rank << ";" << std::endl;
1646  for (size_t i = 0; i < nodes.size(); i++)
1647  {
1648  nodes[i]->OutputDot(out, indent);
1649 
1650  // Use invisible edges to order nodes in the subgraph
1651  if (i != 0)
1652  withIndent(out, indent) << nodes[i - 1]->GetFullId() << " -> " << nodes[i]->GetFullId()
1653  << "[style=invis];" << std::endl;
1654  }
1655  withIndent(out, --indent) << "}" << std::endl;
1656  };
1657 
1658  PrintOrderedSubgraph(ArgumentNodes_, "source", indent);
1659 
1660  for (auto & node : Nodes_)
1661  {
1662  node->OutputDot(out, indent);
1663  }
1664 
1665  PrintOrderedSubgraph(ResultNodes_, "sink", indent);
1666 
1667  for (auto & edge : Edges_)
1668  {
1669  edge->OutputDot(out, indent);
1670  }
1671 
1672  indent--;
1673  withIndent(out, indent) << "}" << std::endl;
1674 }
1675 
1676 void
1677 Graph::outputJson(std::ostream & out, size_t indent) const
1678 {
1679  bool firstField = true;
1680  outputJsonObjectOpening(out, indent, firstField);
1681  indent++;
1682 
1683  // If we are a subgraph, list both the node and its parent graph
1684  if (IsSubgraph())
1685  {
1687  out << '"';
1688  ParentNode_->PrintFullId(out);
1689  out << '"';
1690 
1692  out << '"';
1694  out << '"';
1695  }
1696 
1697  // Arguments
1698  if (!ArgumentNodes_.empty())
1699  {
1700  printNextJsonField(out, JSON_ARGUMENTS_FIELD, indent, firstField);
1702  }
1703 
1704  // Nodes
1705  if (!Nodes_.empty())
1706  {
1707  printNextJsonField(out, JSON_NODES_FIELD, indent, firstField);
1709  }
1710 
1711  // Results
1712  if (!ResultNodes_.empty())
1713  {
1714  printNextJsonField(out, JSON_RESULTS_FIELD, indent, firstField);
1716  }
1717 
1718  // Edges
1719  if (!Edges_.empty())
1720  {
1721  printNextJsonField(out, JSON_EDGES_FIELD, indent, firstField);
1723  }
1724 
1725  indent--;
1726  outputJsonObjectClosing(out, indent, firstField);
1727 }
1728 
1729 void
1730 Graph::Output(std::ostream & out, OutputFormat format, size_t indent) const
1731 {
1733 
1734  switch (format)
1735  {
1736  case OutputFormat::ASCII:
1737  OutputASCII(out, indent);
1738  break;
1739  case OutputFormat::Dot:
1740  OutputDot(out, indent);
1741  break;
1742  case OutputFormat::Json:
1743  outputJson(out, indent);
1744  break;
1745  default:
1746  JLM_UNREACHABLE("Unknown output format");
1747  }
1748 }
1749 
1750 Graph &
1752 {
1753  auto graph = new Graph(*this);
1754  Graphs_.emplace_back(graph);
1755  return *graph;
1756 }
1757 
1758 size_t
1759 Writer::NumGraphs() const noexcept
1760 {
1761  return Graphs_.size();
1762 }
1763 
1764 Graph &
1765 Writer::GetGraph(size_t index)
1766 {
1767  JLM_ASSERT(index < NumGraphs());
1768  return *Graphs_[index];
1769 }
1770 
1771 Graph &
1773 {
1774  auto graph = new Graph(*this, parentNode);
1775  Graphs_.emplace_back(graph);
1776  return *graph;
1777 }
1778 
1779 GraphElement *
1781 {
1782  for (auto & graph : Graphs_)
1783  if (auto found = graph->GetElementFromProgramObject(object))
1784  return found;
1785 
1786  return nullptr;
1787 }
1788 
1789 size_t
1791 {
1792  size_t & nextValue = NextUniqueIdStubSuffix_[idStub];
1793  return nextValue++;
1794 }
1795 
1796 void
1798 {
1799  for (auto & graph : Graphs_)
1800  if (!graph->IsSubgraph())
1801  graph->Finalize();
1802 }
1803 
1804 void
1805 Writer::outputAllGraphs(std::ostream & out, OutputFormat format)
1806 {
1807  Finalize();
1808 
1809  switch (format)
1810  {
1811  case OutputFormat::ASCII:
1812  for (auto & graph : Graphs_)
1813  {
1814  // In ASCII printing, subgraphs are printed inside their nodes,
1815  // so we only output root graphs in this loop
1816  if (!graph->IsSubgraph())
1817  graph->Output(out, format, 0);
1818  }
1819  break;
1820 
1821  case OutputFormat::Dot:
1822  for (auto & graph : Graphs_)
1823  {
1824  graph->Output(out, format, 0);
1825  }
1826  break;
1827 
1828  case OutputFormat::Json:
1829  {
1830  out << "{" << std::endl;
1831  bool first = true;
1832  for (auto & graph : Graphs_)
1833  {
1834  if (first)
1835  first = false;
1836  else
1837  out << "," << std::endl;
1838  graph->Output(out, format, 1);
1839  }
1840  out << std::endl << "}" << std::endl;
1841  break;
1842  }
1843 
1844  default:
1845  JLM_UNREACHABLE("Unknown OutputFormat");
1846  }
1847 }
1848 }
bool CanBeEdgeHead() const override
void SetOutsideSource(const Port &outsideSource)
const char * GetIdPrefix() const override
void OutputASCII(std::ostream &out, size_t indent) const override
void SetStyle(std::string style)
void SetArrowTail(std::string arrow)
Port & GetOtherEnd(const Port &end)
const char * GetIdPrefix() const override
Graph & GetGraph() override
std::string_view getDirection() const
Edge(Port &from, Port &to, bool directed)
void OutputDot(std::ostream &out, size_t indent) const
void SetArrowHead(std::string arrow)
void outputJson(std::ostream &out, size_t indent) const
void outputJsonObjectOpening(std::ostream &out, size_t indent, bool &firstField) const
virtual Graph & GetGraph()=0
void SetLabel(std::string label)
uintptr_t GetProgramObject() const noexcept
std::string GetFullId() const
void SetAttributeObject(const std::string &attribute, uintptr_t object)
void AppendToLabel(std::string_view text, std::string_view sep="\n")
void SetAttribute(const std::string &attribute, std::string value)
virtual const char * GetIdPrefix() const =0
std::string_view GetLabelOr(std::string_view otherwise) const
const std::string & GetLabel() const
void PrintFullId(std::ostream &out) const
bool RemoveAttribute(const std::string &attribute)
std::optional< uintptr_t > GetAttributeObject(const std::string &attribute) const
std::unordered_map< std::string, AttributeValue > AttributeMap_
void OutputAttribute(std::ostream &out, const std::string &name, AttributeOutputFormat format) const
void OutputAttributes(std::ostream &out, AttributeOutputFormat format) const
bool HasAttribute(const std::string &attribute) const
const GraphElement * GetAttributeGraphElement(const std::string &attribute) const
std::optional< size_t > UniqueIdSuffix_
std::optional< std::string_view > GetAttributeString(const std::string &attribute) const
void SetProgramObjectUintptr(uintptr_t object)
void SetAttributeGraphElement(const std::string &attribute, const GraphElement &element)
bool HasProgramObject() const noexcept
std::vector< std::unique_ptr< ArgumentNode > > ArgumentNodes_
ArgumentNode & CreateArgumentNode()
Node & GetNode(size_t index)
void Output(std::ostream &out, OutputFormat format, size_t indent) const
GraphElement * GetElementFromProgramObject(uintptr_t object) const
std::unordered_map< uintptr_t, GraphElement * > ProgramObjectMapping_
Node & GetResultNode(size_t index)
Edge & GetEdge(size_t index)
Edge * GetEdgeBetween(Port &a, Port &b)
void OutputASCII(std::ostream &out, size_t indent) const
const char * GetIdPrefix() const override
void RemoveProgramObjectMapping(uintptr_t object)
std::vector< std::unique_ptr< Edge > > Edges_
Graph & GetGraph() override
ResultNode & CreateResultNode()
size_t NumResultNodes() const noexcept
std::vector< std::unique_ptr< ResultNode > > ResultNodes_
void Finalize() override
void OutputDot(std::ostream &out, size_t indent) const
size_t NumEdges() const noexcept
size_t NumNodes() const noexcept
Node & GetArgumentNode(size_t index)
InOutNode & CreateInOutNode(size_t inputPorts, size_t outputPorts)
size_t NumArgumentNodes() const noexcept
void outputJson(std::ostream &out, size_t indent) const
std::vector< std::unique_ptr< Node > > Nodes_
Edge & CreateEdge(Port &from, Port &to, bool directed)
void MapProgramObjectToElement(GraphElement &element)
void OutputSubgraphs(std::ostream &out, OutputFormat format, size_t indent) const override
std::unordered_map< std::string, std::string > HtmlTableAttributes_
std::vector< std::unique_ptr< OutputPort > > OutputPorts_
void OutputDot(std::ostream &out, size_t indent) const override
InOutNode(Graph &graph, size_t inputPorts, size_t outputPorts)
void outputJson(std::ostream &out, size_t indent) const override
void SetFillColor(std::string color) override
OutputPort & GetOutputPort(size_t index)
std::vector< Graph * > SubGraphs_
std::vector< std::unique_ptr< InputPort > > InputPorts_
void SetShape(std::string) override
InputPort & GetInputPort(size_t index)
Graph & GetSubgraph(size_t index)
void OutputASCII(std::ostream &out, size_t indent) const override
void SetHtmlTableAttribute(std::string name, std::string value)
OutputPort & CreateOutputPort()
const char * GetIdPrefix() const override
bool CanBeEdgeTail() const override
void SetFillColor(std::string color) override
void outputJson(std::ostream &out, size_t indent) const
void OutputDotPortId(std::ostream &out) const override
InputPort(InOutNode &node)
Node & GetNode() override
Node & GetNode() override
const char * GetIdPrefix() const override
Graph & GetGraph() override
virtual void OutputDot(std::ostream &out, size_t indent) const
virtual void OutputSubgraphs(std::ostream &out, OutputFormat format, size_t indent) const
void OutputDotPortId(std::ostream &out) const override
virtual void OutputASCII(std::ostream &out, size_t indent) const
virtual void outputJson(std::ostream &out, size_t indent) const
virtual void SetShape(std::string shape)
void SetFillColor(std::string color) override
void SetFillColor(std::string color) override
void outputJson(std::ostream &out, size_t indent) const
bool CanBeEdgeHead() const override
void OutputDotPortId(std::ostream &out) const override
const char * GetIdPrefix() const override
const std::vector< Edge * > & GetConnections() const
virtual bool CanBeEdgeTail() const
virtual bool CanBeEdgeHead() const
std::vector< Edge * > Connections_
bool HasIncomingEdges() const
void OutputIncomingEdgesASCII(std::ostream &out) const
Graph & GetGraph() override
void OnEdgeAdded(Edge &edge)
bool HasOutgoingEdges() const
virtual void OutputDotPortId(std::ostream &out) const =0
virtual Node & GetNode()=0
void SetOutsideDestination(const Port &outsideSource)
void OutputASCII(std::ostream &out, size_t indent) const override
const char * GetIdPrefix() const override
bool CanBeEdgeTail() const override
GraphElement * GetElementFromProgramObject(uintptr_t object) const
size_t GetNextUniqueIdStubSuffix(const char *idStub)
std::unordered_map< std::string, size_t > NextUniqueIdStubSuffix_
size_t NumGraphs() const noexcept
Graph & CreateSubGraph(Node &parentNode)
std::vector< std::unique_ptr< Graph > > Graphs_
void outputAllGraphs(std::ostream &out, OutputFormat format)
Graph & GetGraph(size_t index)
#define JLM_ASSERT(x)
Definition: common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition: common.hpp:43
std::string Edge(jlm::rvsdg::Output *output, jlm::rvsdg::Input *input, std::unordered_map< rvsdg::Output *, ViewColors > &tailLabel, bool back_edge=false)
Definition: view.cpp:172
static bool empty(const rvsdg::GammaNode *gamma)
Definition: pull.cpp:49
static std::string indent(size_t depth)
Definition: view.cpp:22
static void printJsonString(std::ostream &out, std::string_view string)
static const char *const JSON_PARENT_NODE_FIELD
Definition: GraphWriter.cpp:37
static void printStringAsHtmlAttributeName(std::ostream &out, std::string_view string)
static void outputJsonObjectClosing(std::ostream &out, size_t indent, bool firstField)
static const char *const JSON_NODES_FIELD
Definition: GraphWriter.cpp:40
static const char *const DOT_EDGE_ID_ATTRIBUTE
Definition: GraphWriter.cpp:17
std::ostream & withIndent(std::ostream &out, size_t indent)
static const char *const JSON_LABEL_FIELD
Definition: GraphWriter.cpp:22
static const char *const JSON_SUBGRAPHS_FIELD
Definition: GraphWriter.cpp:33
static void printIdentifierSafe(std::ostream &out, std::string_view string)
Definition: GraphWriter.cpp:81
static const char *const DOT_TOOLTIP_ATTRIBUTE
Definition: GraphWriter.cpp:15
static void printJsonElementMap(std::ostream &out, size_t indent, const T &elements)
static const char *const DOT_HTML_TABLE_BGCOLOR_ATTRIBUTE
Definition: GraphWriter.cpp:19
static const char *const JSON_HTML_TABLE_ATTRIBUTES_FIELD
Definition: GraphWriter.cpp:34
static bool looksLikeIdentifier(std::string_view string)
Definition: GraphWriter.cpp:50
static const char *const JSON_ATTRIBUTE_FIELD
Definition: GraphWriter.cpp:24
static const char *const JSON_NODE_TYPE_FIELD
Definition: GraphWriter.cpp:28
static std::ostream & printNextJsonField(std::ostream &out, std::string_view name, size_t indent, bool &firstField)
static const char *const JSON_OBJECT_POINTER_FIELD
Definition: GraphWriter.cpp:26
static const char *const JSON_IN_PORTS_FIELD
Definition: GraphWriter.cpp:31
static const char *const JSON_OUT_PORTS_FIELD
Definition: GraphWriter.cpp:32
static const char *const JSON_PARENT_GRAPH_FIELD
Definition: GraphWriter.cpp:38
static const char *const JSON_ARGUMENTS_FIELD
Definition: GraphWriter.cpp:39
static void outputKeyValuePair(std::ostream &out, std::string_view name, std::string_view value, AttributeOutputFormat format)
static void printStringAsHtmlText(std::ostream &out, std::string_view string, bool replaceNewlines)
static const char *const JSON_EDGES_FIELD
Definition: GraphWriter.cpp:42
static const char *const JSON_RESULTS_FIELD
Definition: GraphWriter.cpp:41
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35