determine stack size simply by trying
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
package codegen;
|
package codegen;
|
||||||
|
|
||||||
|
import codegen.analysis.StackSizeAnalyzer;
|
||||||
import parser.ast.AST;
|
import parser.ast.AST;
|
||||||
import parser.ast.ASTNode;
|
import parser.ast.ASTNode;
|
||||||
|
|
||||||
@ -93,7 +94,6 @@ public final class CodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateHeader(String source) {
|
private void generateHeader(String source) {
|
||||||
System.out.println(" - Generating Jasmin Assembler...");
|
|
||||||
final String clazz = this.tree.getRoot().getChildren().get(1).getValue();
|
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
|
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) {
|
public StringBuilder generateCode(String source) {
|
||||||
|
System.out.println(" - Generating Jasmin assembler...");
|
||||||
if (!this.tree.getRoot().hasChildren()) {
|
if (!this.tree.getRoot().hasChildren()) {
|
||||||
throw new CodeGenerationException("Empty File can't be compiled");
|
throw new CodeGenerationException("Empty File can't be compiled");
|
||||||
}
|
}
|
||||||
@ -132,12 +133,13 @@ public final class CodeGenerator {
|
|||||||
return this.jasmin;
|
return this.jasmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Stack size
|
|
||||||
private void generateMain() {
|
private void generateMain() {
|
||||||
this.jasmin.append(".method public static main([Ljava/lang/String;)V\n")
|
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");
|
.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
|
// 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));
|
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");
|
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));
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
final String type = this.nodeTypeMap.get(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) {
|
private void intExpr(ASTNode node) {
|
||||||
String inst = "";
|
String inst = "";
|
||||||
|
|
||||||
if (node.getChildren().size() == 1) {
|
if (node.getChildren().size() == 1) { //! Stack + 0
|
||||||
// Unary operator
|
// Unary operator
|
||||||
|
|
||||||
this.generateNode(node.getChildren().get(0));
|
this.generateNode(node.getChildren().get(0));
|
||||||
|
|
||||||
inst = switch (node.getValue()) {
|
inst = switch (node.getValue()) {
|
||||||
case "ADD" -> "";
|
case "ADD" -> "";
|
||||||
case "SUB" -> "ldc -1\n\t\timul";
|
case "SUB" -> "ineg";
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + node.getValue());
|
default -> throw new IllegalStateException("Unexpected value: " + node.getValue());
|
||||||
};
|
};
|
||||||
} else if (node.getChildren().size() == 2) {
|
} else if (node.getChildren().size() == 2) { //! Stack - 1
|
||||||
// Binary operator
|
// Binary operator
|
||||||
|
|
||||||
this.generateNode(node.getChildren().get(0));
|
this.generateNode(node.getChildren().get(0));
|
||||||
@ -272,7 +274,7 @@ public final class CodeGenerator {
|
|||||||
private void boolExpr(ASTNode node) {
|
private void boolExpr(ASTNode node) {
|
||||||
String inst = "";
|
String inst = "";
|
||||||
|
|
||||||
if (node.getChildren().size() == 1) {
|
if (node.getChildren().size() == 1) { //! Stack + 1
|
||||||
// Unary operator
|
// Unary operator
|
||||||
|
|
||||||
if (!"NOT".equals(node.getValue())) {
|
if (!"NOT".equals(node.getValue())) {
|
||||||
@ -282,11 +284,9 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
this.generateNode(node.getChildren().get(0));
|
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
|
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
|
// Binary operator
|
||||||
|
|
||||||
final int currentLabel = this.labelCounter;
|
final int currentLabel = this.labelCounter;
|
||||||
@ -329,7 +329,7 @@ public final class CodeGenerator {
|
|||||||
|
|
||||||
// Leafs
|
// Leafs
|
||||||
|
|
||||||
private void intLiteral(ASTNode node) {
|
private void intLiteral(ASTNode node) { //! Stack + 1
|
||||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||||
|
|
||||||
// bipush only pushes 1 byte as int
|
// bipush only pushes 1 byte as int
|
||||||
@ -338,7 +338,7 @@ public final class CodeGenerator {
|
|||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stringLiteral(ASTNode node) {
|
private void stringLiteral(ASTNode node) { //! Stack + 1
|
||||||
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
log("literal(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||||
|
|
||||||
// bipush only pushes 1 byte as int
|
// bipush only pushes 1 byte as int
|
||||||
@ -347,7 +347,7 @@ public final class CodeGenerator {
|
|||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void boolLiteral(ASTNode node) {
|
private void boolLiteral(ASTNode node) { //! Stack + 1
|
||||||
log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc");
|
||||||
|
|
||||||
final String val = "true".equals(node.getValue()) ? "1" : "0";
|
final String val = "true".equals(node.getValue()) ? "1" : "0";
|
||||||
@ -357,7 +357,7 @@ public final class CodeGenerator {
|
|||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void identifier(ASTNode node) {
|
private void identifier(ASTNode node) { //! Stack + 1
|
||||||
final String type = this.nodeTypeMap.get(node);
|
final String type = this.nodeTypeMap.get(node);
|
||||||
final String inst = switch (type) {
|
final String inst = switch (type) {
|
||||||
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload ";
|
case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload ";
|
||||||
@ -373,7 +373,7 @@ public final class CodeGenerator {
|
|||||||
.append("\n");
|
.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
|
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);
|
final ASTNode expr = node.getChildren().get(1).getChildren().get(1);
|
||||||
|
46
src/main/java/codegen/analysis/StackModel.java
Normal file
46
src/main/java/codegen/analysis/StackModel.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
88
src/main/java/codegen/analysis/StackSizeAnalyzer.java
Normal file
88
src/main/java/codegen/analysis/StackSizeAnalyzer.java
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user