diff --git a/src/main/java/codegen/flowgraph/FlowBasicBlock.java b/src/main/java/codegen/flowgraph/FlowBasicBlock.java new file mode 100644 index 0000000..2357a5f --- /dev/null +++ b/src/main/java/codegen/flowgraph/FlowBasicBlock.java @@ -0,0 +1,171 @@ +package codegen.flowgraph; + +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 FlowBasicBlock { + + private final String id; + private final String label; + private final List instructions; + private final Set predecessors; + private final Set successors; + + private int instNr; + + public FlowBasicBlock(String label) { + this.label = label; + this.id = String.valueOf(System.nanoTime()); + this.instructions = new ArrayList<>(); + this.predecessors = new HashSet<>(); + this.successors = new HashSet<>(); + } + + public FlowBasicBlock() { + this(""); + } + + public boolean isEmpty() { + return this.label.isBlank() && this.instructions.isEmpty(); + } + + public String getId() { + return this.id; + } + + public String getLabel() { + return this.label; + } + + public void addInstruction(String instruction, String... args) { + this.instNr++; + this.instructions.add(new FlowInstruction(String.valueOf(Long.parseLong(this.id) + this.instNr), this.id, + instruction, args)); + } + + public List getInstructions() { + return this.instructions; + } + + public void addSuccessor(FlowBasicBlock block) { + this.successors.add(block); + } + + public void addSuccessors(Set successors) { + this.successors.addAll(successors); + } + + public void addPredecessor(FlowBasicBlock block) { + this.predecessors.add(block); + } + + public void addPredecessors(Set predecessors) { + this.predecessors.addAll(predecessors); + } + + public Set getSuccessorSet() { + return this.successors; + } + + public Set getPredecessors(FlowInstruction inst) { + final int index = this.instructions.indexOf(inst); + + if (index == -1) { + return null; + } + + if (index > 0 && index <= this.instructions.size() - 1) { + // Instruction is in the middle or end + + return Set.of(this.instructions.get(index - 1)); + } + + // Instruction is at the beginning + return this.predecessors.stream() + .map(FlowBasicBlock::getLastInst) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableSet()); + } + + public Set getSuccessors(FlowInstruction inst) { + final int index = this.instructions.indexOf(inst); + + if (index == -1) { + return null; + } + + if (index >= 0 && index < this.instructions.size() - 1) { + // Instruction is in the beginning or middle + + return Set.of(this.instructions.get(index + 1)); + } + + // Instruction is at the end + return this.successors.stream() + .map(FlowBasicBlock::getFirstInst) + .filter(Objects::nonNull) + .collect(Collectors.toUnmodifiableSet()); + } + + public Set getPredecessorSet() { + return this.predecessors; + } + + public FlowInstruction getFirstInst() { + if (!this.instructions.isEmpty()) { + return this.instructions.get(0); + } + + return null; + } + + public FlowInstruction getLastInst() { + if (!this.instructions.isEmpty()) { + return this.instructions.get(this.instructions.size() - 1); + } + + return null; + } + + // Print + Overrides + + public String printInst() { + return this.instructions.stream() + .map(inst -> inst.toString().trim() + "\\n") + .map(inst -> inst.replace("\"", "\\\"")) + .collect(Collectors.joining()); + } + + @Override + public int hashCode() { + return Objects.hash(this.id); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof FlowBasicBlock) { + return this.id.equals(((FlowBasicBlock) obj).id); + } + + return false; + } + + @Override + public String toString() { + final String linesString = this.instructions.stream() + .map(FlowInstruction::toString) + .map(line -> line + "\n") + .collect(Collectors.joining()); + + if (this.label.isBlank()) { + return linesString; + } + + return this.label + ":\n" + + linesString; + } +} diff --git a/src/main/java/codegen/sourcegraph/SourceGraph.java b/src/main/java/codegen/flowgraph/FlowGraph.java similarity index 71% rename from src/main/java/codegen/sourcegraph/SourceGraph.java rename to src/main/java/codegen/flowgraph/FlowGraph.java index 221ce8a..ace10b1 100644 --- a/src/main/java/codegen/sourcegraph/SourceGraph.java +++ b/src/main/java/codegen/flowgraph/FlowGraph.java @@ -1,4 +1,4 @@ -package codegen.sourcegraph; +package codegen.flowgraph; import java.io.IOException; import java.nio.file.Files; @@ -10,25 +10,25 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -public class SourceGraph { +public class FlowGraph { - private final SourceGraphHead head; - private final List blocks; - private final SourceGraphTail tail; + private final FlowGraphHead head; + private final List blocks; + private final FlowGraphTail tail; // If a new block has this label, the value in this map is a predecessor - private final Map predecessorMap; + private final Map predecessorMap; - public SourceGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) { - this.head = new SourceGraphHead(bytecodeVersion, source, clazz, stackSize, localCount); + public FlowGraph(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) { + this.head = new FlowGraphHead(bytecodeVersion, source, clazz, stackSize, localCount); this.blocks = new ArrayList<>(); - this.tail = new SourceGraphTail(); + this.tail = new FlowGraphTail(); this.predecessorMap = new HashMap<>(); } // Label marks beginning of block public void addLabel(String label) { - final SourceBlock newBlock = new SourceBlock(label); + final FlowBasicBlock newBlock = new FlowBasicBlock(label); // Resolve missing successors/predecessors from jumps @@ -39,34 +39,37 @@ public class SourceGraph { // this.predecessorMap.remove(label); // Problematic if multiple gotos to same label } + /* + TODO: Hier ist ein Bug, welcher im Datenflussgraph zu gotos mit 2 successors führt if (this.getCurrentBlock().isEmpty()) { // Replace empty blocks, we don't need them 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()); + this.blocks.get(this.blocks.size() - 2).addSuccessors(this.getCurrentBlock().getSuccessorSet()); } // Previous blocks predecessors are also the new blocks predecessors - newBlock.addPredecessors(this.getCurrentBlock().getPredecessors()); + newBlock.addPredecessors(this.getCurrentBlock().getPredecessorSet()); this.blocks.set(this.blocks.size() - 1, newBlock); // Replace } else { + */ // 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(); + final FlowBasicBlock newBlock = new FlowBasicBlock(); newBlock.addPredecessor(this.getCurrentBlock()); // Obvious predecessor of new block if (!"goto".equals(jumpInstruction)) { @@ -76,7 +79,7 @@ public class SourceGraph { } // Jumped successor - final SourceBlock labelBlock = this.getBlockByLabel(label); + final FlowBasicBlock labelBlock = this.getBlockByLabel(label); if (labelBlock != null) { // Successor exists @@ -93,15 +96,15 @@ public class SourceGraph { public void addInst(String instruction, String... args) { if (this.blocks.isEmpty()) { - this.blocks.add(new SourceBlock()); // First block doesn't exist + this.blocks.add(new FlowBasicBlock()); // First block doesn't exist } - this.getCurrentBlock().addLine(instruction, args); // Add to last block + this.getCurrentBlock().addInstruction(instruction, args); // Add to last block } public String print() { final String blocksString = this.blocks.stream() - .map(SourceBlock::toString) + .map(FlowBasicBlock::toString) .map(string -> string + "-".repeat(50) + "\n") .collect(Collectors.joining()); @@ -116,9 +119,11 @@ public class SourceGraph { dot.append("digraph dfd {\n") .append("node[shape=Mrecord]\n"); - for (SourceBlock block : this.blocks) { + for (FlowBasicBlock block : this.blocks) { dot.append(block.getId()) .append(" [label=\"{ ") + .append(this.blocks.indexOf(block)) + .append(": ") .append(block.getLabel()) .append("| ") .append(block.printInst()) @@ -131,8 +136,8 @@ public class SourceGraph { 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()) { + for (FlowBasicBlock block : this.blocks) { + for (FlowBasicBlock succ : block.getSuccessorSet()) { dot.append(block.getId()).append(" -> ").append(succ.getId()).append(";\n"); } } @@ -141,14 +146,14 @@ public class SourceGraph { final String dotOut = dot.toString(); - final Path dotFile = Paths.get(System.getProperty("user.dir") + "/DotOut.dot"); + final Path dotFile = Paths.get(System.getProperty("user.dir") + "/FlowGraph.dot"); try { Files.writeString(dotFile, dotOut); } catch (IOException e) { e.printStackTrace(); } - final ProcessBuilder dotCompile = new ProcessBuilder("dot", "-Tsvg", "-O", "DotOut.dot"); + final ProcessBuilder dotCompile = new ProcessBuilder("dot", "-Tsvg", "-oFlowGraph.svg", "FlowGraph.dot"); try { dotCompile.start(); } catch (IOException e) { @@ -161,7 +166,7 @@ public class SourceGraph { @Override public String toString() { final String blocksString = this.blocks.stream() - .map(SourceBlock::toString) + .map(FlowBasicBlock::toString) .collect(Collectors.joining()); return this.head @@ -169,14 +174,25 @@ public class SourceGraph { + this.tail; } - private SourceBlock getBlockByLabel(String label) { + private FlowBasicBlock getBlockByLabel(String label) { return this.blocks.stream() .filter(block -> block.getLabel().equals(label)) .findFirst() .orElse(null); } - private SourceBlock getCurrentBlock() { + private FlowBasicBlock getCurrentBlock() { return this.blocks.get(this.blocks.size() - 1); } + + public List getBlocks() { + return this.blocks; + } + + public FlowBasicBlock getBlockById(String id) { + return this.blocks.stream() + .filter(block -> block.getId().equals(id)) + .findFirst() + .orElse(null); + } } diff --git a/src/main/java/codegen/sourcegraph/SourceGraphGenerator.java b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java similarity index 93% rename from src/main/java/codegen/sourcegraph/SourceGraphGenerator.java rename to src/main/java/codegen/flowgraph/FlowGraphGenerator.java index ec0deca..c4e3445 100644 --- a/src/main/java/codegen/sourcegraph/SourceGraphGenerator.java +++ b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java @@ -1,4 +1,4 @@ -package codegen.sourcegraph; +package codegen.flowgraph; import codegen.CodeGenerationException; import codegen.analysis.StackSizeAnalyzer; @@ -17,14 +17,14 @@ import java.util.Map; import static java.util.Map.entry; import static util.Logger.log; -public final class SourceGraphGenerator { +public final class FlowGraphGenerator { private static final Map methodMap; static { Map map; try { - final Class gen = SourceGraphGenerator.class; + final Class gen = FlowGraphGenerator.class; map = Map.ofEntries( entry("cond", gen.getDeclaredMethod("condNode", ASTNode.class)), entry("loop", gen.getDeclaredMethod("loopNode", ASTNode.class)), @@ -47,34 +47,33 @@ public final class SourceGraphGenerator { private final AST tree; private final Map varMap; private final Map nodeTypeMap; - private final SourceGraph graph; - + private final FlowGraph graph; private int labelCounter; - private SourceGraphGenerator(Map varMap, AST tree, Map nodeTypeMap, SourceGraph graph) { + private FlowGraphGenerator(Map varMap, AST tree, Map nodeTypeMap, FlowGraph graph) { this.varMap = varMap; this.tree = tree; this.nodeTypeMap = nodeTypeMap; this.graph = graph; } - public static SourceGraphGenerator fromAST(AST tree, Map nodeTypeMap, String source) { + public static FlowGraphGenerator fromAST(AST tree, Map nodeTypeMap, String source) { if (!tree.getRoot().hasChildren()) { throw new CodeGenerationException("Empty File can't be compiled"); } - final Map varMap = varMapFromAST(tree); + final Map varMap = initVarMap(tree); final String bytecodeVersion = "49.0"; final String clazz = tree.getRoot().getChildren().get(1).getValue(); final int stackSize = StackSizeAnalyzer.runStackModel(tree); final int localCount = varMap.size() + 1; - final SourceGraph graph = new SourceGraph(bytecodeVersion, source, clazz, stackSize, localCount); + final FlowGraph graph = new FlowGraph(bytecodeVersion, source, clazz, stackSize, localCount); - return new SourceGraphGenerator(varMap, tree, nodeTypeMap, graph); + return new FlowGraphGenerator(varMap, tree, nodeTypeMap, graph); } - private static Map varMapFromAST(AST tree) { + private static Map initVarMap(AST tree) { final Map varMap = new HashMap<>(); // Assign variables to map @@ -98,7 +97,11 @@ public final class SourceGraphGenerator { return Collections.unmodifiableMap(varMap); } - public SourceGraph generateGraph() { + public Map getVarMap() { + return this.varMap; + } + + public FlowGraph generateGraph() { System.out.println(" - Generating Source Graph..."); this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11)); diff --git a/src/main/java/codegen/sourcegraph/SourceGraphHead.java b/src/main/java/codegen/flowgraph/FlowGraphHead.java similarity index 86% rename from src/main/java/codegen/sourcegraph/SourceGraphHead.java rename to src/main/java/codegen/flowgraph/FlowGraphHead.java index b0da5fd..6fa2594 100644 --- a/src/main/java/codegen/sourcegraph/SourceGraphHead.java +++ b/src/main/java/codegen/flowgraph/FlowGraphHead.java @@ -1,6 +1,6 @@ -package codegen.sourcegraph; +package codegen.flowgraph; -public class SourceGraphHead { +public class FlowGraphHead { private final String bytecodeVersion; private final String source; @@ -8,7 +8,7 @@ public class SourceGraphHead { private final int stackSize; private final int localCount; - public SourceGraphHead(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) { + public FlowGraphHead(String bytecodeVersion, String source, String clazz, int stackSize, int localCount) { this.bytecodeVersion = bytecodeVersion; this.source = source; this.clazz = clazz; diff --git a/src/main/java/codegen/sourcegraph/SourceGraphTail.java b/src/main/java/codegen/flowgraph/FlowGraphTail.java similarity index 66% rename from src/main/java/codegen/sourcegraph/SourceGraphTail.java rename to src/main/java/codegen/flowgraph/FlowGraphTail.java index 57b5d29..6083e4c 100644 --- a/src/main/java/codegen/sourcegraph/SourceGraphTail.java +++ b/src/main/java/codegen/flowgraph/FlowGraphTail.java @@ -1,6 +1,6 @@ -package codegen.sourcegraph; +package codegen.flowgraph; -public class SourceGraphTail { +public class FlowGraphTail { @Override public String toString() { diff --git a/src/main/java/codegen/sourcegraph/SourceInst.java b/src/main/java/codegen/flowgraph/FlowInstruction.java similarity index 54% rename from src/main/java/codegen/sourcegraph/SourceInst.java rename to src/main/java/codegen/flowgraph/FlowInstruction.java index 4c1a22a..2394c5e 100644 --- a/src/main/java/codegen/sourcegraph/SourceInst.java +++ b/src/main/java/codegen/flowgraph/FlowInstruction.java @@ -1,11 +1,15 @@ -package codegen.sourcegraph; +package codegen.flowgraph; -public class SourceInst { +public class FlowInstruction { + private final String id; + private final String blockId; private final String instruction; private final String[] args; - public SourceInst(String instruction, String... args) { + public FlowInstruction(String id, String blockId, String instruction, String... args) { + this.id = id; + this.blockId = blockId; this.instruction = instruction; this.args = args; } @@ -24,4 +28,12 @@ public class SourceInst { public String[] getArgs() { return this.args; } + + public String getBlockId() { + return this.blockId; + } + + public String getId() { + return this.id; + } } diff --git a/src/test/java/codegen/SourceGraphGeneratorTest.java b/src/test/java/codegen/FlowGraphGeneratorTest.java similarity index 90% rename from src/test/java/codegen/SourceGraphGeneratorTest.java rename to src/test/java/codegen/FlowGraphGeneratorTest.java index 52c5d9b..9d0fa9e 100644 --- a/src/test/java/codegen/SourceGraphGeneratorTest.java +++ b/src/test/java/codegen/FlowGraphGeneratorTest.java @@ -1,7 +1,7 @@ package codegen; -import codegen.sourcegraph.SourceGraph; -import codegen.sourcegraph.SourceGraphGenerator; +import codegen.flowgraph.FlowGraph; +import codegen.flowgraph.FlowGraphGenerator; import lexer.StupsLexer; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.Lexer; @@ -29,14 +29,14 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -class SourceGraphGeneratorTest { +class FlowGraphGeneratorTest { private static StupsParser parser; private static Grammar stupsGrammar; @BeforeAll static void init() throws IOException, URISyntaxException { - final Path path = Paths.get(SourceGraphGeneratorTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI()); + final Path path = Paths.get(FlowGraphGeneratorTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI()); final Grammar grammar = Grammar.fromFile(path); parser = StupsParser.fromGrammar(grammar); stupsGrammar = grammar; @@ -44,7 +44,7 @@ class SourceGraphGeneratorTest { private static String readProgram(String prog) { try { - final Path progPath = Paths.get(SourceGraphGeneratorTest.class.getClassLoader().getResource("examplePrograms/" + prog).toURI()); + final Path progPath = Paths.get(FlowGraphGeneratorTest.class.getClassLoader().getResource("examplePrograms/" + prog).toURI()); return Files.readString(progPath); } catch (URISyntaxException | IOException e) { e.printStackTrace(); @@ -242,8 +242,8 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutpu"); - final SourceGraph srcProg = gen.generateGraph(); + final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutpu"); + final FlowGraph srcProg = gen.generateGraph(); compileJasmin(srcProg.toString()); assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result); @@ -257,8 +257,8 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); - final SourceGraph srcProg = gen.generateGraph(); + final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); + final FlowGraph srcProg = gen.generateGraph(); compileJasmin(srcProg.toString()); assertThat(Integer.parseInt(executeCompiledProgram())).isEqualTo(result); @@ -272,8 +272,8 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); - final SourceGraph srcProg = gen.generateGraph(); + final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); + final FlowGraph srcProg = gen.generateGraph(); compileJasmin(srcProg.toString()); assertThat(Boolean.parseBoolean(executeCompiledProgram())).isEqualTo(result); @@ -287,8 +287,8 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); - final SourceGraph srcProg = gen.generateGraph(); + final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); + final FlowGraph srcProg = gen.generateGraph(); compileJasmin(srcProg.toString()); assertThat(executeCompiledProgram()).isEqualTo(result); @@ -302,8 +302,8 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - final SourceGraphGenerator gen = SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); - final SourceGraph srcProg = gen.generateGraph(); + final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); + final FlowGraph srcProg = gen.generateGraph(); compileJasmin(srcProg.toString()); assertThat(executeCompiledProgram()).isEqualTo(result); @@ -316,7 +316,7 @@ class SourceGraphGeneratorTest { final AST tree = lexParseProgram(program); final Map nodeTable = TypeChecker.validate(tree); - assertThatThrownBy(() -> SourceGraphGenerator.fromAST(tree, nodeTable, "TestOutput")) + assertThatThrownBy(() -> FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput")) .isInstanceOf(CodeGenerationException.class); } }