Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GraphWriter API for programatically building graphs and outputting as ASCII or dot #403

Merged
merged 17 commits into from
Feb 29, 2024

Conversation

haved
Copy link
Collaborator

@haved haved commented Feb 20, 2024

This is the API I ended up with, it uses some friend declarations to ensure internal consistency, so the public API should be hard to misuse.

You create graphs, where you can add nodes and edges. These can all have labels and arbitrary attributes set, which will be included in the dot output. The ASCII output ignores attributes for brevity.

There are also ArgumentNode, ResultNode and InOutNode that are nice when representing data flowing into the graph, through nodes, and out of the graph. InOutNodes have an arbitrary number of input ports and output ports, which can also have labels and attributes. They can also have subgraphs.

The InOutNodes use the HTML table labels in dot. In dot, subgraphs are rendered as separate graphs, so I was planning on customizing a JavaScript library for GraphViz to make graphs inside nodes render nicely when requested.

The ArgumentNodes and ResultNodes have the option of being connected to ports in outside graphs. Otherwise, edges must go between ports within the same graph.

All GraphElements have the option of being mapped to a program object (any type of pointer), which serves two purposes: You can look up elements by program object, to aid in building the graphs, and you can refer to program objects by pointer in attributes, and have the pointer be replaced by the short unique id of the graph element.

I'm very open to suggestions for changes to the interface, internals or the output, as this should be something usable for everybody.

There is currently a helper function for setting the background color on a node/port.
I could envision a lot more helper functions, to make it easier for the end user to find useful and correct attributes, e.g.

edge.SetAttribute("style", "dashed");
// becomes
edge.SetStyle(EdgeStyle::Dashed);

The code takes care to escape strings in both regular dot and the HTML sub-language.
I will of course add tests to all the functionality that it makes sense to test.

Under follows some examples of API usage, and the corresponding output in ASCII and rendered GraphViz:

A regular graph
  jlm::util::GraphWriter writer;
  auto & graph = writer.CreateGraph();
  graph.SetAttribute("rankdir", "LR");

  auto & node = graph.CreateNode();
  node.SetLabel("DELTA");
  node.SetAttribute("shape", "oval");

  auto & node2 = graph.CreateNode();
  node2.SetLabel("ALLOCA");

  auto & edge = graph.CreateDirectedEdge(node, node2);
  edge.SetAttribute("style", "dashed");
  edge.SetAttribute("minlen", "3");
  edge.SetAttribute("headlabel", "Pointee");
  edge.SetAttribute("taillabel", "Pointer");

  auto & node3 = graph.CreateNode();
  node3.SetLabel("ALLOCA2");
  auto & edge2 = graph.CreateUndirectedEdge(node2, node3);
  edge2.SetLabel("Unified");

becomes

{
  node0:DELTA
  node1:ALLOCA<-[node0, node2]
  node2:ALLOCA2<-node1
}

and
image

A dataflow graph
  jlm::util::GraphWriter writer;
  auto & graph = writer.CreateGraph();
  auto & arg0 = graph.CreateArgumentNode();
  arg0.SetLabel("CTX");
  arg0.SetFillColor("yellow");
  auto & res0 = graph.CreateResultNode();

  auto & node0 = graph.CreateInOutNode(0, 1);
  node0.SetLabel("\"Cool Name!\"");
  node0.SetFillColor("gray");

  auto & node1 = graph.CreateInOutNode(3, 3);
  node1.SetLabel("<Main Node>");
  node1.GetOutputPort(1).SetFillColor("yellow");

  auto & node2 = graph.CreateInOutNode(2, 1);
  node2.SetLabel("End Node");

  graph.CreateDirectedEdge(arg0, node1.GetInputPort(2));

  graph.CreateDirectedEdge(node0.GetOutputPort(0), node1.GetInputPort(1));
  graph.CreateDirectedEdge(node0.GetOutputPort(0), node1.GetInputPort(2));

  graph.CreateDirectedEdge(node1.GetOutputPort(0), node2.GetInputPort(0));
  graph.CreateDirectedEdge(node1.GetOutputPort(1), node2.GetInputPort(1));

  graph.CreateDirectedEdge(node2.GetOutputPort(0), res0);

becomes

{
  ARG a0
  o0 := "\"Cool Name!\""
  o1, o2, o3 := "<Main Node>" [], o0, [a0, o0]
  o4 := "End Node" o1, o2
  RES o4
}

and
image

A graph with a subgraph
  jlm::util::GraphWriter writer;
  auto & graph = writer.CreateGraph();

  auto & source = graph.CreateNode();
  auto & node = graph.CreateInOutNode(3, 3);
  graph.CreateDirectedEdge(source, node.GetInputPort(0));
  graph.CreateDirectedEdge(node.GetOutputPort(0), node.GetInputPort(2));

  auto & subgraph = node.CreateSubgraph();
  auto & arg0 = subgraph.CreateArgumentNode();
  auto & arg1 = subgraph.CreateArgumentNode();
  arg0.SetOutsideSource(node.GetInputPort(0));
  arg1.SetOutsideSource(node.GetInputPort(2));

  auto & res0 = subgraph.CreateResultNode();
  auto & res1 = subgraph.CreateResultNode();
  res0.SetOutsideDestination(node.GetOutputPort(2));
  res1.SetOutsideDestination(node.GetOutputPort(1));

  subgraph.CreateDirectedEdge(arg0, res1);
  subgraph.CreateDirectedEdge(arg1, res0);

becomes

{
  node0:NODE
  o0, o1, o2 := NODE node0, [], o0
    {
      ARG a0 <= node0, a1 <= o0
      RES a1 => o2, a0 => o1
    }
}

and
image

This PR is part of #308

@haved haved requested a review from phate February 20, 2024 13:26
Copy link
Owner

@phate phate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. Thank you sooo much for doing this!

I got interrupted and have to stop. I will just leave the comments that I have now, but I will need another round.

jlm/util/Makefile.sub Show resolved Hide resolved
jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
@haved
Copy link
Collaborator Author

haved commented Feb 22, 2024

I think I made most of your suggested changes. It was nice to not have switch-case on format all over the place.

@sjalander
Copy link
Collaborator

I have only a minor comment in that I prefer the layout of the nodes in @davidmetz graphs #308 (comment) over the square table shown in the "A dataflow graph" above.

I talked with David, and he says that he also use HTML table, so I guess it's not difficult to modify the layout.
Guess example code can be found here davidmetz@90c5485.

@haved
Copy link
Collaborator Author

haved commented Feb 23, 2024

@sjalander I copied most of his table layout, was it something like this you wanted?
image
image
I'm not entirely sure why the text is left-adjusted, center is supposed to be default, but putting tables inside tables might mess with it a bit.

I can also reduce the font size of argument and result nodes to match the input and output ports, since they pretty much represent freestanding ports.

On another note, input and output ports are currently named with globally unique names, as a fallback if they don't have a label. It might be better to use either a blank box or the port's index within the node.

@sjalander
Copy link
Collaborator

@haved I think that looks better.

Copy link
Owner

@phate phate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that it took so long. I was sick the last 3 days.

Nothing major. The only reason why I block this is because of the missing unit tests.

Great work!

jlm/util/GraphWriter.cpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Show resolved Hide resolved
jlm/util/GraphWriter.hpp Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/GraphWriter.hpp Outdated Show resolved Hide resolved
jlm/util/Makefile.sub Show resolved Hide resolved
@sjalander
Copy link
Collaborator

@haved @phate
Javascript is mentioned as a viewer, and I fully understand that having a browser based viewer can be beneficial.
I just want to throw out there that we already use one modified viewer that is used for working with the HLS backend.
https://github.com/davidmetz/xdot.py

@haved
Copy link
Collaborator Author

haved commented Feb 29, 2024

@sjalander Absolutely, it is very nice to be able to pan around and zoom, and it looks crisp.

The dot that is emitted by this PR is quite close to the dot David has, especially if you add "tooltip" attributes. I don't quite know what op_HLS_LOCAL_MEM__ or "JHLS vcd" means, so I can't comment on those features.

@haved haved requested a review from phate February 29, 2024 16:04
Copy link
Owner

@phate phate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Fantastic work. Thanks a lot!

@phate phate enabled auto-merge (squash) February 29, 2024 19:05
@phate phate merged commit 4f96ff1 into phate:master Feb 29, 2024
9 checks passed
@haved haved deleted the graph-output branch February 29, 2024 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants