determine stack size simply by trying

This commit is contained in:
ChUrl
2021-01-23 20:43:19 +01:00
parent 60452b3713
commit fab8881947
3 changed files with 150 additions and 16 deletions

View File

@ -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);

View File

@ -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<ASTNode> 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;
}
}

View File

@ -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<String> mod;
private static final Set<String> 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
}
}
}