diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index e3df1a24e..a15cb682b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -554,7 +554,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen); new WasmInteropFunctionGenerator(classGenerator, functionTypes).generateFunctions(module); - exceptionHandlingIntrinsic.postProcess(context.callSites); + exceptionHandlingIntrinsic.postProcess(context.callSites()); generateIsSupertypeFunctions(tagRegistry, classGenerator, functions); classGenerator.postProcess(); new WasmSpecialFunctionGenerator(classGenerator, functionTypes, gcIntrinsic.regionSizeExpressions) @@ -821,7 +821,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } var methodGeneratorContext = new MethodGeneratorContextImpl(binaryWriter, - context.getStringPool(), context.getDiagnostics(), context.functions, classGenerator, classes); + context.getStringPool(), context.getDiagnostics(), context.functions(), classGenerator, classes); for (MethodHolder method : methods) { ClassHolder cls = classes.get(method.getOwnerName()); @@ -848,7 +848,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (implementor.hasModifier(ElementModifier.NATIVE)) { var methodGenerator = context.getGenerator(method.getReference()); if (methodGenerator != null) { - var function = context.functions.forMethod(method); + var function = context.functions().forMethod(method); methodGenerator.apply(method.getReference(), function, methodGeneratorContext); } else if (!isShadowStackMethod(method.getReference())) { if (context.getImportedMethod(method.getReference()) == null) { @@ -866,7 +866,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { if (method == implementor) { generator.generate(method.getReference(), implementor); } else { - generateStub(context.functions, method, implementor); + generateStub(context.functions(), method, implementor); } if (dwarfClassGen != null) { var dwarfClass = dwarfClassGen.getClass(method.getOwnerName()); @@ -1203,8 +1203,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { var block = new WasmBlock(false); block.setType(WasmType.INT32); var callSiteId = manager.generateCallSiteId(invocation.getLocation()); - block.getBody().add(manager.generateRegisterCallSite(callSiteId, - invocation.getLocation())); + callSiteId.generateRegister(block.getBody(), invocation.getLocation()); block.getBody().add(arg); arg = block; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java b/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java index 475edcbb8..4cb2d1c1a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java @@ -17,10 +17,9 @@ package org.teavm.backend.wasm.generate; import org.teavm.backend.wasm.model.expression.WasmExpression; -abstract class CachedExpression { - abstract WasmExpression expr(); +public abstract class CachedExpression { + public abstract WasmExpression expr(); - void release() { + public void release() { } - } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java b/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java index b70c3f5bf..f2d01a5d6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java @@ -24,10 +24,10 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.model.TextLocation; -class ExpressionCache { +public class ExpressionCache { private TemporaryVariablePool tmpVars; - ExpressionCache(TemporaryVariablePool tmpVars) { + public ExpressionCache(TemporaryVariablePool tmpVars) { this.tmpVars = tmpVars; } @@ -56,7 +56,7 @@ class ExpressionCache { } @Override - WasmExpression expr() { + public WasmExpression expr() { return new WasmGetLocal(localVar); } } @@ -69,12 +69,12 @@ class ExpressionCache { } @Override - WasmExpression expr() { + public WasmExpression expr() { return new WasmGetLocal(tmpVar); } @Override - void release() { + public void release() { tmpVars.release(tmpVar); } } @@ -87,7 +87,7 @@ class ExpressionCache { } @Override - WasmExpression expr() { + public WasmExpression expr() { return new WasmInt32Constant(value); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java index 44c3d10f6..3bade0474 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java @@ -23,15 +23,15 @@ import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; -class TemporaryVariablePool { +public class TemporaryVariablePool { private WasmFunction function; private Map> temporaryVariablesByType = new HashMap<>(); - TemporaryVariablePool(WasmFunction function) { + public TemporaryVariablePool(WasmFunction function) { this.function = function; } - WasmLocal acquire(WasmType type) { + public WasmLocal acquire(WasmType type) { var stack = temporaryVariablesByType.computeIfAbsent(type, k -> new ArrayDeque<>()); WasmLocal variable = stack.pollFirst(); if (variable == null) { @@ -41,7 +41,7 @@ class TemporaryVariablePool { return variable; } - void release(WasmLocal variable) { + public void release(WasmLocal variable) { var stack = temporaryVariablesByType.get(variable.getType()); stack.push(variable); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index 162b1b2e7..d1664a704 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -22,6 +22,7 @@ import java.util.Map; import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext; import org.teavm.backend.wasm.generators.WasmMethodGenerator; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.model.WasmModule; @@ -42,11 +43,11 @@ import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.Characteristics; -public class WasmGenerationContext { +public class WasmGenerationContext implements BaseWasmGenerationContext { private ClassReaderSource classSource; public final WasmModule module; - public final WasmFunctionTypes functionTypes; - public final WasmFunctionRepository functions; + private final WasmFunctionTypes functionTypes; + private final WasmFunctionRepository functions; private Diagnostics diagnostics; private VirtualTableProvider vtableProvider; private TagRegistry tagRegistry; @@ -59,7 +60,7 @@ public class WasmGenerationContext { private Map intrinsicCache = new HashMap<>(); private Map generatorCache = new HashMap<>(); private WasmTag exceptionTag; - public final List callSites = new ArrayList<>(); + private final List callSites = new ArrayList<>(); public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, WasmFunctionTypes functionTypes, WasmFunctionRepository functions, Diagnostics diagnostics, VirtualTableProvider vtableProvider, @@ -78,6 +79,25 @@ public class WasmGenerationContext { this.exceptionTag = exceptionTag; } + @Override + public WasmFunctionRepository functions() { + return functions; + } + + @Override + public WasmFunctionTypes functionTypes() { + return functionTypes; + } + + @Override + public ClassReaderSource classSource() { + return classSource; + } + + public List callSites() { + return callSites; + } + public void addIntrinsic(WasmIntrinsic intrinsic) { intrinsics.add(intrinsic); } @@ -167,6 +187,7 @@ public class WasmGenerationContext { return diagnostics; } + @Override public WasmTag getExceptionTag() { return exceptionTag; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 77abc4d28..ac75ff4e2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -17,92 +17,46 @@ package org.teavm.backend.wasm.generate; import static org.teavm.model.lowlevel.ExceptionHandlingUtil.isManagedMethodCall; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import org.teavm.ast.ArrayFromDataExpr; import org.teavm.ast.ArrayType; -import org.teavm.ast.AssignmentStatement; -import org.teavm.ast.BinaryExpr; -import org.teavm.ast.BlockStatement; -import org.teavm.ast.BoundCheckExpr; -import org.teavm.ast.BreakStatement; import org.teavm.ast.CastExpr; -import org.teavm.ast.ConditionalExpr; -import org.teavm.ast.ConditionalStatement; -import org.teavm.ast.ConstantExpr; -import org.teavm.ast.ContinueStatement; import org.teavm.ast.Expr; -import org.teavm.ast.ExprVisitor; -import org.teavm.ast.GotoPartStatement; -import org.teavm.ast.IdentifiedStatement; -import org.teavm.ast.InitClassStatement; import org.teavm.ast.InstanceOfExpr; import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationType; -import org.teavm.ast.MonitorEnterStatement; -import org.teavm.ast.MonitorExitStatement; -import org.teavm.ast.NewArrayExpr; -import org.teavm.ast.NewExpr; -import org.teavm.ast.NewMultiArrayExpr; -import org.teavm.ast.OperationType; -import org.teavm.ast.PrimitiveCastExpr; import org.teavm.ast.QualificationExpr; -import org.teavm.ast.ReturnStatement; -import org.teavm.ast.SequentialStatement; import org.teavm.ast.Statement; -import org.teavm.ast.StatementVisitor; import org.teavm.ast.SubscriptExpr; -import org.teavm.ast.SwitchClause; -import org.teavm.ast.SwitchStatement; -import org.teavm.ast.ThrowStatement; import org.teavm.ast.TryCatchStatement; -import org.teavm.ast.UnaryExpr; import org.teavm.ast.UnwrapArrayExpr; -import org.teavm.ast.VariableExpr; -import org.teavm.ast.WhileStatement; import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.DataPrimitives; +import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; -import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmTag; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBreak; import org.teavm.backend.wasm.model.expression.WasmCall; -import org.teavm.backend.wasm.model.expression.WasmCatch; import org.teavm.backend.wasm.model.expression.WasmConditional; -import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmExpression; -import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; -import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; -import org.teavm.backend.wasm.model.expression.WasmFloatBinary; -import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; -import org.teavm.backend.wasm.model.expression.WasmFloatType; import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; -import org.teavm.backend.wasm.model.expression.WasmInt64Constant; import org.teavm.backend.wasm.model.expression.WasmInt64Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; -import org.teavm.backend.wasm.model.expression.WasmIntUnary; -import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; @@ -115,14 +69,10 @@ import org.teavm.backend.wasm.model.expression.WasmStoreFloat64; import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmSwitch; -import org.teavm.backend.wasm.model.expression.WasmThrow; -import org.teavm.backend.wasm.model.expression.WasmTry; import org.teavm.backend.wasm.model.expression.WasmUnreachable; -import org.teavm.backend.wasm.render.WasmTypeInference; import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.model.FieldReference; -import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; @@ -137,16 +87,8 @@ import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.ShadowStack; -class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { +public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { private static final FieldReference MONITOR_FIELD = new FieldReference("java.lang.Object", "monitor"); - private static final MethodReference MONITOR_ENTER_SYNC = new MethodReference(Object.class, - "monitorEnterSync", Object.class, void.class); - private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, - "monitorExitSync", Object.class, void.class); - private static final MethodReference MONITOR_ENTER = new MethodReference(Object.class, - "monitorEnter", Object.class, void.class); - private static final MethodReference MONITOR_EXIT = new MethodReference(Object.class, - "monitorExit", Object.class, void.class); private static final MethodReference CATCH_METHOD = new MethodReference(ExceptionHandling.class, "catchException", Throwable.class); private static final MethodReference PEEK_EXCEPTION_METHOD = new MethodReference(ExceptionHandling.class, @@ -160,20 +102,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private static final MethodReference THROW_AIOOBE_METHOD = new MethodReference(ExceptionHandling.class, "throwArrayIndexOutOfBoundsException", void.class); - private static final int SWITCH_TABLE_THRESHOLD = 256; private WasmGenerationContext context; private WasmClassGenerator classGenerator; - private WasmTypeInference typeInference; - private WasmFunction function; private MethodReference currentMethod; - private int firstVariable; - private IdentifiedStatement currentContinueTarget; - private IdentifiedStatement currentBreakTarget; - private Map breakTargets = new HashMap<>(); - private Map continueTargets = new HashMap<>(); - private Set usedBlocks = new HashSet<>(); - private TemporaryVariablePool tempVars; - private ExpressionCache exprCache; private List handlers = new ArrayList<>(); private WasmBlock lastTryBlock; @@ -182,32 +113,23 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmLocal stackVariable; private BinaryWriter binaryWriter; - private boolean async; private boolean managed; - WasmExpression result; - List resultConsumer; - WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, + public WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod, int firstVariable, boolean async) { + super(context, function, firstVariable, async); this.context = context; this.classGenerator = classGenerator; this.binaryWriter = binaryWriter; - this.function = function; this.currentMethod = currentMethod; - this.firstVariable = firstVariable; - tempVars = new TemporaryVariablePool(function); - exprCache = new ExpressionCache(tempVars); - typeInference = new WasmTypeInference(); - this.async = async; this.managed = context.characteristics.isManaged(currentMethod); } - void generate(Statement statement, List target) { + @Override + public void generate(Statement statement, List target) { var lastTargetSize = target.size(); - resultConsumer = target; - statement.acceptVisitor(this); - resultConsumer = null; + super.generate(statement, target); if (rethrowBlock != null) { var body = target.subList(lastTargetSize, target.size()); rethrowBlock.getBody().addAll(body); @@ -223,334 +145,46 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } } - private void accept(Expr expr) { - expr.acceptVisitor(this); - } - - private void accept(Statement statement) { - statement.acceptVisitor(this); + @Override + protected WasmExpression generateThrowNPE() { + return new WasmCall(context.functions().forStaticMethod(THROW_NPE_METHOD)); } @Override - public void visit(BinaryExpr expr) { - switch (expr.getOperation()) { - case ADD: - generateBinary(WasmIntBinaryOperation.ADD, WasmFloatBinaryOperation.ADD, expr); - break; - case SUBTRACT: - generateBinary(WasmIntBinaryOperation.SUB, WasmFloatBinaryOperation.SUB, expr); - break; - case MULTIPLY: - generateBinary(WasmIntBinaryOperation.MUL, WasmFloatBinaryOperation.MUL, expr); - break; - case DIVIDE: - generateBinary(WasmIntBinaryOperation.DIV_SIGNED, WasmFloatBinaryOperation.DIV, expr); - break; - case MODULO: { - switch (expr.getType()) { - case INT: - case LONG: - generateBinary(WasmIntBinaryOperation.REM_SIGNED, expr); - break; - default: - Class type = convertType(expr.getType()); - MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type); - WasmCall call = new WasmCall(context.functions.forStaticMethod(method)); - - accept(expr.getFirstOperand()); - call.getArguments().add(result); - - accept(expr.getSecondOperand()); - call.getArguments().add(result); - - call.setLocation(expr.getLocation()); - result = call; - break; - } - - break; - } - case BITWISE_AND: - generateBinary(WasmIntBinaryOperation.AND, expr); - break; - case BITWISE_OR: - generateBinary(WasmIntBinaryOperation.OR, expr); - break; - case BITWISE_XOR: - generateBinary(WasmIntBinaryOperation.XOR, expr); - break; - case EQUALS: - generateBinary(WasmIntBinaryOperation.EQ, WasmFloatBinaryOperation.EQ, expr); - break; - case NOT_EQUALS: - generateBinary(WasmIntBinaryOperation.NE, WasmFloatBinaryOperation.NE, expr); - break; - case GREATER: - generateBinary(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, expr); - break; - case GREATER_OR_EQUALS: - generateBinary(WasmIntBinaryOperation.GE_SIGNED, WasmFloatBinaryOperation.GE, expr); - break; - case LESS: - generateBinary(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT, expr); - break; - case LESS_OR_EQUALS: - generateBinary(WasmIntBinaryOperation.LE_SIGNED, WasmFloatBinaryOperation.LE, expr); - break; - case LEFT_SHIFT: - generateBinary(WasmIntBinaryOperation.SHL, expr); - break; - case RIGHT_SHIFT: - generateBinary(WasmIntBinaryOperation.SHR_SIGNED, expr); - break; - case UNSIGNED_RIGHT_SHIFT: - generateBinary(WasmIntBinaryOperation.SHR_UNSIGNED, expr); - break; - case COMPARE: { - Class type = convertType(expr.getType()); - MethodReference method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class); - var call = new WasmCall(context.functions.forStaticMethod(method)); - - accept(expr.getFirstOperand()); - call.getArguments().add(result); - - accept(expr.getSecondOperand()); - call.getArguments().add(result); - - call.setLocation(expr.getLocation()); - result = call; - break; - } - case AND: - generateAnd(expr); - break; - case OR: - generateOr(expr); - break; - } - } - - private void generateBinary(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, BinaryExpr expr) { - accept(expr.getFirstOperand()); - WasmExpression first = result; - accept(expr.getSecondOperand()); - WasmExpression second = result; - - if (expr.getType() == null) { - result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); - } else { - switch (expr.getType()) { - case INT: - result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); - break; - case LONG: - result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); - break; - case FLOAT: - result = new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second); - break; - case DOUBLE: - result = new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second); - break; + public void visit(CastExpr expr) { + var type = expr.getTarget(); + if (type instanceof ValueType.Object) { + var className = ((ValueType.Object) type).getClassName(); + if (!context.characteristics.isManaged(className)) { + expr.getValue().acceptVisitor(this); + return; } } - result.setLocation(expr.getLocation()); - } - - private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) { - accept(expr.getFirstOperand()); - WasmExpression first = result; - accept(expr.getSecondOperand()); - WasmExpression second = result; - - if (expr.getType() == OperationType.LONG) { - switch (expr.getOperation()) { - case LEFT_SHIFT: - case RIGHT_SHIFT: - case UNSIGNED_RIGHT_SHIFT: - second = new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, second); - break; - default: - break; - } - } - - switch (expr.getType()) { - case INT: - result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); - break; - case LONG: - result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); - break; - case FLOAT: - case DOUBLE: - throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType()); - } - - result.setLocation(expr.getLocation()); - } - - private Class convertType(OperationType type) { - switch (type) { - case INT: - return int.class; - case LONG: - return long.class; - case FLOAT: - return float.class; - case DOUBLE: - return double.class; - } - throw new AssertionError(type.toString()); - } - - private void generateAnd(BinaryExpr expr) { - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - - accept(expr.getFirstOperand()); - WasmBranch branch = new WasmBranch(negate(result), block); - branch.setResult(new WasmInt32Constant(0)); - branch.setLocation(expr.getLocation()); - branch.getResult().setLocation(expr.getLocation()); - block.getBody().add(new WasmDrop(branch)); - - accept(expr.getSecondOperand()); - block.getBody().add(result); - - block.setLocation(expr.getLocation()); - - result = block; - } - - private void generateOr(BinaryExpr expr) { - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - - accept(expr.getFirstOperand()); - WasmBranch branch = new WasmBranch(result, block); - branch.setResult(new WasmInt32Constant(1)); - branch.setLocation(expr.getLocation()); - branch.getResult().setLocation(expr.getLocation()); - block.getBody().add(new WasmDrop(branch)); - - accept(expr.getSecondOperand()); - block.getBody().add(result); - - block.setLocation(expr.getLocation()); - - result = block; + super.visit(expr); } @Override - public void visit(UnaryExpr expr) { - switch (expr.getOperation()) { - case INT_TO_BYTE: - accept(expr.getOperand()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, - result, new WasmInt32Constant(24)); - result.setLocation(expr.getLocation()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, - result, new WasmInt32Constant(24)); - result.setLocation(expr.getLocation()); - break; - case INT_TO_SHORT: - accept(expr.getOperand()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, - result, new WasmInt32Constant(16)); - result.setLocation(expr.getLocation()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, - result, new WasmInt32Constant(16)); - result.setLocation(expr.getLocation()); - break; - case INT_TO_CHAR: - accept(expr.getOperand()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, - result, new WasmInt32Constant(16)); - result.setLocation(expr.getLocation()); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, - result, new WasmInt32Constant(16)); - result.setLocation(expr.getLocation()); - break; - case LENGTH: - accept(expr.getOperand()); - result = generateArrayLength(result); - break; - case NOT: - accept(expr.getOperand()); - result = negate(result); - break; - case NEGATE: - accept(expr.getOperand()); - switch (expr.getType()) { - case INT: - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, - new WasmInt32Constant(0), result); - result.setLocation(expr.getLocation()); - break; - case LONG: - result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB, - new WasmInt64Constant(0), result); - result.setLocation(expr.getLocation()); - break; - case FLOAT: - result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB, - new WasmFloat32Constant(0), result); - result.setLocation(expr.getLocation()); - break; - case DOUBLE: - result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB, - new WasmFloat64Constant(0), result); - result.setLocation(expr.getLocation()); - break; - } - break; - case NULL_CHECK: - if (!managed) { - expr.getOperand().acceptVisitor(this); - } else { - result = nullCheck(expr.getOperand(), expr.getLocation()); - } - break; - } + protected WasmExpression generateCast(WasmExpression value, WasmType targetType) { + return value; } - private WasmExpression nullCheck(Expr value, TextLocation location) { - var block = new WasmBlock(false); - block.setType(WasmType.INT32); - block.setLocation(location); - - accept(value); - var cachedValue = exprCache.create(result, WasmType.INT32, location, block.getBody()); - - var check = new WasmBranch(cachedValue.expr(), block); - check.setResult(cachedValue.expr()); - block.getBody().add(new WasmDrop(check)); - - var callSiteId = generateCallSiteId(location); - block.getBody().add(generateRegisterCallSite(callSiteId, location)); - - var call = new WasmCall(context.functions.forStaticMethod(THROW_NPE_METHOD)); - block.getBody().add(call); - - if (context.getExceptionTag() == null) { - var target = throwJumpTarget(); - var breakExpr = new WasmBreak(target); - if (target != rethrowBlock) { - breakExpr.setResult(generateGetHandlerId(callSiteId, location)); - block.getBody().add(new WasmDrop(breakExpr)); - } else { - block.getBody().add(breakExpr); - } - } else { - block.getBody().add(new WasmUnreachable()); - } - - cachedValue.release(); - return block; + @Override + protected WasmExpression peekException() { + return new WasmCall(context.functions().forStaticMethod(PEEK_EXCEPTION_METHOD)); } - private WasmExpression generateArrayLength(WasmExpression array) { + @Override + protected WasmExpression catchException() { + return new WasmCall(context.functions().forStaticMethod(CATCH_METHOD)); + } + + @Override + protected WasmType mapType(ValueType type) { + return WasmGeneratorUtil.mapType(type); + } + + @Override + protected WasmExpression generateArrayLength(WasmExpression array) { int sizeOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeArray.class.getName(), "size")); var length = new WasmLoadInt32(4, array, WasmInt32Subtype.INT32); length.setOffset(sizeOffset); @@ -559,41 +193,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(AssignmentStatement statement) { - Expr left = statement.getLeftValue(); - if (left == null) { - if (statement.getRightValue() instanceof InvocationExpr) { - var invocation = (InvocationExpr) statement.getRightValue(); - invocation(invocation, resultConsumer, invocation.getMethod().getReturnType() != ValueType.VOID); - } else { - accept(statement.getRightValue()); - result.acceptVisitor(typeInference); - if (typeInference.getResult() != null) { - result = new WasmDrop(result); - result.setLocation(statement.getLocation()); - } - resultConsumer.add(result); - result = null; - } - } else if (left instanceof VariableExpr) { - VariableExpr varExpr = (VariableExpr) left; - WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable); - accept(statement.getRightValue()); - var setLocal = new WasmSetLocal(local, result); - setLocal.setLocation(statement.getLocation()); - resultConsumer.add(setLocal); - } else if (left instanceof QualificationExpr) { - QualificationExpr lhs = (QualificationExpr) left; - storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue(), statement.getLocation()); - } else if (left instanceof SubscriptExpr) { - SubscriptExpr lhs = (SubscriptExpr) left; - storeArrayItem(lhs, statement.getRightValue()); - } else { - throw new UnsupportedOperationException("This expression is not supported yet"); - } - } - - private void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { + protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { WasmExpression address = getAddress(qualified, field, location); accept(value); if (field.equals(MONITOR_FIELD)) { @@ -651,15 +251,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { resultConsumer.add(store); } - private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) { - leftValue.getArray().acceptVisitor(this); - WasmExpression array = result; - leftValue.getIndex().acceptVisitor(this); - WasmExpression index = result; - rightValue.acceptVisitor(this); - WasmExpression value = result; - resultConsumer.add(storeArrayItem(getArrayElementPointer(array, index, leftValue.getType()), value, - leftValue.getType())); + @Override + protected WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, + ArrayType type) { + return storeArrayItem(getArrayElementPointer(array, index, type), value, type); } private static WasmExpression storeArrayItem(WasmExpression array, WasmExpression value, ArrayType type) { @@ -685,74 +280,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(ConditionalExpr expr) { - accept(expr.getCondition()); - WasmConditional conditional = new WasmConditional(forCondition(result)); - - accept(expr.getConsequent()); - conditional.getThenBlock().getBody().add(result); - result.acceptVisitor(typeInference); - WasmType thenType = typeInference.getResult(); - conditional.getThenBlock().setType(thenType); - - accept(expr.getAlternative()); - conditional.getElseBlock().getBody().add(result); - result.acceptVisitor(typeInference); - WasmType elseType = typeInference.getResult(); - conditional.getElseBlock().setType(elseType); - - assert thenType == elseType; - conditional.setType(thenType); - - result = conditional; + protected WasmExpression stringLiteral(String s) { + return new WasmInt32Constant(context.getStringPool().getStringPointer(s)); } @Override - public void visit(SequentialStatement statement) { - for (var part : statement.getSequence()) { - part.acceptVisitor(this); - } - } - - @Override - public void visit(ConstantExpr expr) { - if (expr.getValue() == null) { - result = new WasmInt32Constant(0); - } else if (expr.getValue() instanceof Integer) { - result = new WasmInt32Constant((Integer) expr.getValue()); - } else if (expr.getValue() instanceof Long) { - result = new WasmInt64Constant((Long) expr.getValue()); - } else if (expr.getValue() instanceof Float) { - result = new WasmFloat32Constant((Float) expr.getValue()); - } else if (expr.getValue() instanceof Double) { - result = new WasmFloat64Constant((Double) expr.getValue()); - } else if (expr.getValue() instanceof String) { - String str = (String) expr.getValue(); - result = new WasmInt32Constant(context.getStringPool().getStringPointer(str)); - } else if (expr.getValue() instanceof ValueType) { - int pointer = classGenerator.getClassPointer((ValueType) expr.getValue()); - result = new WasmInt32Constant(pointer); - } else { - throw new IllegalArgumentException("Constant unsupported: " + expr.getValue()); - } - } - - @Override - public void visit(ConditionalStatement statement) { - accept(statement.getCondition()); - var conditional = new WasmConditional(forCondition(result)); - visitMany(statement.getConsequent(), conditional.getThenBlock().getBody()); - visitMany(statement.getAlternative(), conditional.getElseBlock().getBody()); - resultConsumer.add(conditional); - } - - @Override - public void visit(VariableExpr expr) { - result = new WasmGetLocal(localVar(expr.getIndex())); - } - - private WasmLocal localVar(int index) { - return function.getLocalVariables().get(index - firstVariable); + protected WasmExpression classLiteral(ValueType type) { + return new WasmInt32Constant(classGenerator.getClassPointer(type)); } @Override @@ -823,178 +357,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, index); } - @Override - public void visit(SwitchStatement statement) { - int min = statement.getClauses().stream() - .flatMapToInt(clause -> Arrays.stream(clause.getConditions())) - .min().orElse(0); - int max = statement.getClauses().stream() - .flatMapToInt(clause -> Arrays.stream(clause.getConditions())) - .max().orElse(0); - - WasmBlock defaultBlock = new WasmBlock(false); - breakTargets.put(statement, defaultBlock); - IdentifiedStatement oldBreakTarget = currentBreakTarget; - currentBreakTarget = statement; - - WasmBlock wrapper = new WasmBlock(false); - accept(statement.getValue()); - WasmExpression condition = result; - WasmBlock initialWrapper = wrapper; - - List clauses = statement.getClauses(); - WasmBlock[] targets = new WasmBlock[clauses.size()]; - for (int i = 0; i < clauses.size(); i++) { - SwitchClause clause = clauses.get(i); - WasmBlock caseBlock = new WasmBlock(false); - caseBlock.getBody().add(wrapper); - targets[i] = wrapper; - - visitMany(clause.getBody(), caseBlock.getBody()); - wrapper = caseBlock; - } - - defaultBlock.getBody().add(wrapper); - visitMany(statement.getDefaultClause(), defaultBlock.getBody()); - WasmBlock defaultTarget = wrapper; - wrapper = defaultBlock; - - if ((long) max - (long) min >= SWITCH_TABLE_THRESHOLD) { - translateSwitchToBinarySearch(statement, condition, initialWrapper, defaultTarget, targets); - } else { - translateSwitchToWasmSwitch(statement, condition, initialWrapper, defaultTarget, targets, min, max); - } - - breakTargets.remove(statement); - currentBreakTarget = oldBreakTarget; - - resultConsumer.add(wrapper); - } - - private void translateSwitchToBinarySearch(SwitchStatement statement, WasmExpression condition, - WasmBlock initialWrapper, WasmBlock defaultTarget, WasmBlock[] targets) { - List entries = new ArrayList<>(); - for (int i = 0; i < statement.getClauses().size(); i++) { - SwitchClause clause = statement.getClauses().get(i); - for (int label : clause.getConditions()) { - entries.add(new TableEntry(label, targets[i])); - } - } - entries.sort(Comparator.comparingInt(entry -> entry.label)); - - var cachedCondition = exprCache.create(condition, WasmType.INT32, statement.getValue().getLocation(), - initialWrapper.getBody()); - - generateBinarySearch(entries, 0, entries.size() - 1, initialWrapper, defaultTarget, cachedCondition); - - cachedCondition.release(); - } - - private void generateBinarySearch(List entries, int lower, int upper, WasmBlock consumer, - WasmBlock defaultTarget, CachedExpression testValue) { - if (upper - lower == 0) { - int label = entries.get(lower).label; - var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, - testValue.expr(), new WasmInt32Constant(label)); - WasmConditional conditional = new WasmConditional(condition); - consumer.getBody().add(conditional); - - conditional.getThenBlock().getBody().add(new WasmBreak(entries.get(lower).target)); - conditional.getElseBlock().getBody().add(new WasmBreak(defaultTarget)); - } else if (upper - lower <= 0) { - consumer.getBody().add(new WasmBreak(defaultTarget)); - } else { - int mid = (upper + lower) / 2; - int label = entries.get(mid).label; - var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, - testValue.expr(), new WasmInt32Constant(label)); - WasmConditional conditional = new WasmConditional(condition); - consumer.getBody().add(conditional); - - generateBinarySearch(entries, mid + 1, upper, conditional.getThenBlock(), defaultTarget, testValue); - generateBinarySearch(entries, lower, mid, conditional.getElseBlock(), defaultTarget, testValue); - } - } - - static class TableEntry { - final int label; - final WasmBlock target; - - TableEntry(int label, WasmBlock target) { - this.label = label; - this.target = target; - } - } - - private void translateSwitchToWasmSwitch(SwitchStatement statement, WasmExpression condition, - WasmBlock initialWrapper, WasmBlock defaultTarget, WasmBlock[] targets, int min, int max) { - if (min != 0) { - condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, condition, - new WasmInt32Constant(min)); - } - - WasmSwitch wasmSwitch = new WasmSwitch(condition, initialWrapper); - initialWrapper.getBody().add(wasmSwitch); - wasmSwitch.setDefaultTarget(defaultTarget); - - WasmBlock[] expandedTargets = new WasmBlock[max - min + 1]; - for (int i = 0; i < statement.getClauses().size(); i++) { - SwitchClause clause = statement.getClauses().get(i); - for (int label : clause.getConditions()) { - expandedTargets[label - min] = targets[i]; - } - } - - for (WasmBlock target : expandedTargets) { - wasmSwitch.getTargets().add(target != null ? target : wasmSwitch.getDefaultTarget()); - } - } - @Override public void visit(UnwrapArrayExpr expr) { accept(expr.getArray()); } @Override - public void visit(WhileStatement statement) { - WasmBlock wrapper = new WasmBlock(false); - WasmBlock loop = new WasmBlock(true); - - continueTargets.put(statement, loop); - breakTargets.put(statement, wrapper); - IdentifiedStatement oldBreakTarget = currentBreakTarget; - IdentifiedStatement oldContinueTarget = currentContinueTarget; - currentBreakTarget = statement; - currentContinueTarget = statement; - - if (statement.getCondition() != null) { - accept(statement.getCondition()); - loop.getBody().add(new WasmBranch(negate(result), wrapper)); - usedBlocks.add(wrapper); - } - - visitMany(statement.getBody(), loop.getBody()); - loop.getBody().add(new WasmBreak(loop)); - - currentBreakTarget = oldBreakTarget; - currentContinueTarget = oldContinueTarget; - continueTargets.remove(statement); - breakTargets.remove(statement); - - if (usedBlocks.contains(wrapper)) { - wrapper.getBody().add(loop); - resultConsumer.add(wrapper); - } else { - resultConsumer.add(loop); - } - } - - @Override - public void visit(InvocationExpr expr) { - result = invocation(expr, null, false); - } - - private WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { + protected WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { if (expr.getMethod().getClassName().equals(ShadowStack.class.getName())) { switch (expr.getMethod().getName()) { case "allocStack": @@ -1042,62 +411,44 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return trivialInvocation(resultExpr, resultConsumer, expr.getLocation(), willDrop); } - var callSiteId = generateCallSiteId(expr.getLocation()); - if (needsCallSiteId() && isManagedMethodCall(context.characteristics, expr.getMethod())) { - var invocation = generateInvocation(expr, callSiteId); - var type = WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()); + return super.invocation(expr, resultConsumer, willDrop); + } - List targetList; - WasmBlock block; - if (resultConsumer != null) { - targetList = resultConsumer; - result = null; - block = null; - } else { - block = new WasmBlock(false); - block.setType(type); - block.setLocation(expr.getLocation()); - targetList = block.getBody(); - result = block; - } - - if (expr.getArguments().isEmpty()) { - targetList.add(generateRegisterCallSite(callSiteId, expr.getLocation())); - } - - WasmLocal resultVar = null; - if (!willDrop) { - if (type != null) { - resultVar = tempVars.acquire(type); - var setLocal = new WasmSetLocal(resultVar, invocation); - setLocal.setLocation(expr.getLocation()); - targetList.add(setLocal); - } else { - targetList.add(invocation); - } - } else if (type == null) { - targetList.add(invocation); - } else { - var drop = new WasmDrop(invocation); - drop.setLocation(expr.getLocation()); - targetList.add(drop); - } - - if (context.getExceptionTag() == null) { - checkHandlerId(targetList, callSiteId, expr.getLocation()); - } - if (resultVar != null) { - var getLocal = new WasmGetLocal(resultVar); - getLocal.setLocation(expr.getLocation()); - targetList.add(getLocal); - tempVars.release(resultVar); - } - - return block; - } else { - var resultExpr = generateInvocation(expr, -1); - return trivialInvocation(resultExpr, resultConsumer, expr.getLocation(), willDrop); + @Override + protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method, + List arguments) { + int vtableOffset = classGenerator.getClassSize(RuntimeClass.class.getName()); + VirtualTable vtable = context.getVirtualTableProvider().lookup(method.getClassName()); + if (vtable != null) { + vtable = vtable.findMethodContainer(method.getDescriptor()); } + if (vtable == null) { + return new WasmUnreachable(); + } + int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor()); + if (vtable.getParent() != null) { + vtableIndex += vtable.getParent().size(); + } + var classRef = getReferenceToClass(new WasmGetLocal(instance)); + var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32); + methodIndex.setOffset(vtableIndex * 4 + vtableOffset); + + var parameterTypes = new WasmType[method.parameterCount() + 1]; + parameterTypes[0] = WasmType.INT32; + for (var i = 0; i < method.parameterCount(); ++i) { + parameterTypes[i + 1] = WasmGeneratorUtil.mapType(method.parameterType(i)); + } + var functionType = context.functionTypes().of(WasmGeneratorUtil.mapType(method.getReturnType()), + parameterTypes); + var call = new WasmIndirectCall(methodIndex, functionType); + call.getArguments().addAll(arguments); + return call; + } + + private WasmExpression getReferenceToClass(WasmExpression instance) { + var classIndex = new WasmLoadInt32(4, instance, WasmInt32Subtype.INT32); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classIndex, + new WasmInt32Constant(3)); } private WasmExpression trivialInvocation(WasmExpression resultExpr, List resultConsumer, @@ -1117,34 +468,100 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } } - private int generateCallSiteId(TextLocation location) { + @Override + protected CallSiteIdentifier generateCallSiteId(TextLocation location) { + return generateCallSiteIdImpl(location); + } + + private CallSiteIdentifierImpl generateCallSiteIdImpl(TextLocation location) { var callSiteLocations = CallSiteLocation.fromTextLocation(location, currentMethod); - var callSite = new CallSiteDescriptor(context.callSites.size(), callSiteLocations); + var callSite = new CallSiteDescriptor(context.callSites().size(), callSiteLocations); var reverseHandlers = new ArrayList<>(handlers); Collections.reverse(reverseHandlers); callSite.getHandlers().addAll(reverseHandlers); - context.callSites.add(callSite); - return callSite.getId(); + context.callSites().add(callSite); + return new CallSiteIdentifierImpl(callSite.getId()); } - private void checkHandlerId(List target, int callSiteId, TextLocation location) { - var jumpTarget = throwJumpTarget(); - if (jumpTarget == rethrowBlock) { - var handlerId = generateGetHandlerId(callSiteId, location); - var br = new WasmBranch(handlerId, throwJumpTarget()); - target.add(br); - } else { - var handlerVar = tempVars.acquire(WasmType.INT32); - var handlerId = generateGetHandlerId(callSiteId, location); - var saveHandler = new WasmSetLocal(handlerVar, handlerId); - saveHandler.setLocation(location); - target.add(saveHandler); - var br = new WasmBranch(new WasmGetLocal(handlerVar), throwJumpTarget()); - br.setResult(new WasmGetLocal(handlerVar)); - var dropBr = new WasmDrop(br); - dropBr.setLocation(location); - target.add(dropBr); - tempVars.release(handlerVar); + @Override + public boolean isManaged() { + return managed; + } + + @Override + protected boolean isManagedCall(MethodReference method) { + return isManagedMethodCall(context.characteristics, method); + } + + @Override + protected WasmExpression generateThrow(WasmExpression expression) { + return new WasmCall(context.functions().forStaticMethod(THROW_METHOD), result); + } + + private class CallSiteIdentifierImpl extends CallSiteIdentifier + implements WasmIntrinsicManager.CallSiteIdentifier { + private int id; + + CallSiteIdentifierImpl(int id) { + this.id = id; + } + + @Override + public void generateRegister(List consumer, TextLocation location) { + var result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), new WasmInt32Constant(id), + WasmInt32Subtype.INT32); + result.setLocation(location); + consumer.add(result); + } + + @Override + public void checkHandlerId(List target, TextLocation location) { + if (context.getExceptionTag() != null) { + return; + } + var jumpTarget = throwJumpTarget(); + if (jumpTarget == rethrowBlock) { + var handlerId = generateGetHandlerId(location); + var br = new WasmBranch(handlerId, throwJumpTarget()); + target.add(br); + } else { + var handlerVar = tempVars.acquire(WasmType.INT32); + var handlerId = generateGetHandlerId(location); + var saveHandler = new WasmSetLocal(handlerVar, handlerId); + saveHandler.setLocation(location); + target.add(saveHandler); + var br = new WasmBranch(new WasmGetLocal(handlerVar), throwJumpTarget()); + br.setResult(new WasmGetLocal(handlerVar)); + var dropBr = new WasmDrop(br); + dropBr.setLocation(location); + target.add(dropBr); + tempVars.release(handlerVar); + } + } + + @Override + public void generateThrow(List target, TextLocation location) { + if (context.getExceptionTag() == null) { + var throwTarget = throwJumpTarget(); + var breakExpr = new WasmBreak(throwTarget); + if (throwTarget != rethrowBlock) { + breakExpr.setResult(generateGetHandlerId(location)); + target.add(new WasmDrop(breakExpr)); + } else { + target.add(breakExpr); + } + } else { + target.add(new WasmUnreachable()); + } + } + + + private WasmExpression generateGetHandlerId(TextLocation location) { + WasmExpression handlerId = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32); + handlerId = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, handlerId, + new WasmInt32Constant(id)); + handlerId.setLocation(location); + return handlerId; } } @@ -1152,108 +569,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return lastTryBlock != null ? lastTryBlock : rethrowBlock(); } - private WasmExpression generateInvocation(InvocationExpr expr, int callSiteId) { - if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { - MethodReader method = context.getClassSource().resolve(expr.getMethod()); - MethodReference reference = method != null ? method.getReference() : expr.getMethod(); - var function = context.functions.forMethod(reference, expr.getType() == InvocationType.STATIC); - - var call = new WasmCall(function); - for (Expr argument : expr.getArguments()) { - accept(argument); - call.getArguments().add(result); - } - addCallSiteIdToLastArg(callSiteId, call.getArguments()); - call.setLocation(expr.getLocation()); - return call; - } else if (expr.getType() == InvocationType.CONSTRUCTOR) { - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - - WasmLocal tmp = tempVars.acquire(WasmType.INT32); - if (callSiteId >= 0) { - generateRegisterCallSite(callSiteId, expr.getLocation()); - } - block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), - expr.getLocation()))); - - var function = context.functions.forInstanceMethod(expr.getMethod()); - var call = new WasmCall(function); - call.getArguments().add(new WasmGetLocal(tmp)); - for (Expr argument : expr.getArguments()) { - accept(argument); - call.getArguments().add(result); - } - addCallSiteIdToLastArg(callSiteId, call.getArguments()); - block.getBody().add(call); - - block.getBody().add(new WasmGetLocal(tmp)); - tempVars.release(tmp); - - return block; - } else { - MethodReference reference = expr.getMethod(); - accept(expr.getArguments().get(0)); - WasmExpression instance = result; - WasmBlock block = new WasmBlock(false); - block.setType(WasmGeneratorUtil.mapType(reference.getReturnType())); - - WasmLocal instanceVar = tempVars.acquire(WasmType.INT32); - block.getBody().add(new WasmSetLocal(instanceVar, instance)); - instance = new WasmGetLocal(instanceVar); - - int vtableOffset = classGenerator.getClassSize(RuntimeClass.class.getName()); - VirtualTable vtable = context.getVirtualTableProvider().lookup(reference.getClassName()); - if (vtable != null) { - vtable = vtable.findMethodContainer(reference.getDescriptor()); - } - if (vtable == null) { - return new WasmUnreachable(); - } - int vtableIndex = vtable.getMethods().indexOf(reference.getDescriptor()); - if (vtable.getParent() != null) { - vtableIndex += vtable.getParent().size(); - } - var classRef = getReferenceToClass(instance); - var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32); - methodIndex.setOffset(vtableIndex * 4 + vtableOffset); - - var parameterTypes = new WasmType[expr.getMethod().parameterCount() + 1]; - parameterTypes[0] = WasmType.INT32; - for (var i = 0; i < expr.getMethod().parameterCount(); ++i) { - parameterTypes[i + 1] = WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i)); - } - var functionType = context.functionTypes.of(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()), - parameterTypes); - var call = new WasmIndirectCall(methodIndex, functionType); - - call.getArguments().add(instance); - for (int i = 1; i < expr.getArguments().size(); ++i) { - accept(expr.getArguments().get(i)); - call.getArguments().add(result); - } - addCallSiteIdToLastArg(callSiteId, call.getArguments()); - - block.getBody().add(call); - tempVars.release(instanceVar); - return block; - } - } - - private void addCallSiteIdToLastArg(int callSiteId, List args) { - if (args.isEmpty() || callSiteId < 0) { - return; - } - var arg = args.get(args.size() - 1); - var block = new WasmBlock(false); - arg.acceptVisitor(typeInference); - block.setType(typeInference.getResult()); - block.setLocation(arg.getLocation()); - block.getBody().add(arg); - args.set(args.size() - 1, block); - block.getBody().add(generateRegisterCallSite(callSiteId, arg.getLocation())); - } - private boolean needsCallSiteId() { return managed; } @@ -1286,26 +601,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32); } - private WasmExpression generateRegisterCallSite(int callSite, TextLocation location) { - return generateRegisterCallSite(new WasmInt32Constant(callSite), location); - } - - private WasmExpression generateRegisterCallSite(WasmExpression callSite, TextLocation location) { - var result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), callSite, WasmInt32Subtype.INT32); - result.setLocation(location); - return result; - } - - private WasmExpression generateGetHandlerId(int callSite, TextLocation location) { - WasmExpression handlerId = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32); - if (callSite > 0) { - handlerId = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, handlerId, - new WasmInt32Constant(callSite)); - } - handlerId.setLocation(location); - return handlerId; - } - private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) { if (stackVariable == null) { throw new IllegalStateException("Call to ShadowStack.registerGCRoot must be dominated by " @@ -1360,23 +655,6 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } } - @Override - public void visit(BlockStatement statement) { - WasmBlock block = new WasmBlock(false); - - if (statement.getId() != null) { - breakTargets.put(statement, block); - } - - visitMany(statement.getBody(), block.getBody()); - - if (statement.getId() != null) { - breakTargets.remove(statement); - } - - resultConsumer.add(block); - } - @Override public void visit(QualificationExpr expr) { WasmExpression address = getAddress(expr.getQualified(), expr.getField(), expr.getLocation()); @@ -1454,6 +732,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return result; } else { accept(qualified); + assert result != null; return result; } } @@ -1466,45 +745,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(BreakStatement statement) { - IdentifiedStatement target = statement.getTarget(); - if (target == null) { - target = currentBreakTarget; - } - WasmBlock wasmTarget = breakTargets.get(target); - usedBlocks.add(wasmTarget); - var br = new WasmBreak(wasmTarget); - br.setLocation(statement.getLocation()); - resultConsumer.add(br); - } - - @Override - public void visit(ContinueStatement statement) { - IdentifiedStatement target = statement.getTarget(); - if (target == null) { - target = currentContinueTarget; - } - WasmBlock wasmTarget = continueTargets.get(target); - usedBlocks.add(wasmTarget); - var br = new WasmBreak(wasmTarget); - br.setLocation(statement.getLocation()); - resultConsumer.add(br); - } - - @Override - public void visit(NewExpr expr) { - var block = new WasmBlock(false); - block.setLocation(expr.getLocation()); - block.setType(WasmType.INT32); - var callSiteId = generateCallSiteId(expr.getLocation()); - block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - block.getBody().add(allocateObject(expr.getConstructedClass(), expr.getLocation())); - result = block; - } - - private WasmExpression allocateObject(String className, TextLocation location) { + protected WasmExpression allocateObject(String className, TextLocation location) { int tag = classGenerator.getClassPointer(ValueType.object(className)); - var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocate", + var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocate", RuntimeClass.class, Address.class)); WasmCall call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(tag)); @@ -1513,129 +756,44 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(NewArrayExpr expr) { - var block = new WasmBlock(false); - block.setType(WasmType.INT32); - - var callSiteId = generateCallSiteId(expr.getLocation()); - block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - - ValueType type = expr.getType(); - - int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateArray", + protected WasmExpression allocateArray(ValueType itemType, WasmExpression length, TextLocation location) { + int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(itemType)); + var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class)); var call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); - accept(expr.getLength()); - call.getArguments().add(result); - call.setLocation(expr.getLocation()); - block.getBody().add(call); - - result = block; + call.getArguments().add(length); + call.setLocation(location); + return call; } @Override - public void visit(ArrayFromDataExpr expr) { - ValueType type = expr.getType(); - - ArrayType arrayType = ArrayType.OBJECT; - if (type instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) type).getKind()) { - case BOOLEAN: - case BYTE: - arrayType = ArrayType.BYTE; - break; - case SHORT: - arrayType = ArrayType.SHORT; - break; - case CHARACTER: - arrayType = ArrayType.CHAR; - break; - case INTEGER: - arrayType = ArrayType.INT; - break; - case LONG: - arrayType = ArrayType.LONG; - break; - case FLOAT: - arrayType = ArrayType.FLOAT; - break; - case DOUBLE: - arrayType = ArrayType.DOUBLE; - break; - } - } - - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - var callSiteId = generateCallSiteId(expr.getLocation()); - block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - - WasmLocal array = tempVars.acquire(WasmType.INT32); - int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); - var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateArray", - RuntimeClass.class, int.class, Address.class)); - WasmCall call = new WasmCall(allocFunction); - call.getArguments().add(new WasmInt32Constant(classPointer)); - call.getArguments().add(new WasmInt32Constant(expr.getData().size())); - call.setLocation(expr.getLocation()); - block.getBody().add(new WasmSetLocal(array, call)); - - for (int i = 0; i < expr.getData().size(); ++i) { - WasmExpression ptr = getArrayElementPointer(new WasmGetLocal(array), new WasmInt32Constant(i), arrayType); - expr.getData().get(i).acceptVisitor(this); - block.getBody().add(storeArrayItem(ptr, result, arrayType)); - } - - block.getBody().add(new WasmGetLocal(array)); - block.setLocation(expr.getLocation()); - tempVars.release(array); - - result = block; - } - - @Override - public void visit(NewMultiArrayExpr expr) { - ValueType type = expr.getType(); - - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - + protected WasmExpression allocateMultiArray(List target, ValueType itemType, + List dimensions, TextLocation location) { int dimensionList = -1; - for (Expr dimension : expr.getDimensions()) { + for (var dimension : dimensions) { int dimensionAddress = binaryWriter.append(DataPrimitives.INT.createValue()); if (dimensionList < 0) { dimensionList = dimensionAddress; } - accept(dimension); - block.getBody().add(new WasmStoreInt32(4, new WasmInt32Constant(dimensionAddress), result, + target.add(new WasmStoreInt32(4, new WasmInt32Constant(dimensionAddress), dimension, WasmInt32Subtype.INT32)); } - int classPointer = classGenerator.getClassPointer(type); - var allocFunction = context.functions.forStaticMethod(new MethodReference(Allocator.class, "allocateMultiArray", - RuntimeClass.class, Address.class, int.class, RuntimeArray.class)); + int classPointer = classGenerator.getClassPointer(itemType); + var allocFunction = context.functions().forStaticMethod(new MethodReference(Allocator.class, + "allocateMultiArray", RuntimeClass.class, Address.class, int.class, RuntimeArray.class)); var call = new WasmCall(allocFunction); call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(dimensionList)); - call.getArguments().add(new WasmInt32Constant(expr.getDimensions().size())); - call.setLocation(expr.getLocation()); - - block.getBody().add(call); - result = block; + call.getArguments().add(new WasmInt32Constant(dimensions.size())); + call.setLocation(location); + return call; } @Override - public void visit(ReturnStatement statement) { - if (statement.getResult() != null) { - accept(statement.getResult()); - } else { - result = null; - } - var wasmStatement = new WasmReturn(result); - wasmStatement.setLocation(statement.getLocation()); - resultConsumer.add(wasmStatement); + public void visit(InvocationExpr expr) { + result = invocation(expr, null, false); } @Override @@ -1649,29 +807,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } } - classGenerator.getClassPointer(type); - - accept(expr.getExpr()); - - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - block.setLocation(expr.getLocation()); - - var cachedObject = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); - - var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); - ifNull.setResult(new WasmInt32Constant(0)); - block.getBody().add(new WasmDrop(ifNull)); - - block.getBody().add(instanceOfImpl(cachedObject.expr(), type)); - - cachedObject.release(); - - result = block; + super.visit(expr); } - private WasmExpression instanceOfImpl(WasmExpression expression, ValueType type) { - var supertypeCall = new WasmCall(context.functions.forSupertype(type)); + @Override + protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) { + classGenerator.getClassPointer(type); + var supertypeCall = new WasmCall(context.functions().forSupertype(type)); WasmExpression classRef = new WasmLoadInt32(4, expression, WasmInt32Subtype.INT32); classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, new WasmInt32Constant(3)); @@ -1680,126 +822,28 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(ThrowStatement statement) { - var callSiteId = generateCallSiteId(statement.getLocation()); - resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); + protected WasmExpression generateThrowCCE() { + return new WasmCall(context.functions().forStaticMethod(THROW_CCE_METHOD)); + } - accept(statement.getException()); - var call = new WasmCall(context.functions.forStaticMethod(THROW_METHOD), result); - call.setLocation(statement.getLocation()); - resultConsumer.add(call); + @Override + protected WasmExpression generateClassInitializer(String className, TextLocation location) { + var call = new WasmCall(context.functions().forClassInitializer(className)); + call.setLocation(location); + return call; + } + @Override + protected boolean needsClassInitializer(String clasName) { + return classGenerator.hasClinit(clasName); + } + + @Override + protected void generateTry(List tryCatchStatements, List protectedBody) { if (context.getExceptionTag() == null) { - var target = throwJumpTarget(); - var breakExpr = new WasmBreak(target); - breakExpr.setLocation(statement.getLocation()); - if (target != rethrowBlock) { - breakExpr.setResult(generateGetHandlerId(callSiteId, statement.getLocation())); - } - resultConsumer.add(breakExpr); + emulatedTry(tryCatchStatements, protectedBody); } else { - resultConsumer.add(new WasmUnreachable()); - } - } - - @Override - public void visit(CastExpr expr) { - var type = expr.getTarget(); - if (type instanceof ValueType.Object) { - var className = ((ValueType.Object) type).getClassName(); - if (!context.characteristics.isManaged(className)) { - expr.getValue().acceptVisitor(this); - return; - } - } - - classGenerator.getClassPointer(type); - - var block = new WasmBlock(false); - block.setType(WasmType.INT32); - block.setLocation(expr.getLocation()); - - accept(expr.getValue()); - var valueToCast = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); - - var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block); - nullCheck.setResult(valueToCast.expr()); - block.getBody().add(new WasmDrop(nullCheck)); - - var supertypeCall = new WasmCall(context.functions.forSupertype(expr.getTarget())); - WasmExpression classRef = new WasmLoadInt32(4, valueToCast.expr(), WasmInt32Subtype.INT32); - classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, - new WasmInt32Constant(3)); - supertypeCall.getArguments().add(classRef); - - var breakIfPassed = new WasmBranch(supertypeCall, block); - breakIfPassed.setResult(valueToCast.expr()); - block.getBody().add(new WasmDrop(breakIfPassed)); - - var callSiteId = generateCallSiteId(expr.getLocation()); - block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - - var call = new WasmCall(context.functions.forStaticMethod(THROW_CCE_METHOD)); - block.getBody().add(call); - - if (context.getExceptionTag() == null) { - var target = throwJumpTarget(); - var breakExpr = new WasmBreak(target); - if (target != rethrowBlock) { - breakExpr.setResult(generateGetHandlerId(callSiteId, expr.getLocation())); - } - block.getBody().add(breakExpr); - } else { - block.getBody().add(new WasmUnreachable()); - } - - valueToCast.release(); - result = block; - } - - @Override - public void visit(InitClassStatement statement) { - if (classGenerator.hasClinit(statement.getClassName())) { - var call = new WasmCall(context.functions.forClassInitializer(statement.getClassName())); - call.setLocation(statement.getLocation()); - - var callSiteId = generateCallSiteId(statement.getLocation()); - resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); - resultConsumer.add(call); - if (context.getExceptionTag() == null) { - checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); - } - } - } - - @Override - public void visit(PrimitiveCastExpr expr) { - accept(expr.getValue()); - result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()), - WasmGeneratorUtil.mapType(expr.getTarget()), true, result); - result.setLocation(expr.getLocation()); - } - - @Override - public void visit(TryCatchStatement statement) { - var tryCatchStatements = new ArrayList(); - while (true) { - if (statement.getProtectedBody().size() != 1) { - break; - } - var next = statement.getProtectedBody().get(0); - if (!(next instanceof TryCatchStatement)) { - break; - } - tryCatchStatements.add(statement); - statement = (TryCatchStatement) next; - } - tryCatchStatements.add(statement); - - if (context.getExceptionTag() == null) { - emulatedTry(tryCatchStatements, statement.getProtectedBody()); - } else { - builtInTry(tryCatchStatements, statement.getProtectedBody()); + super.generateTry(tryCatchStatements, protectedBody); } } @@ -1851,70 +895,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { var tryCatch = tryCatchStatements.get(i); var catchBlock = catchBlocks.get(i); catchBlock.getBody().add(currentBlock); - var catchFunction = context.functions.forStaticMethod(CATCH_METHOD); - var catchCall = new WasmCall(catchFunction); - var catchWrapper = tryCatch.getExceptionVariable() != null - ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) - : new WasmDrop(catchCall); - catchBlock.getBody().add(catchWrapper); - visitMany(tryCatch.getHandler(), catchBlock.getBody()); - if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) { - catchBlock.getBody().add(new WasmBreak(outerCatchBlock)); - } - currentBlock = catchBlock; - } - - resultConsumer.add(outerCatchBlock); - } - - private void builtInTry(List tryCatchStatements, List protectedBody) { - var innerCatchBlock = new WasmBlock(false); - - var catchBlocks = new ArrayList(); - for (int i = 0; i < tryCatchStatements.size(); ++i) { - catchBlocks.add(new WasmBlock(false)); - } - var outerCatchBlock = catchBlocks.get(0); - - var tryBlock = new WasmTry(); - visitMany(protectedBody, tryBlock.getBody()); - if (!tryBlock.isTerminating()) { - tryBlock.getBody().add(new WasmBreak(outerCatchBlock)); - } - var catchClause = new WasmCatch(context.getExceptionTag()); - tryBlock.getCatches().add(catchClause); - innerCatchBlock.getBody().add(tryBlock); - - var obj = exprCache.create(new WasmCall(context.functions.forStaticMethod(PEEK_EXCEPTION_METHOD)), - WasmType.INT32, null, innerCatchBlock.getBody()); - var currentBlock = innerCatchBlock; - boolean catchesAll = false; - for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { - var tryCatch = tryCatchStatements.get(i); - var catchBlock = catchBlocks.get(i); - if (tryCatch.getExceptionType() != null && !tryCatch.getExceptionType().equals(Throwable.class.getName())) { - var exceptionType = ValueType.object(tryCatch.getExceptionType()); - classGenerator.getClassPointer(exceptionType); - var isMatched = instanceOfImpl(obj.expr(), exceptionType); - innerCatchBlock.getBody().add(new WasmBranch(isMatched, currentBlock)); - } else { - innerCatchBlock.getBody().add(new WasmBreak(currentBlock)); - catchesAll = true; - } - currentBlock = catchBlock; - } - if (!catchesAll) { - innerCatchBlock.getBody().add(new WasmThrow(context.getExceptionTag())); - } - obj.release(); - - currentBlock = innerCatchBlock; - for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { - var tryCatch = tryCatchStatements.get(i); - var catchBlock = catchBlocks.get(i); - catchBlock.getBody().add(currentBlock); - - var catchFunction = context.functions.forStaticMethod(CATCH_METHOD); + var catchFunction = context.functions().forStaticMethod(CATCH_METHOD); var catchCall = new WasmCall(catchFunction); var catchWrapper = tryCatch.getExceptionVariable() != null ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) @@ -1947,245 +928,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public void visit(GotoPartStatement statement) { - } - - @Override - public void visit(MonitorEnterStatement statement) { - var call = new WasmCall(context.functions.forStaticMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)); - call.setLocation(statement.getLocation()); - statement.getObjectRef().acceptVisitor(this); - call.getArguments().add(result); - - var callSiteId = generateCallSiteId(statement.getLocation()); - resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); - resultConsumer.add(call); - checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); - } - - @Override - public void visit(MonitorExitStatement statement) { - var call = new WasmCall(context.functions.forStaticMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); - call.setLocation(statement.getLocation()); - statement.getObjectRef().acceptVisitor(this); - call.getArguments().add(result); - - var callSiteId = generateCallSiteId(statement.getLocation()); - resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); - resultConsumer.add(call); - checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); - } - - @Override - public void visit(BoundCheckExpr expr) { - if (!managed) { - expr.getIndex().acceptVisitor(this); - return; - } - - var block = new WasmBlock(false); - block.setType(WasmType.INT32); - block.setLocation(expr.getLocation()); - - accept(expr.getIndex()); - var index = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); - - if (expr.getArray() != null) { - var condBlock = block; - if (expr.isLower()) { - condBlock = new WasmBlock(false); - block.getBody().add(condBlock); - - var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, - index.expr(), new WasmInt32Constant(0)); - var lowerBranch = new WasmBranch(lowerCond, condBlock); - condBlock.getBody().add(lowerBranch); - } - - accept(expr.getArray()); - var upperBound = generateArrayLength(result); - var upperCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, - index.expr(), upperBound); - var upperBranch = new WasmBranch(upperCond, block); - upperBranch.setResult(index.expr()); - condBlock.getBody().add(new WasmDrop(upperBranch)); - } else if (expr.isLower()) { - var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, - index.expr(), new WasmInt32Constant(0)); - var lowerBranch = new WasmBranch(lowerCond, block); - lowerBranch.setResult(index.expr()); - block.getBody().add(new WasmDrop(lowerBranch)); - } - - var callSiteId = generateCallSiteId(expr.getLocation()); - block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - block.getBody().add(new WasmCall(context.functions.forStaticMethod(THROW_AIOOBE_METHOD))); - if (context.getExceptionTag() == null) { - var br = new WasmBreak(throwJumpTarget()); - if (br.getTarget() != rethrowBlock) { - br.setResult(generateGetHandlerId(callSiteId, expr.getLocation())); - } - block.getBody().add(br); - } else { - block.getBody().add(new WasmUnreachable()); - } - - result = block; - } - - private static WasmExpression negate(WasmExpression expr) { - if (expr instanceof WasmIntBinary) { - var binary = (WasmIntBinary) expr; - if (binary.getType() == WasmIntType.INT32 && binary.getOperation() == WasmIntBinaryOperation.XOR) { - if (isOne(binary.getFirst())) { - var result = binary.getSecond(); - if (result.getLocation() == null && expr.getLocation() != null) { - result.setLocation(expr.getLocation()); - } - return result; - } - if (isOne(binary.getSecond())) { - var result = binary.getFirst(); - if (result.getLocation() == null && expr.getLocation() != null) { - result.setLocation(expr.getLocation()); - } - return result; - } - } - - var negatedOp = negate(binary.getOperation()); - if (negatedOp != null) { - var result = new WasmIntBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); - result.setLocation(expr.getLocation()); - return result; - } - } else if (expr instanceof WasmFloatBinary) { - var binary = (WasmFloatBinary) expr; - var negatedOp = negate(binary.getOperation()); - if (negatedOp != null) { - var result = new WasmFloatBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); - result.setLocation(expr.getLocation()); - return result; - } - } - - var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, expr, new WasmInt32Constant(0)); - result.setLocation(expr.getLocation()); - return result; - } - - private static boolean isOne(WasmExpression expression) { - return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 1; - } - - private static boolean isZero(WasmExpression expression) { - return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 0; - } - - private boolean isBoolean(WasmExpression expression) { - if (expression instanceof WasmIntBinary) { - WasmIntBinary binary = (WasmIntBinary) expression; - switch (binary.getOperation()) { - case EQ: - case NE: - case LT_SIGNED: - case LT_UNSIGNED: - case LE_SIGNED: - case LE_UNSIGNED: - case GT_SIGNED: - case GT_UNSIGNED: - case GE_SIGNED: - case GE_UNSIGNED: - return true; - default: - return false; - } - } else if (expression instanceof WasmFloatBinary) { - WasmFloatBinary binary = (WasmFloatBinary) expression; - switch (binary.getOperation()) { - case EQ: - case NE: - case LT: - case LE: - case GT: - case GE: - return true; - default: - return false; - } - } - return false; - } - - private WasmExpression forCondition(WasmExpression expression) { - if (expression instanceof WasmIntBinary) { - WasmIntBinary binary = (WasmIntBinary) expression; - switch (binary.getOperation()) { - case EQ: - if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) { - return negate(binary.getSecond()); - } else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) { - return negate(binary.getFirst()); - } - break; - case NE: - if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) { - return binary.getSecond(); - } else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) { - return binary.getFirst(); - } - break; - default: - break; - } - } - return expression; - } - - private static WasmIntBinaryOperation negate(WasmIntBinaryOperation op) { - switch (op) { - case EQ: - return WasmIntBinaryOperation.NE; - case NE: - return WasmIntBinaryOperation.EQ; - case LT_SIGNED: - return WasmIntBinaryOperation.GE_SIGNED; - case LT_UNSIGNED: - return WasmIntBinaryOperation.GE_UNSIGNED; - case LE_SIGNED: - return WasmIntBinaryOperation.GT_SIGNED; - case LE_UNSIGNED: - return WasmIntBinaryOperation.GT_UNSIGNED; - case GT_SIGNED: - return WasmIntBinaryOperation.LE_SIGNED; - case GT_UNSIGNED: - return WasmIntBinaryOperation.LE_UNSIGNED; - case GE_SIGNED: - return WasmIntBinaryOperation.LT_SIGNED; - case GE_UNSIGNED: - return WasmIntBinaryOperation.LT_UNSIGNED; - default: - return null; - } - } - - private static WasmFloatBinaryOperation negate(WasmFloatBinaryOperation op) { - switch (op) { - case EQ: - return WasmFloatBinaryOperation.NE; - case NE: - return WasmFloatBinaryOperation.EQ; - case LT: - return WasmFloatBinaryOperation.GE; - case LE: - return WasmFloatBinaryOperation.GT; - case GT: - return WasmFloatBinaryOperation.LE; - case GE: - return WasmFloatBinaryOperation.LT; - default: - return null; - } + protected WasmExpression generateThrowAIOOBE() { + return new WasmCall(context.functions().forStaticMethod(THROW_AIOOBE_METHOD)); } private WasmIntrinsicManager intrinsicManager = new WasmIntrinsicManager() { @@ -2212,12 +956,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public WasmFunctionRepository getFunctions() { - return context.functions; + return context.functions(); } @Override public WasmFunctionTypes getFunctionTypes() { - return context.functionTypes; + return context.functionTypes(); } @Override @@ -2251,13 +995,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } @Override - public int generateCallSiteId(TextLocation location) { - return WasmGenerationVisitor.this.generateCallSiteId(location); - } - - @Override - public WasmExpression generateRegisterCallSite(int callSite, TextLocation location) { - return WasmGenerationVisitor.this.generateRegisterCallSite(callSite, location); + public CallSiteIdentifier generateCallSiteId(TextLocation location) { + return generateCallSiteIdImpl(location); } @Override @@ -2265,14 +1004,4 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return context.getExceptionTag(); } }; - - private WasmExpression getReferenceToClass(WasmExpression instance) { - var classIndex = new WasmLoadInt32(4, instance, WasmInt32Subtype.INT32); - return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classIndex, - new WasmInt32Constant(3)); - } - - private WasmExpression genIsZero(WasmExpression value) { - return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); - } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index 80e0f20c3..960545661 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -57,7 +57,7 @@ public class WasmGenerator { MethodHolder method = cls.getMethod(methodReference.getDescriptor()); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); - WasmFunction function = context.functions.forMethod(method); + WasmFunction function = context.functions().forMethod(method); int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) { VariableNode variable = methodAst.getVariables().get(i); @@ -97,7 +97,7 @@ public class WasmGenerator { } public WasmFunction generateNative(MethodReader method) { - var function = context.functions.forMethod(method); + var function = context.functions().forMethod(method); var importedMethod = context.getImportedMethod(method.getReference()); if (importedMethod != null) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java new file mode 100644 index 000000000..9fa58b3cd --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationContext.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generate.common.methods; + +import org.teavm.backend.wasm.WasmFunctionRepository; +import org.teavm.backend.wasm.WasmFunctionTypes; +import org.teavm.backend.wasm.model.WasmTag; +import org.teavm.model.ClassReaderSource; + +public interface BaseWasmGenerationContext { + WasmFunctionRepository functions(); + + WasmFunctionTypes functionTypes(); + + WasmTag getExceptionTag(); + + ClassReaderSource classSource(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java new file mode 100644 index 000000000..2fb2c0f29 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -0,0 +1,1541 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generate.common.methods; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.teavm.ast.ArrayFromDataExpr; +import org.teavm.ast.ArrayType; +import org.teavm.ast.AssignmentStatement; +import org.teavm.ast.BinaryExpr; +import org.teavm.ast.BlockStatement; +import org.teavm.ast.BoundCheckExpr; +import org.teavm.ast.BreakStatement; +import org.teavm.ast.CastExpr; +import org.teavm.ast.ConditionalExpr; +import org.teavm.ast.ConditionalStatement; +import org.teavm.ast.ConstantExpr; +import org.teavm.ast.ContinueStatement; +import org.teavm.ast.Expr; +import org.teavm.ast.ExprVisitor; +import org.teavm.ast.GotoPartStatement; +import org.teavm.ast.IdentifiedStatement; +import org.teavm.ast.InitClassStatement; +import org.teavm.ast.InstanceOfExpr; +import org.teavm.ast.InvocationExpr; +import org.teavm.ast.InvocationType; +import org.teavm.ast.MonitorEnterStatement; +import org.teavm.ast.MonitorExitStatement; +import org.teavm.ast.NewArrayExpr; +import org.teavm.ast.NewExpr; +import org.teavm.ast.NewMultiArrayExpr; +import org.teavm.ast.OperationType; +import org.teavm.ast.PrimitiveCastExpr; +import org.teavm.ast.QualificationExpr; +import org.teavm.ast.ReturnStatement; +import org.teavm.ast.SequentialStatement; +import org.teavm.ast.Statement; +import org.teavm.ast.StatementVisitor; +import org.teavm.ast.SubscriptExpr; +import org.teavm.ast.SwitchStatement; +import org.teavm.ast.ThrowStatement; +import org.teavm.ast.TryCatchStatement; +import org.teavm.ast.UnaryExpr; +import org.teavm.ast.VariableExpr; +import org.teavm.ast.WhileStatement; +import org.teavm.backend.wasm.WasmRuntime; +import org.teavm.backend.wasm.generate.CachedExpression; +import org.teavm.backend.wasm.generate.ExpressionCache; +import org.teavm.backend.wasm.generate.TemporaryVariablePool; +import org.teavm.backend.wasm.generate.WasmGeneratorUtil; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmNumType; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmBranch; +import org.teavm.backend.wasm.model.expression.WasmBreak; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCatch; +import org.teavm.backend.wasm.model.expression.WasmConditional; +import org.teavm.backend.wasm.model.expression.WasmConversion; +import org.teavm.backend.wasm.model.expression.WasmDrop; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; +import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; +import org.teavm.backend.wasm.model.expression.WasmFloatBinary; +import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmFloatType; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt64Constant; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.backend.wasm.model.expression.WasmSwitch; +import org.teavm.backend.wasm.model.expression.WasmThrow; +import org.teavm.backend.wasm.model.expression.WasmTry; +import org.teavm.backend.wasm.render.WasmTypeInference; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.TextLocation; +import org.teavm.model.ValueType; + +public abstract class BaseWasmGenerationVisitor implements StatementVisitor, ExprVisitor { + private static final MethodReference MONITOR_ENTER_SYNC = new MethodReference(Object.class, + "monitorEnterSync", Object.class, void.class); + private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, + "monitorExitSync", Object.class, void.class); + private static final MethodReference MONITOR_ENTER = new MethodReference(Object.class, + "monitorEnter", Object.class, void.class); + private static final MethodReference MONITOR_EXIT = new MethodReference(Object.class, + "monitorExit", Object.class, void.class); + + private static final int SWITCH_TABLE_THRESHOLD = 256; + private BaseWasmGenerationContext context; + protected final WasmTypeInference typeInference; + protected final WasmFunction function; + private int firstVariable; + private IdentifiedStatement currentContinueTarget; + private IdentifiedStatement currentBreakTarget; + private Map breakTargets = new HashMap<>(); + private Map continueTargets = new HashMap<>(); + private Set usedBlocks = new HashSet<>(); + protected final TemporaryVariablePool tempVars; + private ExpressionCache exprCache; + + private boolean async; + protected WasmExpression result; + protected List resultConsumer; + + public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, WasmFunction function, + int firstVariable, boolean async) { + this.context = context; + this.function = function; + this.firstVariable = firstVariable; + tempVars = new TemporaryVariablePool(function); + exprCache = new ExpressionCache(tempVars); + typeInference = new WasmTypeInference(); + this.async = async; + } + + public void generate(Statement statement, List target) { + resultConsumer = target; + statement.acceptVisitor(this); + resultConsumer = null; + } + + protected void accept(Expr expr) { + expr.acceptVisitor(this); + } + + protected void accept(Statement statement) { + statement.acceptVisitor(this); + } + + @Override + public void visit(BinaryExpr expr) { + switch (expr.getOperation()) { + case ADD: + generateBinary(WasmIntBinaryOperation.ADD, WasmFloatBinaryOperation.ADD, expr); + break; + case SUBTRACT: + generateBinary(WasmIntBinaryOperation.SUB, WasmFloatBinaryOperation.SUB, expr); + break; + case MULTIPLY: + generateBinary(WasmIntBinaryOperation.MUL, WasmFloatBinaryOperation.MUL, expr); + break; + case DIVIDE: + generateBinary(WasmIntBinaryOperation.DIV_SIGNED, WasmFloatBinaryOperation.DIV, expr); + break; + case MODULO: { + switch (expr.getType()) { + case INT: + case LONG: + generateBinary(WasmIntBinaryOperation.REM_SIGNED, expr); + break; + default: + var type = convertType(expr.getType()); + var method = new MethodReference(WasmRuntime.class, "remainder", type, type, type); + var call = new WasmCall(context.functions().forStaticMethod(method)); + + accept(expr.getFirstOperand()); + call.getArguments().add(result); + + accept(expr.getSecondOperand()); + call.getArguments().add(result); + + call.setLocation(expr.getLocation()); + result = call; + break; + } + + break; + } + case BITWISE_AND: + generateBinary(WasmIntBinaryOperation.AND, expr); + break; + case BITWISE_OR: + generateBinary(WasmIntBinaryOperation.OR, expr); + break; + case BITWISE_XOR: + generateBinary(WasmIntBinaryOperation.XOR, expr); + break; + case EQUALS: + generateBinary(WasmIntBinaryOperation.EQ, WasmFloatBinaryOperation.EQ, expr); + break; + case NOT_EQUALS: + generateBinary(WasmIntBinaryOperation.NE, WasmFloatBinaryOperation.NE, expr); + break; + case GREATER: + generateBinary(WasmIntBinaryOperation.GT_SIGNED, WasmFloatBinaryOperation.GT, expr); + break; + case GREATER_OR_EQUALS: + generateBinary(WasmIntBinaryOperation.GE_SIGNED, WasmFloatBinaryOperation.GE, expr); + break; + case LESS: + generateBinary(WasmIntBinaryOperation.LT_SIGNED, WasmFloatBinaryOperation.LT, expr); + break; + case LESS_OR_EQUALS: + generateBinary(WasmIntBinaryOperation.LE_SIGNED, WasmFloatBinaryOperation.LE, expr); + break; + case LEFT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHL, expr); + break; + case RIGHT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHR_SIGNED, expr); + break; + case UNSIGNED_RIGHT_SHIFT: + generateBinary(WasmIntBinaryOperation.SHR_UNSIGNED, expr); + break; + case COMPARE: { + var type = convertType(expr.getType()); + var method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class); + var call = new WasmCall(context.functions().forStaticMethod(method)); + + accept(expr.getFirstOperand()); + call.getArguments().add(result); + + accept(expr.getSecondOperand()); + call.getArguments().add(result); + + call.setLocation(expr.getLocation()); + result = call; + break; + } + case AND: + generateAnd(expr); + break; + case OR: + generateOr(expr); + break; + } + } + + private void generateBinary(WasmIntBinaryOperation intOp, WasmFloatBinaryOperation floatOp, BinaryExpr expr) { + accept(expr.getFirstOperand()); + var first = result; + accept(expr.getSecondOperand()); + var second = result; + + if (expr.getType() == null) { + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + } else { + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + break; + case FLOAT: + result = new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second); + break; + case DOUBLE: + result = new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second); + break; + } + } + result.setLocation(expr.getLocation()); + } + + private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) { + accept(expr.getFirstOperand()); + var first = result; + accept(expr.getSecondOperand()); + var second = result; + + if (expr.getType() == OperationType.LONG) { + switch (expr.getOperation()) { + case LEFT_SHIFT: + case RIGHT_SHIFT: + case UNSIGNED_RIGHT_SHIFT: + second = new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, second); + break; + default: + break; + } + } + + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + break; + case FLOAT: + case DOUBLE: + throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType()); + } + + result.setLocation(expr.getLocation()); + } + + private Class convertType(OperationType type) { + switch (type) { + case INT: + return int.class; + case LONG: + return long.class; + case FLOAT: + return float.class; + case DOUBLE: + return double.class; + } + throw new AssertionError(type.toString()); + } + + private void generateAnd(BinaryExpr expr) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + + accept(expr.getFirstOperand()); + var branch = new WasmBranch(negate(result), block); + branch.setResult(new WasmInt32Constant(0)); + branch.setLocation(expr.getLocation()); + branch.getResult().setLocation(expr.getLocation()); + block.getBody().add(new WasmDrop(branch)); + + accept(expr.getSecondOperand()); + block.getBody().add(result); + + block.setLocation(expr.getLocation()); + + result = block; + } + + private void generateOr(BinaryExpr expr) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + + accept(expr.getFirstOperand()); + var branch = new WasmBranch(result, block); + branch.setResult(new WasmInt32Constant(1)); + branch.setLocation(expr.getLocation()); + branch.getResult().setLocation(expr.getLocation()); + block.getBody().add(new WasmDrop(branch)); + + accept(expr.getSecondOperand()); + block.getBody().add(result); + + block.setLocation(expr.getLocation()); + + result = block; + } + + @Override + public void visit(UnaryExpr expr) { + switch (expr.getOperation()) { + case INT_TO_BYTE: + accept(expr.getOperand()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(24)); + result.setLocation(expr.getLocation()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, + result, new WasmInt32Constant(24)); + result.setLocation(expr.getLocation()); + break; + case INT_TO_SHORT: + accept(expr.getOperand()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(16)); + result.setLocation(expr.getLocation()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, + result, new WasmInt32Constant(16)); + result.setLocation(expr.getLocation()); + break; + case INT_TO_CHAR: + accept(expr.getOperand()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + result, new WasmInt32Constant(16)); + result.setLocation(expr.getLocation()); + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, + result, new WasmInt32Constant(16)); + result.setLocation(expr.getLocation()); + break; + case LENGTH: + accept(expr.getOperand()); + result = generateArrayLength(result); + break; + case NOT: + accept(expr.getOperand()); + result = negate(result); + break; + case NEGATE: + accept(expr.getOperand()); + switch (expr.getType()) { + case INT: + result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, + new WasmInt32Constant(0), result); + result.setLocation(expr.getLocation()); + break; + case LONG: + result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB, + new WasmInt64Constant(0), result); + result.setLocation(expr.getLocation()); + break; + case FLOAT: + result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB, + new WasmFloat32Constant(0), result); + result.setLocation(expr.getLocation()); + break; + case DOUBLE: + result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB, + new WasmFloat64Constant(0), result); + result.setLocation(expr.getLocation()); + break; + } + break; + case NULL_CHECK: + if (!isManaged()) { + expr.getOperand().acceptVisitor(this); + } else { + result = nullCheck(expr.getOperand(), expr.getLocation()); + } + break; + } + } + + protected abstract boolean isManaged(); + + protected abstract boolean isManagedCall(MethodReference method); + + private WasmExpression nullCheck(Expr value, TextLocation location) { + var block = new WasmBlock(false); + block.setLocation(location); + + accept(value); + result.acceptVisitor(typeInference); + block.setType(typeInference.getResult()); + var cachedValue = exprCache.create(result, WasmType.INT32, location, block.getBody()); + + var check = new WasmBranch(cachedValue.expr(), block); + check.setResult(cachedValue.expr()); + block.getBody().add(new WasmDrop(check)); + + var callSiteId = generateCallSiteId(location); + callSiteId.generateRegister(block.getBody(), location); + block.getBody().add(generateThrowNPE()); + callSiteId.generateThrow(block.getBody(), location); + + cachedValue.release(); + return block; + } + + protected abstract WasmExpression generateThrowNPE(); + + protected abstract WasmExpression generateArrayLength(WasmExpression array); + + @Override + public void visit(AssignmentStatement statement) { + Expr left = statement.getLeftValue(); + if (left == null) { + if (statement.getRightValue() instanceof InvocationExpr) { + var invocation = (InvocationExpr) statement.getRightValue(); + invocation(invocation, resultConsumer, invocation.getMethod().getReturnType() != ValueType.VOID); + } else { + accept(statement.getRightValue()); + result.acceptVisitor(typeInference); + if (typeInference.getResult() != null) { + result = new WasmDrop(result); + result.setLocation(statement.getLocation()); + } + resultConsumer.add(result); + result = null; + } + } else if (left instanceof VariableExpr) { + var varExpr = (VariableExpr) left; + var local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable); + accept(statement.getRightValue()); + var setLocal = new WasmSetLocal(local, result); + setLocal.setLocation(statement.getLocation()); + resultConsumer.add(setLocal); + } else if (left instanceof QualificationExpr) { + var lhs = (QualificationExpr) left; + storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue(), statement.getLocation()); + } else if (left instanceof SubscriptExpr) { + var lhs = (SubscriptExpr) left; + storeArrayItem(lhs, statement.getRightValue()); + } else { + throw new UnsupportedOperationException("This expression is not supported yet"); + } + } + + protected abstract void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location); + + private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) { + leftValue.getArray().acceptVisitor(this); + var array = result; + leftValue.getIndex().acceptVisitor(this); + var index = result; + rightValue.acceptVisitor(this); + var value = result; + resultConsumer.add(storeArrayItem(array, index, value, leftValue.getType())); + } + + protected abstract WasmExpression storeArrayItem(WasmExpression array, WasmExpression index, WasmExpression value, + ArrayType type); + + @Override + public void visit(ConditionalExpr expr) { + accept(expr.getCondition()); + var conditional = new WasmConditional(forCondition(result)); + + accept(expr.getConsequent()); + conditional.getThenBlock().getBody().add(result); + result.acceptVisitor(typeInference); + var thenType = typeInference.getResult(); + conditional.getThenBlock().setType(thenType); + + accept(expr.getAlternative()); + conditional.getElseBlock().getBody().add(result); + result.acceptVisitor(typeInference); + var elseType = typeInference.getResult(); + conditional.getElseBlock().setType(elseType); + + assert thenType == elseType; + conditional.setType(thenType); + + result = conditional; + } + + @Override + public void visit(SequentialStatement statement) { + for (var part : statement.getSequence()) { + part.acceptVisitor(this); + } + } + + @Override + public void visit(ConstantExpr expr) { + if (expr.getValue() == null) { + result = new WasmInt32Constant(0); + } else if (expr.getValue() instanceof Integer) { + result = new WasmInt32Constant((Integer) expr.getValue()); + } else if (expr.getValue() instanceof Long) { + result = new WasmInt64Constant((Long) expr.getValue()); + } else if (expr.getValue() instanceof Float) { + result = new WasmFloat32Constant((Float) expr.getValue()); + } else if (expr.getValue() instanceof Double) { + result = new WasmFloat64Constant((Double) expr.getValue()); + } else if (expr.getValue() instanceof String) { + result = stringLiteral((String) expr.getValue()); + } else if (expr.getValue() instanceof ValueType) { + result = classLiteral((ValueType) expr.getValue()); + } else { + throw new IllegalArgumentException("Constant unsupported: " + expr.getValue()); + } + } + + protected abstract WasmExpression stringLiteral(String s); + + protected abstract WasmExpression classLiteral(ValueType type); + + @Override + public void visit(ConditionalStatement statement) { + accept(statement.getCondition()); + var conditional = new WasmConditional(forCondition(result)); + visitMany(statement.getConsequent(), conditional.getThenBlock().getBody()); + visitMany(statement.getAlternative(), conditional.getElseBlock().getBody()); + resultConsumer.add(conditional); + } + + @Override + public void visit(VariableExpr expr) { + result = new WasmGetLocal(localVar(expr.getIndex())); + } + + protected WasmLocal localVar(int index) { + return function.getLocalVariables().get(index - firstVariable); + } + + @Override + public void visit(SwitchStatement statement) { + int min = statement.getClauses().stream() + .flatMapToInt(clause -> Arrays.stream(clause.getConditions())) + .min().orElse(0); + int max = statement.getClauses().stream() + .flatMapToInt(clause -> Arrays.stream(clause.getConditions())) + .max().orElse(0); + + var defaultBlock = new WasmBlock(false); + breakTargets.put(statement, defaultBlock); + var oldBreakTarget = currentBreakTarget; + currentBreakTarget = statement; + + var wrapper = new WasmBlock(false); + accept(statement.getValue()); + var condition = result; + var initialWrapper = wrapper; + + var clauses = statement.getClauses(); + var targets = new WasmBlock[clauses.size()]; + for (int i = 0; i < clauses.size(); i++) { + var clause = clauses.get(i); + var caseBlock = new WasmBlock(false); + caseBlock.getBody().add(wrapper); + targets[i] = wrapper; + + visitMany(clause.getBody(), caseBlock.getBody()); + wrapper = caseBlock; + } + + defaultBlock.getBody().add(wrapper); + visitMany(statement.getDefaultClause(), defaultBlock.getBody()); + var defaultTarget = wrapper; + wrapper = defaultBlock; + + if ((long) max - (long) min >= SWITCH_TABLE_THRESHOLD) { + translateSwitchToBinarySearch(statement, condition, initialWrapper, defaultTarget, targets); + } else { + translateSwitchToWasmSwitch(statement, condition, initialWrapper, defaultTarget, targets, min, max); + } + + breakTargets.remove(statement); + currentBreakTarget = oldBreakTarget; + + resultConsumer.add(wrapper); + } + + private void translateSwitchToBinarySearch(SwitchStatement statement, WasmExpression condition, + WasmBlock initialWrapper, WasmBlock defaultTarget, WasmBlock[] targets) { + var entries = new ArrayList(); + for (int i = 0; i < statement.getClauses().size(); i++) { + var clause = statement.getClauses().get(i); + for (int label : clause.getConditions()) { + entries.add(new TableEntry(label, targets[i])); + } + } + entries.sort(Comparator.comparingInt(entry -> entry.label)); + + var cachedCondition = exprCache.create(condition, WasmType.INT32, statement.getValue().getLocation(), + initialWrapper.getBody()); + + generateBinarySearch(entries, 0, entries.size() - 1, initialWrapper, defaultTarget, cachedCondition); + + cachedCondition.release(); + } + + private void generateBinarySearch(List entries, int lower, int upper, WasmBlock consumer, + WasmBlock defaultTarget, CachedExpression testValue) { + if (upper - lower == 0) { + int label = entries.get(lower).label; + var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, + testValue.expr(), new WasmInt32Constant(label)); + var conditional = new WasmConditional(condition); + consumer.getBody().add(conditional); + + conditional.getThenBlock().getBody().add(new WasmBreak(entries.get(lower).target)); + conditional.getElseBlock().getBody().add(new WasmBreak(defaultTarget)); + } else if (upper - lower <= 0) { + consumer.getBody().add(new WasmBreak(defaultTarget)); + } else { + int mid = (upper + lower) / 2; + int label = entries.get(mid).label; + var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, + testValue.expr(), new WasmInt32Constant(label)); + var conditional = new WasmConditional(condition); + consumer.getBody().add(conditional); + + generateBinarySearch(entries, mid + 1, upper, conditional.getThenBlock(), defaultTarget, testValue); + generateBinarySearch(entries, lower, mid, conditional.getElseBlock(), defaultTarget, testValue); + } + } + + static class TableEntry { + final int label; + final WasmBlock target; + + TableEntry(int label, WasmBlock target) { + this.label = label; + this.target = target; + } + } + + private void translateSwitchToWasmSwitch(SwitchStatement statement, WasmExpression condition, + WasmBlock initialWrapper, WasmBlock defaultTarget, WasmBlock[] targets, int min, int max) { + if (min != 0) { + condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, condition, + new WasmInt32Constant(min)); + } + + var wasmSwitch = new WasmSwitch(condition, initialWrapper); + initialWrapper.getBody().add(wasmSwitch); + wasmSwitch.setDefaultTarget(defaultTarget); + + var expandedTargets = new WasmBlock[max - min + 1]; + for (int i = 0; i < statement.getClauses().size(); i++) { + var clause = statement.getClauses().get(i); + for (int label : clause.getConditions()) { + expandedTargets[label - min] = targets[i]; + } + } + + for (var target : expandedTargets) { + wasmSwitch.getTargets().add(target != null ? target : wasmSwitch.getDefaultTarget()); + } + } + + @Override + public void visit(WhileStatement statement) { + var wrapper = new WasmBlock(false); + var loop = new WasmBlock(true); + + continueTargets.put(statement, loop); + breakTargets.put(statement, wrapper); + var oldBreakTarget = currentBreakTarget; + var oldContinueTarget = currentContinueTarget; + currentBreakTarget = statement; + currentContinueTarget = statement; + + if (statement.getCondition() != null) { + accept(statement.getCondition()); + loop.getBody().add(new WasmBranch(negate(result), wrapper)); + usedBlocks.add(wrapper); + } + + visitMany(statement.getBody(), loop.getBody()); + loop.getBody().add(new WasmBreak(loop)); + + currentBreakTarget = oldBreakTarget; + currentContinueTarget = oldContinueTarget; + continueTargets.remove(statement); + breakTargets.remove(statement); + + if (usedBlocks.contains(wrapper)) { + wrapper.getBody().add(loop); + resultConsumer.add(wrapper); + } else { + resultConsumer.add(loop); + } + } + + protected WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { + var callSiteId = generateCallSiteId(expr.getLocation()); + if (needsCallSiteId() && isManagedCall(expr.getMethod())) { + var invocation = generateInvocation(expr, callSiteId); + var type = WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()); + + List targetList; + WasmBlock block; + if (resultConsumer != null) { + targetList = resultConsumer; + result = null; + block = null; + } else { + block = new WasmBlock(false); + block.setType(type); + block.setLocation(expr.getLocation()); + targetList = block.getBody(); + result = block; + } + + if (expr.getArguments().isEmpty()) { + callSiteId.generateRegister(targetList, expr.getLocation()); + } + + WasmLocal resultVar = null; + if (!willDrop) { + if (type != null) { + resultVar = tempVars.acquire(type); + var setLocal = new WasmSetLocal(resultVar, invocation); + setLocal.setLocation(expr.getLocation()); + targetList.add(setLocal); + } else { + targetList.add(invocation); + } + } else if (type == null) { + targetList.add(invocation); + } else { + var drop = new WasmDrop(invocation); + drop.setLocation(expr.getLocation()); + targetList.add(drop); + } + + callSiteId.checkHandlerId(targetList, expr.getLocation()); + if (resultVar != null) { + var getLocal = new WasmGetLocal(resultVar); + getLocal.setLocation(expr.getLocation()); + targetList.add(getLocal); + tempVars.release(resultVar); + } + + return block; + } else { + var resultExpr = generateInvocation(expr, null); + return trivialInvocation(resultExpr, resultConsumer, expr.getLocation(), willDrop); + } + } + + private WasmExpression trivialInvocation(WasmExpression resultExpr, List resultConsumer, + TextLocation location, boolean willDrop) { + if (resultConsumer != null) { + if (willDrop) { + var drop = new WasmDrop(resultExpr); + drop.setLocation(location); + resultConsumer.add(drop); + } else { + resultConsumer.add(resultExpr); + } + result = null; + return null; + } else { + return resultExpr; + } + } + + protected abstract CallSiteIdentifier generateCallSiteId(TextLocation location); + + protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) { + if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { + var method = context.classSource().resolve(expr.getMethod()); + var reference = method != null ? method.getReference() : expr.getMethod(); + var function = context.functions().forMethod(reference, expr.getType() == InvocationType.STATIC); + + var call = new WasmCall(function); + for (var argument : expr.getArguments()) { + accept(argument); + call.getArguments().add(result); + } + if (callSiteId != null) { + callSiteId.addToLastArg(call.getArguments()); + } + call.setLocation(expr.getLocation()); + return call; + } else if (expr.getType() == InvocationType.CONSTRUCTOR) { + var block = new WasmBlock(false); + var resultType = mapType(ValueType.object(expr.getMethod().getClassName())); + block.setType(resultType); + + var tmp = tempVars.acquire(resultType); + block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), + expr.getLocation()))); + + var function = context.functions().forInstanceMethod(expr.getMethod()); + var call = new WasmCall(function); + call.getArguments().add(new WasmGetLocal(tmp)); + for (var argument : expr.getArguments()) { + accept(argument); + call.getArguments().add(result); + } + if (callSiteId != null) { + callSiteId.addToLastArg(call.getArguments()); + } + block.getBody().add(call); + + block.getBody().add(new WasmGetLocal(tmp)); + tempVars.release(tmp); + + return block; + } else { + var reference = expr.getMethod(); + accept(expr.getArguments().get(0)); + var instance = result; + var block = new WasmBlock(false); + block.setType(WasmGeneratorUtil.mapType(reference.getReturnType())); + + var instanceVar = tempVars.acquire(WasmType.INT32); + block.getBody().add(new WasmSetLocal(instanceVar, instance)); + instance = new WasmGetLocal(instanceVar); + + var arguments = new ArrayList(); + arguments.add(instance); + for (int i = 1; i < expr.getArguments().size(); ++i) { + accept(expr.getArguments().get(i)); + arguments.add(result); + } + if (callSiteId != null) { + callSiteId.addToLastArg(arguments); + } + var call = generateVirtualCall(instanceVar, reference, arguments); + + block.getBody().add(call); + tempVars.release(instanceVar); + return block; + } + } + + protected abstract WasmExpression generateVirtualCall( + WasmLocal instance, + MethodReference method, + List arguments + ); + + + private boolean needsCallSiteId() { + return isManaged(); + } + + @Override + public void visit(BlockStatement statement) { + var block = new WasmBlock(false); + + if (statement.getId() != null) { + breakTargets.put(statement, block); + } + + visitMany(statement.getBody(), block.getBody()); + + if (statement.getId() != null) { + breakTargets.remove(statement); + } + + resultConsumer.add(block); + } + + + @Override + public void visit(BreakStatement statement) { + var target = statement.getTarget(); + if (target == null) { + target = currentBreakTarget; + } + var wasmTarget = breakTargets.get(target); + usedBlocks.add(wasmTarget); + var br = new WasmBreak(wasmTarget); + br.setLocation(statement.getLocation()); + resultConsumer.add(br); + } + + @Override + public void visit(ContinueStatement statement) { + var target = statement.getTarget(); + if (target == null) { + target = currentContinueTarget; + } + var wasmTarget = continueTargets.get(target); + usedBlocks.add(wasmTarget); + var br = new WasmBreak(wasmTarget); + br.setLocation(statement.getLocation()); + resultConsumer.add(br); + } + + @Override + public void visit(NewExpr expr) { + var block = new WasmBlock(false); + block.setLocation(expr.getLocation()); + block.setType(mapType(ValueType.object(expr.getConstructedClass()))); + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + block.getBody().add(allocateObject(expr.getConstructedClass(), expr.getLocation())); + if (block.getBody().size() == 1) { + result = block.getBody().get(0); + } else { + result = block; + } + } + + protected abstract WasmExpression allocateObject(String className, TextLocation location); + + @Override + public void visit(NewArrayExpr expr) { + var block = new WasmBlock(false); + block.setType(mapType(ValueType.arrayOf(expr.getType()))); + + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + + accept(expr.getLength()); + var length = result; + var call = allocateArray(expr.getType(), length, expr.getLocation()); + block.getBody().add(call); + + if (block.getBody().size() == 1) { + result = block.getBody().get(0); + } else { + result = block; + } + } + + protected abstract WasmExpression allocateArray(ValueType itemType, WasmExpression length, TextLocation location); + + protected abstract WasmExpression allocateMultiArray(List target, ValueType itemType, + List dimensions, TextLocation location); + + @Override + public void visit(ArrayFromDataExpr expr) { + var type = expr.getType(); + + var arrayType = ArrayType.OBJECT; + if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + case BYTE: + arrayType = ArrayType.BYTE; + break; + case SHORT: + arrayType = ArrayType.SHORT; + break; + case CHARACTER: + arrayType = ArrayType.CHAR; + break; + case INTEGER: + arrayType = ArrayType.INT; + break; + case LONG: + arrayType = ArrayType.LONG; + break; + case FLOAT: + arrayType = ArrayType.FLOAT; + break; + case DOUBLE: + arrayType = ArrayType.DOUBLE; + break; + } + } + + var wasmArrayType = mapType(ValueType.arrayOf(expr.getType())); + var block = new WasmBlock(false); + block.setType(wasmArrayType); + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + + var array = tempVars.acquire(wasmArrayType); + var allocation = allocateArray(expr.getType(), new WasmInt32Constant(expr.getData().size()), + expr.getLocation()); + block.getBody().add(new WasmSetLocal(array, allocation)); + + for (int i = 0; i < expr.getData().size(); ++i) { + expr.getData().get(i).acceptVisitor(this); + block.getBody().add(storeArrayItem(new WasmGetLocal(array), new WasmInt32Constant(i), result, arrayType)); + } + + block.getBody().add(new WasmGetLocal(array)); + block.setLocation(expr.getLocation()); + tempVars.release(array); + + result = block; + } + + @Override + public void visit(NewMultiArrayExpr expr) { + var block = new WasmBlock(false); + + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + + var wasmDimensions = new ArrayList(); + var arrayType = expr.getType(); + for (var dimension : expr.getDimensions()) { + accept(dimension); + wasmDimensions.add(result); + arrayType = ValueType.arrayOf(arrayType); + } + block.setType(mapType(arrayType)); + var call = allocateMultiArray(block.getBody(), expr.getType(), wasmDimensions, expr.getLocation()); + block.getBody().add(call); + + if (block.getBody().size() == 1) { + result = block.getBody().get(0); + } else { + result = block; + } + } + + @Override + public void visit(ReturnStatement statement) { + if (statement.getResult() != null) { + accept(statement.getResult()); + } else { + result = null; + } + var wasmStatement = new WasmReturn(result); + wasmStatement.setLocation(statement.getLocation()); + resultConsumer.add(wasmStatement); + } + + @Override + public void visit(InstanceOfExpr expr) { + accept(expr.getExpr()); + + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(expr.getLocation()); + + var cachedObject = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); + + var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); + ifNull.setResult(new WasmInt32Constant(0)); + block.getBody().add(new WasmDrop(ifNull)); + + block.getBody().add(generateInstanceOf(cachedObject.expr(), expr.getType())); + + cachedObject.release(); + + result = block; + } + + protected abstract WasmExpression generateInstanceOf(WasmExpression expression, ValueType type); + + @Override + public void visit(ThrowStatement statement) { + var callSiteId = generateCallSiteId(statement.getLocation()); + callSiteId.generateRegister(resultConsumer, statement.getLocation()); + + accept(statement.getException()); + var call = generateThrow(result); + call.setLocation(statement.getLocation()); + resultConsumer.add(call); + callSiteId.generateThrow(resultConsumer, statement.getLocation()); + } + + protected abstract WasmExpression generateThrow(WasmExpression expression); + + @Override + public void visit(CastExpr expr) { + var block = new WasmBlock(false); + var wasmTargetType = mapType(expr.getTarget()); + block.setType(wasmTargetType); + block.setLocation(expr.getLocation()); + + accept(expr.getValue()); + result.acceptVisitor(typeInference); + var wasmSourceType = typeInference.getResult(); + var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); + + var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block); + nullCheck.setResult(valueToCast.expr()); + block.getBody().add(new WasmDrop(nullCheck)); + + var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget()); + + var breakIfPassed = new WasmBranch(supertypeCall, block); + breakIfPassed.setResult(valueToCast.expr()); + block.getBody().add(new WasmDrop(breakIfPassed)); + + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + block.getBody().add(generateThrowCCE()); + callSiteId.generateThrow(block.getBody(), expr.getLocation()); + + valueToCast.release(); + result = generateCast(block, wasmTargetType); + } + + protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType); + + @Override + public void visit(InitClassStatement statement) { + if (needsClassInitializer(statement.getClassName())) { + var callSiteId = generateCallSiteId(statement.getLocation()); + callSiteId.generateRegister(resultConsumer, statement.getLocation()); + resultConsumer.add(generateClassInitializer(statement.getClassName(), statement.getLocation())); + callSiteId.checkHandlerId(resultConsumer, statement.getLocation()); + } + } + + protected abstract boolean needsClassInitializer(String clasName); + + protected abstract WasmExpression generateClassInitializer(String className, TextLocation location); + + @Override + public void visit(PrimitiveCastExpr expr) { + accept(expr.getValue()); + result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()), + WasmGeneratorUtil.mapType(expr.getTarget()), true, result); + result.setLocation(expr.getLocation()); + } + + @Override + public void visit(TryCatchStatement statement) { + var tryCatchStatements = new ArrayList(); + while (true) { + if (statement.getProtectedBody().size() != 1) { + break; + } + var next = statement.getProtectedBody().get(0); + if (!(next instanceof TryCatchStatement)) { + break; + } + tryCatchStatements.add(statement); + statement = (TryCatchStatement) next; + } + tryCatchStatements.add(statement); + + generateTry(tryCatchStatements, statement.getProtectedBody()); + } + + protected void generateTry(List tryCatchStatements, List protectedBody) { + var innerCatchBlock = new WasmBlock(false); + + var catchBlocks = new ArrayList(); + for (int i = 0; i < tryCatchStatements.size(); ++i) { + catchBlocks.add(new WasmBlock(false)); + } + var outerCatchBlock = catchBlocks.get(0); + + var tryBlock = new WasmTry(); + visitMany(protectedBody, tryBlock.getBody()); + if (!tryBlock.isTerminating()) { + tryBlock.getBody().add(new WasmBreak(outerCatchBlock)); + } + var catchClause = new WasmCatch(context.getExceptionTag()); + tryBlock.getCatches().add(catchClause); + innerCatchBlock.getBody().add(tryBlock); + + var throwableType = mapType(ValueType.object("java.lang.Throwable")); + var obj = exprCache.create(peekException(), throwableType, null, innerCatchBlock.getBody()); + var currentBlock = innerCatchBlock; + boolean catchesAll = false; + for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { + var tryCatch = tryCatchStatements.get(i); + var catchBlock = catchBlocks.get(i); + if (tryCatch.getExceptionType() != null && !tryCatch.getExceptionType().equals(Throwable.class.getName())) { + var exceptionType = ValueType.object(tryCatch.getExceptionType()); + var isMatched = generateInstanceOf(obj.expr(), exceptionType); + innerCatchBlock.getBody().add(new WasmBranch(isMatched, currentBlock)); + } else { + innerCatchBlock.getBody().add(new WasmBreak(currentBlock)); + catchesAll = true; + } + currentBlock = catchBlock; + } + if (!catchesAll) { + innerCatchBlock.getBody().add(new WasmThrow(context.getExceptionTag())); + } + obj.release(); + + currentBlock = innerCatchBlock; + for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { + var tryCatch = tryCatchStatements.get(i); + var catchBlock = catchBlocks.get(i); + catchBlock.getBody().add(currentBlock); + + var catchCall = catchException(); + var catchWrapper = tryCatch.getExceptionVariable() != null + ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) + : new WasmDrop(catchCall); + catchBlock.getBody().add(catchWrapper); + visitMany(tryCatch.getHandler(), catchBlock.getBody()); + if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) { + catchBlock.getBody().add(new WasmBreak(outerCatchBlock)); + } + currentBlock = catchBlock; + } + + resultConsumer.add(outerCatchBlock); + } + + protected abstract WasmExpression peekException(); + + protected abstract WasmExpression catchException(); + + private void visitMany(List statements, List target) { + var oldTarget = resultConsumer; + resultConsumer = target; + for (var part : statements) { + accept(part); + } + resultConsumer = oldTarget; + } + + @Override + public void visit(GotoPartStatement statement) { + } + + @Override + public void visit(MonitorEnterStatement statement) { + var call = new WasmCall(context.functions().forStaticMethod(async ? MONITOR_ENTER : MONITOR_ENTER_SYNC)); + call.setLocation(statement.getLocation()); + statement.getObjectRef().acceptVisitor(this); + call.getArguments().add(result); + + var callSiteId = generateCallSiteId(statement.getLocation()); + callSiteId.generateRegister(resultConsumer, statement.getLocation()); + resultConsumer.add(call); + callSiteId.checkHandlerId(resultConsumer, statement.getLocation()); + } + + @Override + public void visit(MonitorExitStatement statement) { + var call = new WasmCall(context.functions().forStaticMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); + call.setLocation(statement.getLocation()); + statement.getObjectRef().acceptVisitor(this); + call.getArguments().add(result); + + var callSiteId = generateCallSiteId(statement.getLocation()); + callSiteId.generateRegister(resultConsumer, statement.getLocation()); + resultConsumer.add(call); + callSiteId.checkHandlerId(resultConsumer, statement.getLocation()); + } + + @Override + public void visit(BoundCheckExpr expr) { + if (!isManaged()) { + expr.getIndex().acceptVisitor(this); + return; + } + + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(expr.getLocation()); + + accept(expr.getIndex()); + var index = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); + + if (expr.getArray() != null) { + var condBlock = block; + if (expr.isLower()) { + condBlock = new WasmBlock(false); + block.getBody().add(condBlock); + + var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + index.expr(), new WasmInt32Constant(0)); + var lowerBranch = new WasmBranch(lowerCond, condBlock); + condBlock.getBody().add(lowerBranch); + } + + accept(expr.getArray()); + var upperBound = generateArrayLength(result); + var upperCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + index.expr(), upperBound); + var upperBranch = new WasmBranch(upperCond, block); + upperBranch.setResult(index.expr()); + condBlock.getBody().add(new WasmDrop(upperBranch)); + } else if (expr.isLower()) { + var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, + index.expr(), new WasmInt32Constant(0)); + var lowerBranch = new WasmBranch(lowerCond, block); + lowerBranch.setResult(index.expr()); + block.getBody().add(new WasmDrop(lowerBranch)); + } + + var callSiteId = generateCallSiteId(expr.getLocation()); + callSiteId.generateRegister(block.getBody(), expr.getLocation()); + block.getBody().add(generateThrowAIOOBE()); + callSiteId.generateThrow(block.getBody(), expr.getLocation()); + + result = block; + } + + protected abstract WasmExpression generateThrowAIOOBE(); + + protected abstract WasmExpression generateThrowCCE(); + + private static WasmExpression negate(WasmExpression expr) { + if (expr instanceof WasmIntBinary) { + var binary = (WasmIntBinary) expr; + if (binary.getType() == WasmIntType.INT32 && binary.getOperation() == WasmIntBinaryOperation.XOR) { + if (isOne(binary.getFirst())) { + var result = binary.getSecond(); + if (result.getLocation() == null && expr.getLocation() != null) { + result.setLocation(expr.getLocation()); + } + return result; + } + if (isOne(binary.getSecond())) { + var result = binary.getFirst(); + if (result.getLocation() == null && expr.getLocation() != null) { + result.setLocation(expr.getLocation()); + } + return result; + } + } + + var negatedOp = negate(binary.getOperation()); + if (negatedOp != null) { + var result = new WasmIntBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); + result.setLocation(expr.getLocation()); + return result; + } + } else if (expr instanceof WasmFloatBinary) { + var binary = (WasmFloatBinary) expr; + var negatedOp = negate(binary.getOperation()); + if (negatedOp != null) { + var result = new WasmFloatBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); + result.setLocation(expr.getLocation()); + return result; + } + } + + var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, expr, new WasmInt32Constant(0)); + result.setLocation(expr.getLocation()); + return result; + } + + private static boolean isOne(WasmExpression expression) { + return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 1; + } + + private static boolean isZero(WasmExpression expression) { + return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 0; + } + + private boolean isBoolean(WasmExpression expression) { + if (expression instanceof WasmIntBinary) { + WasmIntBinary binary = (WasmIntBinary) expression; + switch (binary.getOperation()) { + case EQ: + case NE: + case LT_SIGNED: + case LT_UNSIGNED: + case LE_SIGNED: + case LE_UNSIGNED: + case GT_SIGNED: + case GT_UNSIGNED: + case GE_SIGNED: + case GE_UNSIGNED: + return true; + default: + return false; + } + } else if (expression instanceof WasmFloatBinary) { + WasmFloatBinary binary = (WasmFloatBinary) expression; + switch (binary.getOperation()) { + case EQ: + case NE: + case LT: + case LE: + case GT: + case GE: + return true; + default: + return false; + } + } + return false; + } + + private WasmExpression forCondition(WasmExpression expression) { + if (expression instanceof WasmIntBinary) { + WasmIntBinary binary = (WasmIntBinary) expression; + switch (binary.getOperation()) { + case EQ: + if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) { + return negate(binary.getSecond()); + } else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) { + return negate(binary.getFirst()); + } + break; + case NE: + if (isZero(binary.getFirst()) && isBoolean(binary.getSecond())) { + return binary.getSecond(); + } else if (isZero(binary.getSecond()) && isBoolean(binary.getFirst())) { + return binary.getFirst(); + } + break; + default: + break; + } + } + return expression; + } + + private static WasmIntBinaryOperation negate(WasmIntBinaryOperation op) { + switch (op) { + case EQ: + return WasmIntBinaryOperation.NE; + case NE: + return WasmIntBinaryOperation.EQ; + case LT_SIGNED: + return WasmIntBinaryOperation.GE_SIGNED; + case LT_UNSIGNED: + return WasmIntBinaryOperation.GE_UNSIGNED; + case LE_SIGNED: + return WasmIntBinaryOperation.GT_SIGNED; + case LE_UNSIGNED: + return WasmIntBinaryOperation.GT_UNSIGNED; + case GT_SIGNED: + return WasmIntBinaryOperation.LE_SIGNED; + case GT_UNSIGNED: + return WasmIntBinaryOperation.LE_UNSIGNED; + case GE_SIGNED: + return WasmIntBinaryOperation.LT_SIGNED; + case GE_UNSIGNED: + return WasmIntBinaryOperation.LT_UNSIGNED; + default: + return null; + } + } + + private static WasmFloatBinaryOperation negate(WasmFloatBinaryOperation op) { + switch (op) { + case EQ: + return WasmFloatBinaryOperation.NE; + case NE: + return WasmFloatBinaryOperation.EQ; + case LT: + return WasmFloatBinaryOperation.GE; + case LE: + return WasmFloatBinaryOperation.GT; + case GT: + return WasmFloatBinaryOperation.LE; + case GE: + return WasmFloatBinaryOperation.LT; + default: + return null; + } + } + + private WasmExpression genIsZero(WasmExpression value) { + return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); + } + + protected abstract WasmType mapType(ValueType type); + + protected abstract class CallSiteIdentifier { + public abstract void generateRegister(List consumer, TextLocation location); + + public final void addToLastArg(List args) { + if (args.isEmpty()) { + return; + } + var arg = args.get(args.size() - 1); + var block = new WasmBlock(false); + arg.acceptVisitor(typeInference); + block.setType(typeInference.getResult()); + block.setLocation(arg.getLocation()); + block.getBody().add(arg); + args.set(args.size() - 1, block); + generateRegister(block.getBody(), arg.getLocation()); + } + + public abstract void checkHandlerId(List target, TextLocation location); + + public abstract void generateThrow(List target, TextLocation location); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 037b850cf..f347fcf0a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -23,7 +23,7 @@ import java.util.Map; import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; -import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider; +import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmFunction; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java similarity index 94% rename from core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java rename to core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java index 195501fb8..933cf6d12 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/method/WasmGCFunctionProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCFunctionProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.wasm.generate.gc.method; +package org.teavm.backend.wasm.generate.gc.methods; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.model.MethodReference; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java index 68db5e618..cb65a62e8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/strings/WasmGCStringPool.java @@ -20,7 +20,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses; import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor; -import org.teavm.backend.wasm.generate.gc.method.WasmGCFunctionProvider; +import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.backend.wasm.model.WasmModule; diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index c182e3ed1..0820fa02f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.intrinsics; +import java.util.List; import org.teavm.ast.Expr; import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; @@ -56,9 +57,11 @@ public interface WasmIntrinsicManager { boolean isManagedMethodCall(MethodReference method); - int generateCallSiteId(TextLocation location); - - WasmExpression generateRegisterCallSite(int callSite, TextLocation location); + CallSiteIdentifier generateCallSiteId(TextLocation location); WasmTag getExceptionTag(); + + interface CallSiteIdentifier { + void generateRegister(List target, TextLocation location); + } }