diff --git a/src/main/java/codegen/CodeGenerator.java b/src/main/java/codegen/CodeGenerator.java index 6e9ed4f..097ee44 100644 --- a/src/main/java/codegen/CodeGenerator.java +++ b/src/main/java/codegen/CodeGenerator.java @@ -1,5 +1,6 @@ package codegen; +import codegen.analysis.StackSizeAnalyzer; import parser.ast.AST; import parser.ast.ASTNode; @@ -93,7 +94,6 @@ public final class CodeGenerator { } private void generateHeader(String source) { - System.out.println(" - Generating Jasmin Assembler..."); final String clazz = this.tree.getRoot().getChildren().get(1).getValue(); this.jasmin.append(".bytecode 49.0\n") // 55.0 has stricter verification => stackmap frames missing @@ -118,6 +118,7 @@ public final class CodeGenerator { } public StringBuilder generateCode(String source) { + System.out.println(" - Generating Jasmin assembler..."); if (!this.tree.getRoot().hasChildren()) { throw new CodeGenerationException("Empty File can't be compiled"); } @@ -132,12 +133,13 @@ public final class CodeGenerator { return this.jasmin; } - // TODO: Stack size private void generateMain() { this.jasmin.append(".method public static main([Ljava/lang/String;)V\n") - .append("\t.limit stack 10\n") + .append("\t.limit stack ").append(StackSizeAnalyzer.model(this.tree)).append("\n") .append("\t.limit locals ").append(this.varMap.size() + 1).append("\n"); + log("\nGenerating main method code"); + // Needs to be skipped to not trigger generation for IDENTIFIER: args or IDENTIFIER: ClassName this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11)); @@ -207,7 +209,7 @@ public final class CodeGenerator { this.jasmin.append("LOOPend").append(currentLabel).append(":\n"); } - private void assign(ASTNode node) { + private void assign(ASTNode node) { //! Stack - 1 this.generateNode(node.getChildren().get(0)); final String type = this.nodeTypeMap.get(node.getChildren().get(0)); @@ -236,17 +238,17 @@ public final class CodeGenerator { private void intExpr(ASTNode node) { String inst = ""; - if (node.getChildren().size() == 1) { + if (node.getChildren().size() == 1) { //! Stack + 0 // Unary operator this.generateNode(node.getChildren().get(0)); inst = switch (node.getValue()) { case "ADD" -> ""; - case "SUB" -> "ldc -1\n\t\timul"; + case "SUB" -> "ineg"; default -> throw new IllegalStateException("Unexpected value: " + node.getValue()); }; - } else if (node.getChildren().size() == 2) { + } else if (node.getChildren().size() == 2) { //! Stack - 1 // Binary operator this.generateNode(node.getChildren().get(0)); @@ -272,7 +274,7 @@ public final class CodeGenerator { private void boolExpr(ASTNode node) { String inst = ""; - if (node.getChildren().size() == 1) { + if (node.getChildren().size() == 1) { //! Stack + 1 // Unary operator if (!"NOT".equals(node.getValue())) { @@ -282,11 +284,9 @@ public final class CodeGenerator { this.generateNode(node.getChildren().get(0)); - // 1 -> 0, 0 -> 1? - // inst = "ldc 1\n\t\tisub\n\t\tdup\n\t\timul"; // Subtract 1 and square for now inst = "ldc 1\n\t\tixor"; // 0 ^1 = 1, 1 ^1 = 0 - } else if (node.getChildren().size() == 2) { + } else if (node.getChildren().size() == 2) { //! Stack - 1 // Binary operator final int currentLabel = this.labelCounter; @@ -329,7 +329,7 @@ public final class CodeGenerator { // Leafs - private void intLiteral(ASTNode node) { + private void intLiteral(ASTNode node) { //! Stack + 1 log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc"); // bipush only pushes 1 byte as int @@ -338,7 +338,7 @@ public final class CodeGenerator { .append("\n"); } - private void stringLiteral(ASTNode node) { + private void stringLiteral(ASTNode node) { //! Stack + 1 log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc"); // bipush only pushes 1 byte as int @@ -347,7 +347,7 @@ public final class CodeGenerator { .append("\n"); } - private void boolLiteral(ASTNode node) { + private void boolLiteral(ASTNode node) { //! Stack + 1 log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc"); final String val = "true".equals(node.getValue()) ? "1" : "0"; @@ -357,7 +357,7 @@ public final class CodeGenerator { .append("\n"); } - private void identifier(ASTNode node) { + private void identifier(ASTNode node) { //! Stack + 1 final String type = this.nodeTypeMap.get(node); final String inst = switch (type) { case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload "; @@ -373,7 +373,7 @@ public final class CodeGenerator { .append("\n"); } - private void println(ASTNode node) { + private void println(ASTNode node) { //! Stack + 1 this.jasmin.append("\t\tgetstatic java/lang/System/out Ljava/io/PrintStream;\n"); // Push System.out to stack final ASTNode expr = node.getChildren().get(1).getChildren().get(1); diff --git a/src/main/java/codegen/analysis/StackModel.java b/src/main/java/codegen/analysis/StackModel.java new file mode 100644 index 0000000..da21b0c --- /dev/null +++ b/src/main/java/codegen/analysis/StackModel.java @@ -0,0 +1,46 @@ +package codegen.analysis; + +import parser.ast.ASTNode; +import util.Logger; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class StackModel { + + private final Deque stack; + private int max; + + public StackModel() { + this.stack = new ArrayDeque<>(); + } + + public void push(ASTNode root) { + Logger.log("PUSH " + root.getName() + ": " + root.getValue()); + this.stack.push(root); + this.updateMax(); + } + + public ASTNode pop() { + if (this.stack.isEmpty()) { + throw new IllegalStateException("Can't pop empty stack"); + } + + Logger.log("POP " + this.stack.peek().getName() + ": " + this.stack.peek().getValue()); + return this.stack.pop(); + } + + public ASTNode peek() { + return this.stack.peek(); + } + + private void updateMax() { + if (this.stack.size() > this.max) { + this.max = this.stack.size(); + } + } + + public int getMax() { + return this.max; + } +} diff --git a/src/main/java/codegen/analysis/StackSizeAnalyzer.java b/src/main/java/codegen/analysis/StackSizeAnalyzer.java new file mode 100644 index 0000000..3a439ff --- /dev/null +++ b/src/main/java/codegen/analysis/StackSizeAnalyzer.java @@ -0,0 +1,88 @@ +package codegen.analysis; + +import parser.ast.AST; +import parser.ast.ASTNode; + +import java.util.Set; + +import static util.Logger.log; + +public final class StackSizeAnalyzer { + + private static final Set mod; + private static final Set bin; + + static { + mod = Set.of("assignment", "expr", "INTEGER_LIT", "BOOLEAN_LIT", "STRING_LIT", "IDENTIFIER", "print"); + bin = Set.of("AND", "OR", "ADD", "SUB", "MUL", "DIV", "MOD", "LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL", "EQUAL", "NOT_EQUAL"); + } + + private StackSizeAnalyzer() {} + + public static int model(AST tree) { + log("\nDetermining required stack depth:"); + + final StackModel stack = new StackModel(); + model(tree.getRoot().getChildren().get(3).getChildren().get(11), stack); + + return stack.getMax(); + } + + private static void model(ASTNode root, StackModel stack) { + if (mod.contains(root.getName())) { + switch (root.getName()) { + case "assignment" -> assignment(root, stack); + case "INTEGER_LIT", "BOOLEAN_LIT", "STRING_LIT", "IDENTIFIER" -> literal(root, stack); + case "expr" -> expr(root, stack); + case "print" -> println(root, stack); + default -> throw new IllegalStateException("Unexpected value: " + root.getName()); + } + } else { + for (ASTNode child : root.getChildren()) { + model(child, stack); + } + } + } + + private static void literal(ASTNode root, StackModel stack) { + log("literal():"); + stack.push(root); + } + + private static void assignment(ASTNode root, StackModel stack) { + model(root.getChildren().get(0), stack); + + log("assignment():"); + stack.pop(); + } + + private static void println(ASTNode root, StackModel stack) { + stack.push(root); // Getstatic + + model(root.getChildren().get(1).getChildren().get(1), stack); + + log("println():"); + stack.pop(); // Objectref + stack.pop(); // Argument + } + + private static void expr(ASTNode root, StackModel stack) { + if (root.getChildren().size() == 2 && bin.contains(root.getValue())) { + model(root.getChildren().get(0), stack); + model(root.getChildren().get(1), stack); + + log("expr():"); + stack.pop(); // Argument + stack.pop(); // Argument + stack.push(root); // Result + } else if (root.getChildren().size() == 1 && "NOT".equals(root.getValue())) { + model(root.getChildren().get(0), stack); + + log("expr():"); + stack.push(root); // 1 for xor + stack.pop(); // xor + stack.pop(); // xor + stack.push(root); // result + } + } +}