Introduce yComp printer
This commit is contained in:
parent
6b7584b3cc
commit
b9d4e06dfc
4 changed files with 361 additions and 2 deletions
38
README.md
38
README.md
|
|
@ -71,10 +71,48 @@ To figure out the cause, we provide utilities that ease debugging.
|
|||
subgraphs, or https://www.yworks.com/yed-live/, which is relatively good at
|
||||
neighbourhoods and larger layouts) that can visualize that output.
|
||||
It allows debugging anything related to the IR.
|
||||
- `edu.kit.kastel.vads.compiler.ir.util.YCompPrinter` can generate output for [yComp](https://pp.ipd.kit.edu/firm/yComp.html).
|
||||
This tool is more sophisticated than GraphViz. See below for further information.
|
||||
|
||||
We also try to keep track of source positions as much as possible through the compiler.
|
||||
You can get rid of all that, but it can be helpful to track down where something comes from.
|
||||
|
||||
### yComp
|
||||
|
||||
To use yComp, you need to patch the provided start script to make it work with modern Java versions.
|
||||
You can copy-paste the following script:
|
||||
```sh
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
YCOMP="$0"
|
||||
while [ -L "$YCOMP" ]; do
|
||||
LINK="$(readlink "$YCOMP")"
|
||||
case "$LINK" in
|
||||
/*) YCOMP="$LINK";;
|
||||
*) YCOMP="${YCOMP%/*}/$LINK";;
|
||||
esac
|
||||
done
|
||||
|
||||
# 1.5.0_22, 1.8, 9, 11.0.2...
|
||||
# We only match on the first field
|
||||
JAVA_VERSION="$(java -version 2>&1 | awk -F '"' '/version/ {print $2}' | cut -d'.' -f1)"
|
||||
|
||||
ADDITIONAL_ARGUMENTS=""
|
||||
|
||||
if [ "$JAVA_VERSION" -gt 9 ]; then
|
||||
echo "Java 9+ detected, opening potentially required packages"
|
||||
ADDITIONAL_ARGUMENTS='--add-opens java.desktop/sun.swing=ALL-UNNAMED'
|
||||
fi
|
||||
|
||||
echo "Commandline is: 'java -Xmx512m $ADDITIONAL_ARGUMENTS -jar "${YCOMP%/*}/yComp.jar" "$@"'"
|
||||
java -Xmx512m $ADDITIONAL_ARGUMENTS -jar "${YCOMP%/*}/yComp.jar" "$@"
|
||||
```
|
||||
|
||||
You can directly dump graphs by setting the `DUMP_GRAPHS` environment variable to `vcg` or by passing `-DdumpGraphs=vcg`
|
||||
to the compiler as a JVM argument (not as a program argument!).
|
||||
The graphs will be dumped to the `graphs` directory relative to the output file.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### Nullability
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import edu.kit.kastel.vads.compiler.backend.aasm.CodeGenerator;
|
|||
import edu.kit.kastel.vads.compiler.ir.IrGraph;
|
||||
import edu.kit.kastel.vads.compiler.ir.SsaTranslation;
|
||||
import edu.kit.kastel.vads.compiler.ir.optimize.LocalValueNumbering;
|
||||
import edu.kit.kastel.vads.compiler.ir.util.YCompPrinter;
|
||||
import edu.kit.kastel.vads.compiler.lexer.Lexer;
|
||||
import edu.kit.kastel.vads.compiler.parser.ParseException;
|
||||
import edu.kit.kastel.vads.compiler.parser.Parser;
|
||||
|
|
@ -12,7 +13,6 @@ import edu.kit.kastel.vads.compiler.parser.ast.FunctionTree;
|
|||
import edu.kit.kastel.vads.compiler.parser.ast.ProgramTree;
|
||||
import edu.kit.kastel.vads.compiler.semantic.SemanticAnalysis;
|
||||
import edu.kit.kastel.vads.compiler.semantic.SemanticException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
|
@ -41,6 +41,14 @@ public class Main {
|
|||
graphs.add(translation.translate());
|
||||
}
|
||||
|
||||
if ("vcg".equals(System.getenv("DUMP_GRAPHS")) || "vcg".equals(System.getProperty("dumpGraphs"))) {
|
||||
Path tmp = output.toAbsolutePath().resolveSibling("graphs");
|
||||
Files.createDirectory(tmp);
|
||||
for (IrGraph graph : graphs) {
|
||||
dumpGraph(graph, tmp, "before-codegen");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generate assembly and invoke gcc instead of generating abstract assembly
|
||||
String s = new CodeGenerator().generateCode(graphs);
|
||||
Files.writeString(output, s);
|
||||
|
|
@ -58,4 +66,11 @@ public class Main {
|
|||
throw new AssertionError("unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpGraph(IrGraph graph, Path path, String key) throws IOException {
|
||||
Files.writeString(
|
||||
path.resolve(graph.name() + "-" + key + ".vcg"),
|
||||
YCompPrinter.print(graph)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,10 @@ public final class ProjNode extends Node {
|
|||
return this.projectionInfo.toString();
|
||||
}
|
||||
|
||||
public ProjectionInfo projectionInfo() {
|
||||
return projectionInfo;
|
||||
}
|
||||
|
||||
public sealed interface ProjectionInfo {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,302 @@
|
|||
package edu.kit.kastel.vads.compiler.ir.util;
|
||||
|
||||
import edu.kit.kastel.vads.compiler.ir.IrGraph;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.BinaryOperationNode;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.Block;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.ConstIntNode;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.Node;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.Phi;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.ProjNode;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.ProjNode.SimpleProjectionInfo;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.ReturnNode;
|
||||
import edu.kit.kastel.vads.compiler.ir.node.StartNode;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class YCompPrinter {
|
||||
|
||||
private final Map<Block, Set<Node>> clusters = new HashMap<>();
|
||||
private final Map<Node, Integer> ids = new HashMap<>();
|
||||
private final IrGraph graph;
|
||||
private int nodeCounter = 0;
|
||||
private int blockCounter = 0;
|
||||
|
||||
public YCompPrinter(IrGraph graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
private void prepare(Node node, Set<Node> seen) {
|
||||
if (!seen.add(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(node instanceof Block)) {
|
||||
this.clusters.computeIfAbsent(
|
||||
node.block(),
|
||||
_ -> Collections.newSetFromMap(new IdentityHashMap<>())
|
||||
)
|
||||
.add(node);
|
||||
}
|
||||
for (Node predecessor : node.predecessors()) {
|
||||
prepare(predecessor, seen);
|
||||
}
|
||||
if (node == this.graph.endBlock()) {
|
||||
this.clusters.put(this.graph.endBlock(), Set.of());
|
||||
}
|
||||
}
|
||||
|
||||
public static String print(IrGraph graph) {
|
||||
YCompPrinter printer = new YCompPrinter(graph);
|
||||
printer.prepare(graph.endBlock(), new HashSet<>());
|
||||
return printer.dumpGraphAsString();
|
||||
}
|
||||
|
||||
private String dumpGraphAsString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
result.append("graph: {");
|
||||
String graphName = this.graph.name();
|
||||
result.append("\n title: ").append('"').append(graphName).append('"').append("\n");
|
||||
|
||||
result.append("""
|
||||
display_edge_labels: yes
|
||||
layoutalgorithm: mindepth //$ "Compilergraph"
|
||||
manhattan_edges: yes
|
||||
port_sharing: no
|
||||
orientation: top_to_bottom
|
||||
""".indent(2));
|
||||
|
||||
for (VcgColor color : VcgColor.values()) {
|
||||
result.append("\n colorentry ").append(color.id()).append(": ").append(color.getRgb());
|
||||
}
|
||||
|
||||
result.append("\n");
|
||||
|
||||
result.append(formatMethod(graphName).indent(2));
|
||||
|
||||
result.append("}");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String formatMethod(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
result.append("graph: {");
|
||||
result.append("\n title: ").append('"').append("method").append('"');
|
||||
result.append("\n label: ").append('"').append(name).append('"');
|
||||
result.append("\n color: ").append(VcgColor.ROOT_BLOCK.id());
|
||||
|
||||
for (Entry<Block, Set<Node>> entry : this.clusters.entrySet()) {
|
||||
result.append("\n").append(formatBlock(entry.getKey(), entry.getValue()).indent(2));
|
||||
}
|
||||
|
||||
result.append("}");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String formatBlock(Block block, Set<Node> nodes) {
|
||||
StringBuilder result = new StringBuilder("graph: {");
|
||||
result.append("\n title: " + '"').append(nodeTitle(block)).append('"');
|
||||
result.append("\n label: " + '"').append(nodeLabel(block)).append('"');
|
||||
result.append("\n status: clustered");
|
||||
result.append("\n color: ").append(VcgColor.BLOCK.id());
|
||||
result.append("\n");
|
||||
|
||||
for (Node node : nodes) {
|
||||
result.append(formatNode(node).indent(2));
|
||||
result.append(formatInputEdges(node).indent(2));
|
||||
}
|
||||
result.append(formatControlflowEdges(block));
|
||||
|
||||
result.append(formatSchedule(block));
|
||||
|
||||
result.append("\n}");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String formatNode(Node node) {
|
||||
String infoText = "I am an info text for " + node;
|
||||
|
||||
String result = "node: {";
|
||||
result += "\n title: " + '"' + nodeTitle(node) + '"' + "\n";
|
||||
result += "\n label: " + '"' + nodeLabel(node) + '"' + "\n";
|
||||
result += "\n color: " + nodeColor(node).id();
|
||||
result += "\n info1: " + '"' + infoText + '"';
|
||||
result += "\n}";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String formatInputEdges(Node node) {
|
||||
var edges = IntStream.range(0, node.predecessors().size())
|
||||
.mapToObj(
|
||||
idx -> new Edge(
|
||||
node.predecessor(idx), node, idx, edgeColor(node.predecessor(idx), node)
|
||||
)
|
||||
)
|
||||
.toList();
|
||||
return formatEdges(edges, "\n priority: 50");
|
||||
}
|
||||
|
||||
private Optional<VcgColor> edgeColor(Node src, Node dst) {
|
||||
if (nodeColor(src) != VcgColor.NORMAL) {
|
||||
return Optional.of(nodeColor(src));
|
||||
}
|
||||
if (nodeColor(dst) != VcgColor.NORMAL) {
|
||||
return Optional.of(nodeColor(dst));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private String formatControlflowEdges(Block block) {
|
||||
StringJoiner result = new StringJoiner("\n");
|
||||
List<? extends Node> parents = block.predecessors();
|
||||
for (Node parent : parents) {
|
||||
if (parent instanceof ReturnNode) {
|
||||
// Return needs no label
|
||||
result.add(formatControlflowEdge(parent, block, ""));
|
||||
} else {
|
||||
throw new RuntimeException("Unknown paren type: " + parent);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String formatControlflowEdge(Node source, Block dst, String label) {
|
||||
String result = "edge: {";
|
||||
result += "\n sourcename: " + '"' + nodeTitle(source) + '"';
|
||||
result += "\n targetname: " + '"' + nodeTitle(dst) + '"';
|
||||
result += "\n label: " + '"' + label + '"';
|
||||
result += "\n color: " + VcgColor.CONTROL_FLOW.id();
|
||||
result += "\n}";
|
||||
return result;
|
||||
}
|
||||
|
||||
private String formatEdges(Collection<Edge> edges, String additionalProps) {
|
||||
StringJoiner result = new StringJoiner("\n");
|
||||
for (Edge edge : edges) {
|
||||
StringBuilder inner = new StringBuilder();
|
||||
// edge: {sourcename: "n74" targetname: "n71" label: "0" class:14 priority:50 color:blue}
|
||||
inner.append("edge: {");
|
||||
inner.append("\n sourcename: ").append('"').append(nodeTitle(edge.src())).append('"');
|
||||
inner.append("\n targetname: ").append('"').append(nodeTitle(edge.dst())).append('"');
|
||||
inner.append("\n label: ").append('"').append(edge.index()).append('"');
|
||||
edge.color.ifPresent(color -> inner.append("\n color: ").append(color.id()));
|
||||
inner.append(additionalProps);
|
||||
inner.append("\n}");
|
||||
result.add(inner);
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String formatSchedule(Block block) {
|
||||
// Once you have a schedule, you might want to also emit it :)
|
||||
return formatEdges(List.of(), "\n color: " + VcgColor.SCHEDULE.id());
|
||||
}
|
||||
|
||||
@SuppressWarnings("DuplicateBranchesInSwitch")
|
||||
private VcgColor nodeColor(Node node) {
|
||||
return switch (node) {
|
||||
case BinaryOperationNode _ -> VcgColor.NORMAL;
|
||||
case Block _ -> VcgColor.NORMAL;
|
||||
case ConstIntNode _ -> VcgColor.NORMAL;
|
||||
case Phi _ -> VcgColor.PHI;
|
||||
case ProjNode proj -> {
|
||||
if (proj.projectionInfo() == SimpleProjectionInfo.SIDE_EFFECT) {
|
||||
yield VcgColor.MEMORY;
|
||||
} else if (proj.projectionInfo() == SimpleProjectionInfo.RESULT) {
|
||||
yield VcgColor.NORMAL;
|
||||
} else {
|
||||
yield VcgColor.NORMAL;
|
||||
}
|
||||
}
|
||||
case ReturnNode _ -> VcgColor.CONTROL_FLOW;
|
||||
case StartNode _ -> VcgColor.CONTROL_FLOW;
|
||||
};
|
||||
}
|
||||
|
||||
private String nodeTitle(Node node) {
|
||||
if (node instanceof Block block) {
|
||||
if (block == this.graph.startBlock()) {
|
||||
return "start-block";
|
||||
} else if (block == this.graph.endBlock()) {
|
||||
return "end-block";
|
||||
}
|
||||
return "block-" + idFor(block);
|
||||
}
|
||||
return "node-" + idFor(node);
|
||||
}
|
||||
|
||||
private String nodeLabel(Node node) {
|
||||
if (node == this.graph.startBlock()) {
|
||||
return "start-block";
|
||||
} else if (node == this.graph.endBlock()) {
|
||||
return "end-block";
|
||||
}
|
||||
return node.toString();
|
||||
}
|
||||
|
||||
private int idFor(Node node) {
|
||||
if (node instanceof Block block) {
|
||||
return this.ids.computeIfAbsent(block, _ -> this.blockCounter++);
|
||||
}
|
||||
return this.ids.computeIfAbsent(node, _ -> this.nodeCounter++);
|
||||
}
|
||||
|
||||
private record Edge(Node src, Node dst, int index, Optional<VcgColor> color) {
|
||||
|
||||
}
|
||||
|
||||
private enum VcgColor {
|
||||
// colorentry 100: 204 204 204 gray
|
||||
// colorentry 101: 222 239 234 faint green
|
||||
// colorentry 103: 242 242 242 white-ish
|
||||
// colorentry 104: 153 255 153 light green
|
||||
// colorentry 105: 153 153 255 blue
|
||||
// colorentry 106: 255 153 153 red
|
||||
// colorentry 107: 255 255 153 yellow
|
||||
// colorentry 108: 255 153 255 pink
|
||||
// colorentry 110: 127 127 127 dark gray
|
||||
// colorentry 111: 153 255 153 light green
|
||||
// colorentry 114: 153 153 255 blue
|
||||
CONTROL_FLOW("255 153 153"),
|
||||
MEMORY("153 153 255"),
|
||||
NORMAL("242 242 242"),
|
||||
SPECIAL("255 153 255"),
|
||||
CONST("255 255 153"),
|
||||
PHI("153 255 153"),
|
||||
ROOT_BLOCK("204 204 204"),
|
||||
BLOCK("222 239 234"),
|
||||
SCHEDULE("255 153 255");
|
||||
|
||||
private final String rgb;
|
||||
|
||||
VcgColor(String rgb) {
|
||||
this.rgb = rgb;
|
||||
}
|
||||
|
||||
public String getRgb() {
|
||||
return rgb;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return 100 + ordinal();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue