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
|
subgraphs, or https://www.yworks.com/yed-live/, which is relatively good at
|
||||||
neighbourhoods and larger layouts) that can visualize that output.
|
neighbourhoods and larger layouts) that can visualize that output.
|
||||||
It allows debugging anything related to the IR.
|
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.
|
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.
|
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
|
## Miscellaneous
|
||||||
|
|
||||||
### Nullability
|
### 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.IrGraph;
|
||||||
import edu.kit.kastel.vads.compiler.ir.SsaTranslation;
|
import edu.kit.kastel.vads.compiler.ir.SsaTranslation;
|
||||||
import edu.kit.kastel.vads.compiler.ir.optimize.LocalValueNumbering;
|
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.lexer.Lexer;
|
||||||
import edu.kit.kastel.vads.compiler.parser.ParseException;
|
import edu.kit.kastel.vads.compiler.parser.ParseException;
|
||||||
import edu.kit.kastel.vads.compiler.parser.Parser;
|
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.parser.ast.ProgramTree;
|
||||||
import edu.kit.kastel.vads.compiler.semantic.SemanticAnalysis;
|
import edu.kit.kastel.vads.compiler.semantic.SemanticAnalysis;
|
||||||
import edu.kit.kastel.vads.compiler.semantic.SemanticException;
|
import edu.kit.kastel.vads.compiler.semantic.SemanticException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
@ -41,6 +41,14 @@ public class Main {
|
||||||
graphs.add(translation.translate());
|
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
|
// TODO: generate assembly and invoke gcc instead of generating abstract assembly
|
||||||
String s = new CodeGenerator().generateCode(graphs);
|
String s = new CodeGenerator().generateCode(graphs);
|
||||||
Files.writeString(output, s);
|
Files.writeString(output, s);
|
||||||
|
|
@ -58,4 +66,11 @@ public class Main {
|
||||||
throw new AssertionError("unreachable");
|
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();
|
return this.projectionInfo.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ProjectionInfo projectionInfo() {
|
||||||
|
return projectionInfo;
|
||||||
|
}
|
||||||
|
|
||||||
public sealed interface 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