add predecessor and successor data to srcgraph + graphviz dot export
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,3 +12,5 @@ jasmin.jar
|
|||||||
*.j
|
*.j
|
||||||
|
|
||||||
TestClass.stups
|
TestClass.stups
|
||||||
|
/DotOut.dot.svg
|
||||||
|
/DotOut.dot
|
||||||
|
109
src/main/java/codegen/sourcegraph/SourceBlock.java
Normal file
109
src/main/java/codegen/sourcegraph/SourceBlock.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class SourceBlock {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String label;
|
||||||
|
private final List<SourceInst> instructions;
|
||||||
|
private final Set<SourceBlock> pred;
|
||||||
|
private final Set<SourceBlock> succ;
|
||||||
|
|
||||||
|
public SourceBlock(String label) {
|
||||||
|
this.label = label;
|
||||||
|
this.id = String.valueOf(System.nanoTime());
|
||||||
|
this.instructions = new ArrayList<>();
|
||||||
|
this.pred = new HashSet<>();
|
||||||
|
this.succ = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceBlock() {
|
||||||
|
this("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.label.isBlank() && this.instructions.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addLine(String instruction, String... args) {
|
||||||
|
this.instructions.add(new SourceInst(instruction, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SourceInst> getInstructions() {
|
||||||
|
return this.instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSuccessor(SourceBlock block) {
|
||||||
|
this.succ.add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPredecessor(SourceBlock block) {
|
||||||
|
this.pred.add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<SourceBlock> getSuccessors() {
|
||||||
|
return this.succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<SourceBlock> getPredecessors() {
|
||||||
|
return this.pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String printInst() {
|
||||||
|
return this.instructions.stream()
|
||||||
|
.map(inst -> inst.toString().trim() + "\\n")
|
||||||
|
.map(inst -> inst.replace("\"", "\\\""))
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSuccessors(Set<SourceBlock> successors) {
|
||||||
|
this.succ.addAll(successors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPredecessors(Set<SourceBlock> predecessors) {
|
||||||
|
this.pred.addAll(predecessors);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SourceBlock) {
|
||||||
|
return this.id.equals(((SourceBlock) obj).id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final String linesString = this.instructions.stream()
|
||||||
|
.map(SourceInst::toString)
|
||||||
|
.map(line -> line + "\n")
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
if (this.label.isBlank()) {
|
||||||
|
return linesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.label + ":\n"
|
||||||
|
+ linesString;
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +1,107 @@
|
|||||||
package codegen.sourcegraph;
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SourceGraph {
|
public class SourceGraph {
|
||||||
|
|
||||||
private final SourceGraphHead head;
|
private final SourceGraphHead head;
|
||||||
private final List<SourceGraphBlock> blocks;
|
private final List<SourceBlock> blocks;
|
||||||
private final SourceGraphTail tail;
|
private final SourceGraphTail tail;
|
||||||
|
|
||||||
|
// If a new block has this label, the value in this map is a predecessor
|
||||||
|
private final Map<String, SourceBlock> predecessorMap;
|
||||||
|
|
||||||
public SourceGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
public SourceGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) {
|
||||||
this.head = new SourceGraphHead(bytecodeVersion, source, clazz, stackSize, localCount);
|
this.head = new SourceGraphHead(bytecodeVersion, source, clazz, stackSize, localCount);
|
||||||
this.blocks = new ArrayList<>();
|
this.blocks = new ArrayList<>();
|
||||||
this.tail = new SourceGraphTail();
|
this.tail = new SourceGraphTail();
|
||||||
|
this.predecessorMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Label marks beginning of block
|
||||||
public void addLabel(String label) {
|
public void addLabel(String label) {
|
||||||
if (this.blocks.get(this.blocks.size() - 1).isEmpty()) {
|
final SourceBlock newBlock = new SourceBlock(label);
|
||||||
|
|
||||||
|
|
||||||
|
// Resolve missing successors/predecessors from jumps
|
||||||
|
if (this.predecessorMap.containsKey(label)) {
|
||||||
|
this.predecessorMap.get(label).addSuccessor(newBlock);
|
||||||
|
newBlock.addPredecessor(this.predecessorMap.get(label));
|
||||||
|
|
||||||
|
// this.predecessorMap.remove(label); // Problematic if multiple gotos to same label
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getCurrentBlock().isEmpty()) {
|
||||||
// Replace empty blocks, we don't need them
|
// Replace empty blocks, we don't need them
|
||||||
|
|
||||||
this.blocks.set(this.blocks.size() - 1, new SourceGraphBlock(label));
|
if (this.blocks.size() >= 2) {
|
||||||
|
// This empty blocks successors become the previous blocks successors after replacment
|
||||||
|
|
||||||
|
this.blocks.get(this.blocks.size() - 2).addSuccessors(this.getCurrentBlock().getSuccessors());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous blocks predecessors are also the new blocks predecessors
|
||||||
|
newBlock.addPredecessors(this.getCurrentBlock().getPredecessors());
|
||||||
|
|
||||||
|
this.blocks.set(this.blocks.size() - 1, newBlock); // Replace
|
||||||
} else {
|
} else {
|
||||||
this.blocks.add(new SourceGraphBlock(label));
|
// Append block if last one isn't empty
|
||||||
|
|
||||||
|
newBlock.addPredecessor(this.getCurrentBlock()); // Obvious predecessor of new block
|
||||||
|
this.getCurrentBlock().addSuccessor(newBlock); // Obvious successor of current block
|
||||||
|
|
||||||
|
this.blocks.add(newBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Jump means end of block
|
||||||
|
public void addJump(String jumpInstruction, String label) {
|
||||||
|
this.addInst(jumpInstruction, label);
|
||||||
|
|
||||||
|
final SourceBlock newBlock = new SourceBlock();
|
||||||
|
newBlock.addPredecessor(this.getCurrentBlock()); // Obvious predecessor of new block
|
||||||
|
|
||||||
|
if (!"goto".equals(jumpInstruction)) {
|
||||||
|
// Goto always jumps
|
||||||
|
|
||||||
|
this.getCurrentBlock().addSuccessor(newBlock); // Obvious successor of current block
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jumped successor
|
||||||
|
final SourceBlock labelBlock = this.getBlockByLabel(label);
|
||||||
|
if (labelBlock != null) {
|
||||||
|
// Successor exists
|
||||||
|
|
||||||
|
this.getCurrentBlock().addSuccessor(labelBlock);
|
||||||
|
labelBlock.addPredecessor(this.getCurrentBlock());
|
||||||
|
} else {
|
||||||
|
// Successor doesn't exist, so wait until it does
|
||||||
|
|
||||||
|
this.predecessorMap.put(label, this.getCurrentBlock()); // Current node is predecessor of label-block
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blocks.add(newBlock);
|
||||||
|
}
|
||||||
|
|
||||||
public void addInst(String instruction, String... args) {
|
public void addInst(String instruction, String... args) {
|
||||||
if (this.blocks.isEmpty()) {
|
if (this.blocks.isEmpty()) {
|
||||||
this.blocks.add(new SourceGraphBlock());
|
this.blocks.add(new SourceBlock()); // First block doesn't exist
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blocks.get(this.blocks.size() - 1).addLine(instruction, args);
|
this.getCurrentBlock().addLine(instruction, args); // Add to last block
|
||||||
}
|
|
||||||
|
|
||||||
public void addJump(String instruction, String label) {
|
|
||||||
this.addInst(instruction, label);
|
|
||||||
this.blocks.add(new SourceGraphBlock());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String print() {
|
public String print() {
|
||||||
final String blocksString = this.blocks.stream()
|
final String blocksString = this.blocks.stream()
|
||||||
.map(SourceGraphBlock::toString)
|
.map(SourceBlock::toString)
|
||||||
.map(string -> string + "-".repeat(50) + "\n")
|
.map(string -> string + "-".repeat(50) + "\n")
|
||||||
.collect(Collectors.joining());
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
@ -50,14 +110,73 @@ public class SourceGraph {
|
|||||||
+ this.tail;
|
+ this.tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String printToImage() {
|
||||||
|
final StringBuilder dot = new StringBuilder();
|
||||||
|
|
||||||
|
dot.append("digraph dfd {\n")
|
||||||
|
.append("node[shape=Mrecord]\n");
|
||||||
|
|
||||||
|
for (SourceBlock block : this.blocks) {
|
||||||
|
dot.append(block.getId())
|
||||||
|
.append(" [label=\"{<f0> ")
|
||||||
|
.append(block.getLabel())
|
||||||
|
.append("|<f1> ")
|
||||||
|
.append(block.printInst())
|
||||||
|
.append("}\"];\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dot.append("START[label=\"START\"];\n")
|
||||||
|
.append("END[label=\"END\"];\n");
|
||||||
|
|
||||||
|
dot.append("START -> ").append(this.blocks.get(0).getId()).append(";\n");
|
||||||
|
dot.append(this.getCurrentBlock().getId()).append(" -> END;\n");
|
||||||
|
|
||||||
|
for (SourceBlock block : this.blocks) {
|
||||||
|
for (SourceBlock succ : block.getSuccessors()) {
|
||||||
|
dot.append(block.getId()).append(" -> ").append(succ.getId()).append(";\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dot.append("}");
|
||||||
|
|
||||||
|
final String dotOut = dot.toString();
|
||||||
|
|
||||||
|
final Path dotFile = Paths.get(System.getProperty("user.dir") + "/DotOut.dot");
|
||||||
|
try {
|
||||||
|
Files.writeString(dotFile, dotOut);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProcessBuilder dotCompile = new ProcessBuilder("dot", "-Tsvg", "-O", "DotOut.dot");
|
||||||
|
try {
|
||||||
|
dotCompile.start();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Finished.";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final String blocksString = this.blocks.stream()
|
final String blocksString = this.blocks.stream()
|
||||||
.map(SourceGraphBlock::toString)
|
.map(SourceBlock::toString)
|
||||||
.collect(Collectors.joining());
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
return this.head
|
return this.head
|
||||||
+ blocksString
|
+ blocksString
|
||||||
+ this.tail;
|
+ this.tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SourceBlock getBlockByLabel(String label) {
|
||||||
|
return this.blocks.stream()
|
||||||
|
.filter(block -> block.getLabel().equals(label))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SourceBlock getCurrentBlock() {
|
||||||
|
return this.blocks.get(this.blocks.size() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package codegen.sourcegraph;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class SourceGraphBlock {
|
|
||||||
|
|
||||||
private final String label;
|
|
||||||
private final List<SourceGraphInst> lines;
|
|
||||||
|
|
||||||
public SourceGraphBlock(String label) {
|
|
||||||
this.label = label;
|
|
||||||
this.lines = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceGraphBlock() {
|
|
||||||
this.label = "";
|
|
||||||
this.lines = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.label.isEmpty() && this.lines.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
final String linesString = this.lines.stream()
|
|
||||||
.map(SourceGraphInst::toString)
|
|
||||||
.map(line -> line + "\n")
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
|
|
||||||
if (this.label.isBlank()) {
|
|
||||||
return linesString;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.label + ":\n"
|
|
||||||
+ linesString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addLine(String instruction, String... args) {
|
|
||||||
this.lines.add(new SourceGraphInst(instruction, args));
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import codegen.CodeGenerationException;
|
|||||||
import codegen.analysis.StackSizeAnalyzer;
|
import codegen.analysis.StackSizeAnalyzer;
|
||||||
import parser.ast.AST;
|
import parser.ast.AST;
|
||||||
import parser.ast.ASTNode;
|
import parser.ast.ASTNode;
|
||||||
|
import util.Logger;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -101,6 +102,7 @@ public final class SourceGraphGenerator {
|
|||||||
System.out.println(" - Generating Source Graph...");
|
System.out.println(" - Generating Source Graph...");
|
||||||
|
|
||||||
this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11));
|
this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11));
|
||||||
|
Logger.call(this.graph::printToImage);
|
||||||
|
|
||||||
log("\n\nSourceGraph print:\n" + "-".repeat(100) + "\n" + this.graph.print() + "-".repeat(100));
|
log("\n\nSourceGraph print:\n" + "-".repeat(100) + "\n" + this.graph.print() + "-".repeat(100));
|
||||||
System.out.println("Graph-generation successful.");
|
System.out.println("Graph-generation successful.");
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package codegen.sourcegraph;
|
package codegen.sourcegraph;
|
||||||
|
|
||||||
public class SourceGraphInst {
|
public class SourceInst {
|
||||||
|
|
||||||
private final String instruction;
|
private final String instruction;
|
||||||
private final String[] args;
|
private final String[] args;
|
||||||
|
|
||||||
public SourceGraphInst(String instruction, String... args) {
|
public SourceInst(String instruction, String... args) {
|
||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
@ -16,4 +16,12 @@ public class SourceGraphInst {
|
|||||||
|
|
||||||
return "\t\t" + this.instruction + " " + argsString;
|
return "\t\t" + this.instruction + " " + argsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getInstruction() {
|
||||||
|
return this.instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getArgs() {
|
||||||
|
return this.args;
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user