mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-04-23 08:38:51 +00:00
Improve codegen readability
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
package com.dfsek.terra.addons.terrascript.codegen.asm;
|
||||||
|
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
|
||||||
|
public enum OpcodeAlias {
|
||||||
|
CMP_GREATER_THAN(Opcodes.IFGT),
|
||||||
|
CMP_GREATER_EQUALS(Opcodes.IFGE),
|
||||||
|
CMP_LESS_THAN(Opcodes.IFLT),
|
||||||
|
CMP_LESS_EQUALS(Opcodes.IFLE),
|
||||||
|
CMP_EQUALS(Opcodes.IFEQ),
|
||||||
|
CMP_NOT_EQUALS(Opcodes.IFNE),
|
||||||
|
BOOL_FALSE(Opcodes.IFEQ),
|
||||||
|
BOOL_TRUE(Opcodes.IFNE),
|
||||||
|
INTEGERS_EQUAL(Opcodes.IF_ICMPEQ),
|
||||||
|
INTEGERS_NOT_EQUAL(Opcodes.IF_ICMPNE),
|
||||||
|
;
|
||||||
|
|
||||||
|
public final int opcode;
|
||||||
|
|
||||||
|
OpcodeAlias(int opcode) { this.opcode = opcode; }
|
||||||
|
}
|
||||||
@@ -46,6 +46,16 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_EQUALS;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.BOOL_FALSE;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_GREATER_EQUALS;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_GREATER_THAN;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_LESS_EQUALS;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_LESS_THAN;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.CMP_NOT_EQUALS;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.BOOL_TRUE;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.INTEGERS_EQUAL;
|
||||||
|
import static com.dfsek.terra.addons.terrascript.codegen.asm.OpcodeAlias.INTEGERS_NOT_EQUAL;
|
||||||
import static com.dfsek.terra.addons.terrascript.util.ASMUtil.dynamicName;
|
import static com.dfsek.terra.addons.terrascript.util.ASMUtil.dynamicName;
|
||||||
|
|
||||||
public class TerraScriptClassGenerator {
|
public class TerraScriptClassGenerator {
|
||||||
@@ -162,7 +172,7 @@ public class TerraScriptClassGenerator {
|
|||||||
@Override
|
@Override
|
||||||
public Void visitBinaryTypedExpr(Binary expr) {
|
public Void visitBinaryTypedExpr(Binary expr) {
|
||||||
switch(expr.operator) {
|
switch(expr.operator) {
|
||||||
case EQUALS, NOT_EQUALS, BOOLEAN_AND, BOOLEAN_OR, GREATER, GREATER_EQUALS, LESS, LESS_EQUALS -> pushComparisonResult(expr);
|
case EQUALS, NOT_EQUALS, BOOLEAN_AND, BOOLEAN_OR, GREATER, GREATER_EQUALS, LESS, LESS_EQUALS -> pushComparisonBool(expr);
|
||||||
case ADD -> {
|
case ADD -> {
|
||||||
pushBinaryOperands(expr);
|
pushBinaryOperands(expr);
|
||||||
switch(expr.type) {
|
switch(expr.type) {
|
||||||
@@ -175,7 +185,6 @@ public class TerraScriptClassGenerator {
|
|||||||
case SUBTRACT -> binaryInsn(expr, Opcodes.DSUB);
|
case SUBTRACT -> binaryInsn(expr, Opcodes.DSUB);
|
||||||
case MULTIPLY -> binaryInsn(expr, Opcodes.DMUL);
|
case MULTIPLY -> binaryInsn(expr, Opcodes.DMUL);
|
||||||
case DIVIDE -> binaryInsn(expr, Opcodes.DDIV);
|
case DIVIDE -> binaryInsn(expr, Opcodes.DDIV);
|
||||||
// case MODULO ->
|
|
||||||
default -> throw new RuntimeException("Unhandled binary operator " + expr.operator);
|
default -> throw new RuntimeException("Unhandled binary operator " + expr.operator);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -190,7 +199,7 @@ public class TerraScriptClassGenerator {
|
|||||||
@Override
|
@Override
|
||||||
public Void visitLiteralTypedExpr(Literal expr) {
|
public Void visitLiteralTypedExpr(Literal expr) {
|
||||||
switch (expr.type) {
|
switch (expr.type) {
|
||||||
case BOOLEAN -> method.visitInsn((boolean) expr.value ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
|
case BOOLEAN -> { if ((boolean) expr.value) pushTrue(); else pushFalse(); }
|
||||||
case NUMBER, STRING -> method.visitLdcInsn(expr.value);
|
case NUMBER, STRING -> method.visitLdcInsn(expr.value);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -265,6 +274,11 @@ public class TerraScriptClassGenerator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes function as a private static method of the current class
|
||||||
|
* @param stmt
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Void visitFunctionDeclarationTypedStmt(FunctionDeclaration stmt) {
|
public Void visitFunctionDeclarationTypedStmt(FunctionDeclaration stmt) {
|
||||||
List<Type> parameterTypes = stmt.parameters.stream().map(Pair::getRight).toList();
|
List<Type> parameterTypes = stmt.parameters.stream().map(Pair::getRight).toList();
|
||||||
@@ -281,7 +295,7 @@ public class TerraScriptClassGenerator {
|
|||||||
int lvidx = 0;
|
int lvidx = 0;
|
||||||
for (Pair<String, Type> parameter : stmt.parameters) {
|
for (Pair<String, Type> parameter : stmt.parameters) {
|
||||||
funcGenerator.lvTable.put(parameter.getLeft(), lvidx);
|
funcGenerator.lvTable.put(parameter.getLeft(), lvidx);
|
||||||
lvidx += switch(parameter.getRight()) {
|
lvidx += switch(parameter.getRight()) { // Increment by how many slots data type takes
|
||||||
case NUMBER -> 2;
|
case NUMBER -> 2;
|
||||||
case STRING, BOOLEAN -> 1;
|
case STRING, BOOLEAN -> 1;
|
||||||
default -> throw new RuntimeException("Unable to register local variable index for parameter, unknown parameter type '" + parameter.getRight() + "'");
|
default -> throw new RuntimeException("Unable to register local variable index for parameter, unknown parameter type '" + parameter.getRight() + "'");
|
||||||
@@ -332,7 +346,7 @@ public class TerraScriptClassGenerator {
|
|||||||
conditionalStmt(elseIfClause.getLeft(), elseIfClause.getRight(), endIf);
|
conditionalStmt(elseIfClause.getLeft(), elseIfClause.getRight(), endIf);
|
||||||
}
|
}
|
||||||
stmt.elseBody.ifPresent(b -> b.accept(this));
|
stmt.elseBody.ifPresent(b -> b.accept(this));
|
||||||
method.visitLabel(endIf);
|
label(endIf);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,15 +357,15 @@ public class TerraScriptClassGenerator {
|
|||||||
Label loopEnd = new Label();
|
Label loopEnd = new Label();
|
||||||
|
|
||||||
stmt.initializer.accept(this);
|
stmt.initializer.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.GOTO, loopBody); // Skip over incrementer on first loop
|
jump(loopBody); // Skip over incrementer on first loop
|
||||||
|
|
||||||
method.visitLabel(loopStart);
|
label(loopStart);
|
||||||
stmt.incrementer.accept(this);
|
stmt.incrementer.accept(this);
|
||||||
method.visitLabel(loopBody);
|
label(loopBody);
|
||||||
loopStack.push(Pair.of(loopStart, loopEnd));
|
loopStack.push(Pair.of(loopStart, loopEnd));
|
||||||
conditionalStmt(stmt.condition, stmt.body, loopStart);
|
conditionalStmt(stmt.condition, stmt.body, loopStart);
|
||||||
loopStack.pop();
|
loopStack.pop();
|
||||||
method.visitLabel(loopEnd);
|
label(loopEnd);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,11 +374,11 @@ public class TerraScriptClassGenerator {
|
|||||||
Label loopStart = new Label();
|
Label loopStart = new Label();
|
||||||
Label loopEnd = new Label();
|
Label loopEnd = new Label();
|
||||||
|
|
||||||
method.visitLabel(loopStart);
|
label(loopStart);
|
||||||
loopStack.push(Pair.of(loopStart, loopEnd));
|
loopStack.push(Pair.of(loopStart, loopEnd));
|
||||||
conditionalStmt(stmt.condition, stmt.body, loopStart);
|
conditionalStmt(stmt.condition, stmt.body, loopStart);
|
||||||
loopStack.pop();
|
loopStack.pop();
|
||||||
method.visitLabel(loopEnd);
|
label(loopEnd);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,13 +389,13 @@ public class TerraScriptClassGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitBreakTypedStmt(Break stmt) {
|
public Void visitBreakTypedStmt(Break stmt) {
|
||||||
method.visitJumpInsn(Opcodes.GOTO, loopStack.getFirst().getRight());
|
jump(loopStack.getFirst().getRight());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visitContinueTypedStmt(Continue stmt) {
|
public Void visitContinueTypedStmt(Continue stmt) {
|
||||||
method.visitJumpInsn(Opcodes.GOTO, loopStack.getFirst().getLeft());
|
jump(loopStack.getFirst().getLeft());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,15 +416,15 @@ public class TerraScriptClassGenerator {
|
|||||||
private void invertBool() {
|
private void invertBool() {
|
||||||
Label invertToFalse = new Label();
|
Label invertToFalse = new Label();
|
||||||
Label finished = new Label();
|
Label finished = new Label();
|
||||||
method.visitJumpInsn(Opcodes.IFNE, invertToFalse);
|
jumpIf(BOOL_TRUE, invertToFalse);
|
||||||
|
|
||||||
method.visitInsn(Opcodes.ICONST_1);
|
pushFalse();
|
||||||
method.visitJumpInsn(Opcodes.GOTO, finished);
|
jump(finished);
|
||||||
|
|
||||||
method.visitLabel(invertToFalse);
|
label(invertToFalse);
|
||||||
method.visitInsn(Opcodes.ICONST_0);
|
pushFalse();
|
||||||
|
|
||||||
method.visitLabel(finished);
|
label(finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushBinaryOperands(Binary expr) {
|
private void pushBinaryOperands(Binary expr) {
|
||||||
@@ -427,11 +441,11 @@ public class TerraScriptClassGenerator {
|
|||||||
* Pushes boolean on to the stack based on comparison result
|
* Pushes boolean on to the stack based on comparison result
|
||||||
* @param condition
|
* @param condition
|
||||||
*/
|
*/
|
||||||
private void pushComparisonResult(TypedExpr condition) {
|
private void pushComparisonBool(TypedExpr condition) {
|
||||||
Label trueFinished = new Label();
|
Label trueFinished = new Label();
|
||||||
conditionalRunnable(condition, () -> method.visitInsn(Opcodes.ICONST_1), trueFinished);
|
conditionalRunnable(condition, this::pushTrue, trueFinished);
|
||||||
method.visitInsn(Opcodes.ICONST_0);
|
pushFalse();
|
||||||
method.visitLabel(trueFinished);
|
label(trueFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,57 +458,77 @@ public class TerraScriptClassGenerator {
|
|||||||
conditionalRunnable(condition, () -> stmt.accept(this), exit);
|
conditionalRunnable(condition, () -> stmt.accept(this), exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void conditionalRunnable(TypedExpr condition, Runnable trueSection, Label trueFinished) {
|
private void pushTrue() {
|
||||||
|
method.visitInsn(Opcodes.ICONST_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pushFalse() {
|
||||||
|
method.visitInsn(Opcodes.ICONST_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void jumpIf(OpcodeAlias insn, Label label) {
|
||||||
|
method.visitJumpInsn(insn.opcode, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void jump(Label label) {
|
||||||
|
method.visitJumpInsn(Opcodes.GOTO, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void label(Label label) {
|
||||||
|
method.visitLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void conditionalRunnable(TypedExpr condition, Runnable trueBlock, Label trueFinished) {
|
||||||
Label exit = new Label(); // If the first conditional is false, jump over statement and don't execute it
|
Label exit = new Label(); // If the first conditional is false, jump over statement and don't execute it
|
||||||
if (condition instanceof Binary binaryCondition) {
|
if (condition instanceof Binary binaryCondition) {
|
||||||
switch(binaryCondition.operator) {
|
switch(binaryCondition.operator) {
|
||||||
case BOOLEAN_AND -> {
|
case BOOLEAN_AND -> {
|
||||||
// Operands assumed booleans
|
// Operands assumed booleans
|
||||||
binaryCondition.left.accept(this);
|
binaryCondition.left.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit); // If left is false, short circuit, don't evaluate right
|
jumpIf(BOOL_FALSE, exit); // If left is false, short circuit, don't evaluate right
|
||||||
binaryCondition.right.accept(this);
|
binaryCondition.right.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(BOOL_FALSE, exit);
|
||||||
}
|
}
|
||||||
case BOOLEAN_OR -> {
|
case BOOLEAN_OR -> {
|
||||||
Label skipRight = new Label();
|
Label skipRight = new Label();
|
||||||
// Operands assumed booleans
|
// Operands assumed booleans
|
||||||
binaryCondition.left.accept(this);
|
binaryCondition.left.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.IFNE, skipRight); // If left is true, skip evaluating right
|
jumpIf(BOOL_TRUE, skipRight);
|
||||||
binaryCondition.right.accept(this);
|
binaryCondition.right.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(BOOL_FALSE, exit);
|
||||||
method.visitLabel(skipRight);
|
label(skipRight);
|
||||||
}
|
}
|
||||||
case EQUALS -> {
|
case EQUALS -> {
|
||||||
if (binaryOperandsSameType(Type.BOOLEAN, binaryCondition)) { // Operands assumed integers
|
if (binaryOperandsSameType(Type.BOOLEAN, binaryCondition)) { // Operands assumed integers
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitJumpInsn(Opcodes.IF_ICMPNE, exit);
|
jumpIf(INTEGERS_NOT_EQUAL, exit);
|
||||||
|
|
||||||
} else if (binaryOperandsSameType(Type.NUMBER, binaryCondition)) { // Operands assumed doubles
|
} else if (binaryOperandsSameType(Type.NUMBER, binaryCondition)) { // Operands assumed doubles
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitInsn(Opcodes.DCMPG);
|
method.visitInsn(Opcodes.DCMPG);
|
||||||
method.visitJumpInsn(Opcodes.IFNE, exit);
|
jumpIf(CMP_NOT_EQUALS, exit);
|
||||||
|
|
||||||
} else if (binaryOperandsSameType(Type.STRING, binaryCondition)) {
|
} else if (binaryOperandsSameType(Type.STRING, binaryCondition)) {
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(BOOL_FALSE, exit);
|
||||||
} else throw new CompilerBugException();
|
} else throw new CompilerBugException();
|
||||||
}
|
}
|
||||||
case NOT_EQUALS -> {
|
case NOT_EQUALS -> {
|
||||||
if (binaryOperandsSameType(Type.BOOLEAN, binaryCondition)) { // Operands assumed integers
|
if (binaryOperandsSameType(Type.BOOLEAN, binaryCondition)) { // Operands assumed integers
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitJumpInsn(Opcodes.IF_ICMPEQ, exit);
|
jumpIf(INTEGERS_EQUAL, exit);
|
||||||
|
|
||||||
} else if (binaryOperandsSameType(Type.NUMBER, binaryCondition)) { // Operands assumed doubles
|
} else if (binaryOperandsSameType(Type.NUMBER, binaryCondition)) { // Operands assumed doubles
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitInsn(Opcodes.DCMPG);
|
method.visitInsn(Opcodes.DCMPG);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(CMP_EQUALS, exit);
|
||||||
|
|
||||||
} else if (binaryOperandsSameType(Type.STRING, binaryCondition)) { // Operands assumed references
|
} else if (binaryOperandsSameType(Type.STRING, binaryCondition)) { // Operands assumed references
|
||||||
pushBinaryOperands(binaryCondition);
|
pushBinaryOperands(binaryCondition);
|
||||||
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
|
||||||
invertBool();
|
invertBool();
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(CMP_EQUALS, exit);
|
||||||
} else throw new CompilerBugException();
|
} else throw new CompilerBugException();
|
||||||
}
|
}
|
||||||
case GREATER, GREATER_EQUALS, LESS, LESS_EQUALS -> {
|
case GREATER, GREATER_EQUALS, LESS, LESS_EQUALS -> {
|
||||||
@@ -507,11 +541,11 @@ public class TerraScriptClassGenerator {
|
|||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
});
|
});
|
||||||
|
|
||||||
method.visitJumpInsn(switch(binaryCondition.operator) {
|
jumpIf(switch(binaryCondition.operator) {
|
||||||
case GREATER -> Opcodes.IFLE;
|
case GREATER -> CMP_LESS_EQUALS;
|
||||||
case GREATER_EQUALS -> Opcodes.IFLT;
|
case GREATER_EQUALS -> CMP_LESS_THAN;
|
||||||
case LESS -> Opcodes.IFGE;
|
case LESS -> CMP_GREATER_EQUALS;
|
||||||
case LESS_EQUALS -> Opcodes.IFGT;
|
case LESS_EQUALS -> CMP_GREATER_THAN;
|
||||||
default -> throw new IllegalStateException();
|
default -> throw new IllegalStateException();
|
||||||
}, exit);
|
}, exit);
|
||||||
}
|
}
|
||||||
@@ -520,11 +554,11 @@ public class TerraScriptClassGenerator {
|
|||||||
} else {
|
} else {
|
||||||
// Assume condition returns bool
|
// Assume condition returns bool
|
||||||
condition.accept(this);
|
condition.accept(this);
|
||||||
method.visitJumpInsn(Opcodes.IFEQ, exit);
|
jumpIf(BOOL_FALSE, exit);
|
||||||
}
|
}
|
||||||
trueSection.run();
|
trueBlock.run();
|
||||||
method.visitJumpInsn(Opcodes.GOTO, trueFinished); // Jump to end of statement after execution
|
jump(trueFinished); // Jump to end of statement after execution
|
||||||
method.visitLabel(exit);
|
label(exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFunctionDescriptor(List<Type> parameters, Type returnType) {
|
private String getFunctionDescriptor(List<Type> parameters, Type returnType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user