diff --git a/annotations/it/src/it/externref-base/src/test/java/endive/test/ExternRefExampleTest.java b/annotations/it/src/it/externref-base/src/test/java/endive/test/ExternRefExampleTest.java index 49b57d3c2..9af91ff72 100644 --- a/annotations/it/src/it/externref-base/src/test/java/endive/test/ExternRefExampleTest.java +++ b/annotations/it/src/it/externref-base/src/test/java/endive/test/ExternRefExampleTest.java @@ -1,6 +1,7 @@ package endive.test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.Test; import run.endive.annotations.WasmModuleInterface; @@ -34,16 +35,13 @@ public TestModule_ModuleExports exports() { private Object sampleObj = null; - public long getHostObject() { + public Object getHostObject() { sampleObj = new Object(); - return 123; + return sampleObj; } - public int isNull(long arg0) { - if (arg0 != 123) { - throw new RuntimeException("unrecognized external ref"); - } - return (sampleObj == null) ? 1 : 0; + public int isNull(Object arg0) { + return (arg0 == null) ? 1 : 0; } } @@ -51,11 +49,11 @@ public int isNull(long arg0) { public void testExternRef() { var module = new TestModule(); - assertEquals(1, module.exports().isNull(123L)); + assertEquals(1, module.exports().isNull(null)); var hostObj = module.exports().getHostObject(); - assertEquals(123, hostObj); + assertNotNull(hostObj); - assertEquals(0, module.exports().isNull(123L)); + assertEquals(0, module.exports().isNull(hostObj)); } } diff --git a/codegen/src/main/java/run/endive/codegen/ModuleInterfaceCodegen.java b/codegen/src/main/java/run/endive/codegen/ModuleInterfaceCodegen.java index b9c41c04a..9af881a87 100644 --- a/codegen/src/main/java/run/endive/codegen/ModuleInterfaceCodegen.java +++ b/codegen/src/main/java/run/endive/codegen/ModuleInterfaceCodegen.java @@ -7,6 +7,8 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Modifier; import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.BodyDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.Parameter; import com.github.javaparser.ast.expr.ArrayAccessExpr; import com.github.javaparser.ast.expr.ArrayCreationExpr; @@ -26,6 +28,7 @@ import com.github.javaparser.ast.expr.VariableDeclarationExpr; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.ReturnStmt; +import com.github.javaparser.ast.stmt.ThrowStmt; import com.github.javaparser.ast.type.Type; import java.util.ArrayList; import java.util.HashMap; @@ -215,17 +218,16 @@ public Map generate() { : functionImports[export.index()].typeIndex(); var exportType = module.typeSection().getType(funcType); + boolean hasObjectRefs = + exportType.params().stream().anyMatch(ValType::isObjectRef) + || exportType.returns().stream().anyMatch(ValType::isObjectRef); + var argPrefix = "arg"; - var handleCallArguments = new ArrayList(); for (var pIdx = 0; pIdx < exportType.params().size(); pIdx++) { var param = exportType.params().get(pIdx); var argName = argPrefix + pIdx; var javaType = javaClassFromValueType(param); - // signature exportMethod.addParameter(javaType, argName); - // body invocation call arguments - var argExpr = new NameExpr(argName); - handleCallArguments.add(toLong(param, argExpr, exportsCu)); } var methodBody = exportMethod.createBody(); @@ -240,29 +242,134 @@ public Map generate() { new AssignExpr( exportFieldName, exportCall, AssignExpr.Operator.ASSIGN)); - var exportApplyHandle = - new MethodCallExpr( - exportFieldName, "apply", NodeList.nodeList(handleCallArguments)); - - if (exportType.returns().size() == 0) { - exportMethod.setType(void.class); - methodBody.addStatement(exportApplyHandle).addStatement(new ReturnStmt()); - } else if (exportType.returns().size() > 1) { - exportMethod.setType(long[].class); - methodBody.addStatement(new ReturnStmt(exportApplyHandle)); + if (hasObjectRefs) { + exportsCu.addImport("run.endive.runtime.CallResult"); + + // Build positional long[] args and Object[] refArgs arrays. + // Both have one slot per param; ref values in refArgs[i], + // non-ref values in args[i]. + int paramCount = exportType.params().size(); + Expression longArrayExpr; + Expression refArrayExpr; + if (paramCount == 0) { + longArrayExpr = + new ArrayCreationExpr( + parseType("long"), + NodeList.nodeList( + new ArrayCreationLevel(new IntegerLiteralExpr("0"))), + null); + refArrayExpr = + new ArrayCreationExpr( + parseType("Object"), + NodeList.nodeList( + new ArrayCreationLevel(new IntegerLiteralExpr("0"))), + null); + } else { + var longSlots = new ArrayList(); + var refSlots = new ArrayList(); + for (var pIdx = 0; pIdx < paramCount; pIdx++) { + var param = exportType.params().get(pIdx); + var argExpr = new NameExpr(argPrefix + pIdx); + if (param.isObjectRef()) { + longSlots.add(new IntegerLiteralExpr("0")); + refSlots.add(argExpr); + } else { + longSlots.add(toLong(param, argExpr, exportsCu)); + refSlots.add(new NullLiteralExpr()); + } + } + longArrayExpr = + new ArrayCreationExpr( + parseType("long"), + NodeList.nodeList(new ArrayCreationLevel()), + new ArrayInitializerExpr(NodeList.nodeList(longSlots))); + refArrayExpr = + new ArrayCreationExpr( + parseType("Object"), + NodeList.nodeList(new ArrayCreationLevel()), + new ArrayInitializerExpr(NodeList.nodeList(refSlots))); + } + + var applyWithRefsCall = + new MethodCallExpr( + exportFieldName, + "applyWithRefs", + NodeList.nodeList(longArrayExpr, refArrayExpr)); + + if (exportType.returns().size() == 0) { + exportMethod.setType(void.class); + methodBody.addStatement(applyWithRefsCall); + methodBody.addStatement(new ReturnStmt()); + } else { + // Declare CallResult cr = field.applyWithRefs(...) + methodBody.addStatement( + new AssignExpr( + new VariableDeclarationExpr(parseType("CallResult"), "cr"), + applyWithRefsCall, + AssignExpr.Operator.ASSIGN)); + + if (exportType.returns().size() == 1) { + var retType = exportType.returns().get(0); + exportMethod.setType(javaClassFromValueType(retType)); + if (retType.isObjectRef()) { + // return cr.refResult(0); + methodBody.addStatement( + new ReturnStmt( + new MethodCallExpr( + new NameExpr("cr"), + "refResult", + NodeList.nodeList( + new IntegerLiteralExpr("0"))))); + } else { + // return fromLong(cr.longResult(0)); + var longResult = + new MethodCallExpr( + new NameExpr("cr"), + "longResult", + NodeList.nodeList(new IntegerLiteralExpr("0"))); + methodBody.addStatement( + new ReturnStmt(fromLong(retType, longResult, exportsCu))); + } + } else { + // Multi-return with refs: return the CallResult directly + exportMethod.setType(parseType("CallResult")); + methodBody.addStatement(new ReturnStmt(new NameExpr("cr"))); + } + } } else { - exportMethod.setType(javaClassFromValueType(exportType.returns().get(0))); - methodBody.addStatement( - new AssignExpr( - new VariableDeclarationExpr(parseType("long"), "result"), - new ArrayAccessExpr(exportApplyHandle, new IntegerLiteralExpr("0")), - AssignExpr.Operator.ASSIGN)); - methodBody.addStatement( - new ReturnStmt( - fromLong( - exportType.returns().get(0), - new NameExpr("result"), - exportsCu))); + // No object refs - use the original apply() path + var handleCallArguments = new ArrayList(); + for (var pIdx = 0; pIdx < exportType.params().size(); pIdx++) { + var param = exportType.params().get(pIdx); + var argExpr = new NameExpr(argPrefix + pIdx); + handleCallArguments.add(toLong(param, argExpr, exportsCu)); + } + + var exportApplyHandle = + new MethodCallExpr( + exportFieldName, "apply", NodeList.nodeList(handleCallArguments)); + + if (exportType.returns().size() == 0) { + exportMethod.setType(void.class); + methodBody.addStatement(exportApplyHandle).addStatement(new ReturnStmt()); + } else if (exportType.returns().size() > 1) { + exportMethod.setType(long[].class); + methodBody.addStatement(new ReturnStmt(exportApplyHandle)); + } else { + exportMethod.setType(javaClassFromValueType(exportType.returns().get(0))); + methodBody.addStatement( + new AssignExpr( + new VariableDeclarationExpr(parseType("long"), "result"), + new ArrayAccessExpr( + exportApplyHandle, new IntegerLiteralExpr("0")), + AssignExpr.Operator.ASSIGN)); + methodBody.addStatement( + new ReturnStmt( + fromLong( + exportType.returns().get(0), + new NameExpr("result"), + exportsCu))); + } } } @@ -386,20 +493,32 @@ public Map generate() { .getType(((FunctionImport) importedFun).typeIndex()); importMethod.removeBody(); - // build lambda return - var functionBodyStatement = new BlockStmt(); + boolean importHasObjectRefs = + importType.params().stream().anyMatch(ValType::isObjectRef) + || importType.returns().stream() + .anyMatch(ValType::isObjectRef); + // Build the call to the user's Java method with proper + // argument extraction List parameters = new ArrayList<>(); for (int i = 0; i < importType.params().size(); i++) { var p = importType.params().get(i); - - parameters.add( - fromLong( - p, - new ArrayAccessExpr( - new NameExpr("args"), - new IntegerLiteralExpr(Integer.toString(i))), - importsCu)); + if (importHasObjectRefs && p.isObjectRef()) { + // Read from refArgs[i] + parameters.add( + new ArrayAccessExpr( + new NameExpr("refArgs"), + new IntegerLiteralExpr(Integer.toString(i)))); + } else { + parameters.add( + fromLong( + p, + new ArrayAccessExpr( + new NameExpr("args"), + new IntegerLiteralExpr( + Integer.toString(i))), + importsCu)); + } } var importApplyHandle = @@ -411,53 +530,211 @@ public Map generate() { importedFun.name(), false), NodeList.nodeList(parameters)); - if (importType.returns().size() == 0) { - importMethod.setType(void.class); - functionBodyStatement.addStatement(importApplyHandle); - functionBodyStatement.addStatement( - new ReturnStmt(new NullLiteralExpr())); - } else if (importType.returns().size() == 1) { - importMethod.setType( - javaClassFromValueType(importType.returns().get(0))); - functionBodyStatement.addStatement( - new ReturnStmt( - new ArrayCreationExpr( - parseType("long"), - NodeList.nodeList(new ArrayCreationLevel()), - new ArrayInitializerExpr( + Expression importedHostFunctionBinding; + + if (importHasObjectRefs) { + importsCu.addImport("run.endive.runtime.CallResult"); + importsCu.addImport("run.endive.runtime.WasmFunctionHandle"); + + // Set interface method return type + if (importType.returns().size() == 0) { + importMethod.setType(void.class); + } else if (importType.returns().size() == 1) { + importMethod.setType( + javaClassFromValueType(importType.returns().get(0))); + } else { + importMethod.setType(long[].class); + } + + // Build applyWithRefs body + var refsBody = new BlockStmt(); + if (importType.returns().size() == 0) { + refsBody.addStatement(importApplyHandle); + refsBody.addStatement( + new ReturnStmt( + new ObjectCreationExpr( + null, + parseClassOrInterfaceType("CallResult"), + NodeList.nodeList( + new NullLiteralExpr(), + new NullLiteralExpr())))); + } else { + if (importType.returns().size() == 1) { + var retType = importType.returns().get(0); + if (retType.isObjectRef()) { + // Object result = env().method(...) + // return new CallResult(null, new Object[]{ result }) + refsBody.addStatement( + new AssignExpr( + new VariableDeclarationExpr( + parseType("Object"), "result"), + importApplyHandle, + AssignExpr.Operator.ASSIGN)); + refsBody.addStatement( + new ReturnStmt( + new ObjectCreationExpr( + null, + parseClassOrInterfaceType( + "CallResult"), + NodeList.nodeList( + new NullLiteralExpr(), + new ArrayCreationExpr( + parseType("Object"), + NodeList.nodeList( + new ArrayCreationLevel()), + new ArrayInitializerExpr( + NodeList + .nodeList( + new NameExpr( + "result")))))))); + } else { + // non-ref single return in a function that has + // ref params + refsBody.addStatement( + new ReturnStmt( + new ObjectCreationExpr( + null, + parseClassOrInterfaceType( + "CallResult"), + NodeList.nodeList( + new ArrayCreationExpr( + parseType("long"), + NodeList.nodeList( + new ArrayCreationLevel()), + new ArrayInitializerExpr( + NodeList + .nodeList( + toLong( + retType, + importApplyHandle, + importsCu)))), + new NullLiteralExpr())))); + } + } else { + // Multi-return: not common with externref, but handle + refsBody.addStatement( + new ReturnStmt( + new ObjectCreationExpr( + null, + parseClassOrInterfaceType("CallResult"), NodeList.nodeList( - toLong( - importType - .returns() - .get(0), - importApplyHandle, - importsCu)))))); + new NullLiteralExpr(), + new NullLiteralExpr())))); + } + } + + // Build apply() that throws UnsupportedOperationException + var applyMethod = new MethodDeclaration(); + applyMethod.setPublic(true); + applyMethod.setType(long[].class); + applyMethod.setName("apply"); + applyMethod.addParameter( + new Parameter(parseType("Instance"), "instance")); + applyMethod.addParameter( + new Parameter(parseType("long"), "args").setVarArgs(true)); + var applyBody = new BlockStmt(); + applyBody.addStatement( + new ThrowStmt( + new ObjectCreationExpr( + null, + parseClassOrInterfaceType( + "UnsupportedOperationException"), + NodeList.nodeList( + new StringLiteralExpr( + "Use applyWithRefs for" + + " externref" + + " functions"))))); + applyMethod.setBody(applyBody); + + // Build applyWithRefs() override + var applyWithRefsMethod = new MethodDeclaration(); + applyWithRefsMethod.setPublic(true); + applyWithRefsMethod.setType(parseType("CallResult")); + applyWithRefsMethod.setName("applyWithRefs"); + applyWithRefsMethod.addParameter( + new Parameter(parseType("Instance"), "instance")); + applyWithRefsMethod.addParameter( + new Parameter(parseType("long[]"), "args")); + applyWithRefsMethod.addParameter( + new Parameter(parseType("Object[]"), "refArgs")); + applyWithRefsMethod.setBody(refsBody); + + // Create anonymous WasmFunctionHandle + var anonHandle = + new ObjectCreationExpr( + null, + parseClassOrInterfaceType("WasmFunctionHandle"), + NodeList.nodeList()); + NodeList> anonBody = new NodeList<>(); + anonBody.add(applyMethod); + anonBody.add(applyWithRefsMethod); + anonHandle.setAnonymousClassBody(anonBody); + + importedHostFunctionBinding = + new ObjectCreationExpr( + null, + parseClassOrInterfaceType("HostFunction"), + NodeList.nodeList( + new StringLiteralExpr(imprt.getKey()), + new StringLiteralExpr(importedFun.name()), + listOfValueTypes(importType.params()), + listOfValueTypes(importType.returns()), + anonHandle)); } else { - importMethod.setType(long[].class); - functionBodyStatement.addStatement(new ReturnStmt(importApplyHandle)); + // No object refs - use original lambda path + var functionBodyStatement = new BlockStmt(); + + if (importType.returns().size() == 0) { + importMethod.setType(void.class); + functionBodyStatement.addStatement(importApplyHandle); + functionBodyStatement.addStatement( + new ReturnStmt(new NullLiteralExpr())); + } else if (importType.returns().size() == 1) { + importMethod.setType( + javaClassFromValueType(importType.returns().get(0))); + functionBodyStatement.addStatement( + new ReturnStmt( + new ArrayCreationExpr( + parseType("long"), + NodeList.nodeList(new ArrayCreationLevel()), + new ArrayInitializerExpr( + NodeList.nodeList( + toLong( + importType + .returns() + .get(0), + importApplyHandle, + importsCu)))))); + } else { + importMethod.setType(long[].class); + functionBodyStatement.addStatement( + new ReturnStmt(importApplyHandle)); + } + + importedHostFunctionBinding = + new ObjectCreationExpr( + null, + parseClassOrInterfaceType("HostFunction"), + NodeList.nodeList( + new StringLiteralExpr(imprt.getKey()), + new StringLiteralExpr(importedFun.name()), + listOfValueTypes(importType.params()), + listOfValueTypes(importType.returns()), + // (Instance instance, long... args) -> null; + new LambdaExpr( + NodeList.nodeList( + new Parameter( + parseType("Instance"), + new SimpleName( + "instance")), + new Parameter( + parseType( + "long"), + "args") + .setVarArgs(true)), + functionBodyStatement))); } - var importedHostFunctionBinding = - new ObjectCreationExpr( - null, - parseClassOrInterfaceType("HostFunction"), - NodeList.nodeList( - new StringLiteralExpr(imprt.getKey()), - new StringLiteralExpr(importedFun.name()), - listOfValueTypes(importType.params()), - listOfValueTypes(importType.returns()), - // (Instance instance, long... args) -> null; - new LambdaExpr( - NodeList.nodeList( - new Parameter( - parseType("Instance"), - new SimpleName("instance")), - new Parameter( - parseType("long"), - "args") - .setVarArgs(true)), - functionBodyStatement))); - toImportValuesBody.addStatement( new MethodCallExpr( new NameExpr("imports"), @@ -516,8 +793,8 @@ static Class javaClassFromValueType(ValType type) { case ValType.ID.F64: return double.class; default: - if (ValType.TypeIdxCode.EXTERN.code() == type.typeIdx()) { - return long.class; + if (type.isObjectRef()) { + return Object.class; } throw new IllegalArgumentException( "javaClassFromValueType - Unsupported WASM type: " + type); @@ -539,9 +816,6 @@ static Expression toLong(ValType type, Expression nameExpr, CompilationUnit cu) return new MethodCallExpr( new NameExpr("Value"), "doubleToLong", new NodeList<>(nameExpr)); default: - if (ValType.TypeIdxCode.EXTERN.code() == type.typeIdx()) { - return nameExpr; - } throw new IllegalArgumentException("toLong - Unsupported WASM type: " + type); } } @@ -561,9 +835,6 @@ static Expression fromLong(ValType type, Expression nameExpr, CompilationUnit cu return new MethodCallExpr( new NameExpr("Value"), "longToDouble", new NodeList<>(nameExpr)); default: - if (ValType.TypeIdxCode.EXTERN.code() == type.typeIdx()) { - return nameExpr; - } throw new IllegalArgumentException("fromLong - Unsupported WASM type: " + type); } } diff --git a/compiler-tests/src/test/java/run/endive/testing/ArgsAdapter.java b/compiler-tests/src/test/java/run/endive/testing/ArgsAdapter.java index dae93f93d..b46820123 100644 --- a/compiler-tests/src/test/java/run/endive/testing/ArgsAdapter.java +++ b/compiler-tests/src/test/java/run/endive/testing/ArgsAdapter.java @@ -1,36 +1,58 @@ package run.endive.testing; -import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import run.endive.runtime.CallResult; +import run.endive.runtime.ExportFunction; public final class ArgsAdapter { - private final ArrayDeque stack; + private final List longs = new ArrayList<>(); + private final List refs = new ArrayList<>(); + private boolean hasRefs; - private ArgsAdapter() { - stack = new ArrayDeque<>(); - } + private ArgsAdapter() {} public static ArgsAdapter builder() { return new ArgsAdapter(); } - public long[] build() { - var result = new long[stack.size()]; - int i = stack.size() - 1; - while (!stack.isEmpty()) { - result[i--] = stack.pop(); - } - return result; + public ArgsAdapter add(long arg) { + longs.add(arg); + refs.add(null); + return this; } public ArgsAdapter add(long[] args) { for (var arg : args) { - stack.push(arg); + longs.add(arg); + refs.add(null); } return this; } - public ArgsAdapter add(long arg) { - stack.push(arg); + public ArgsAdapter addRef(Object ref) { + longs.add(0L); + refs.add(ref); + hasRefs = true; return this; } + + public long[] build() { + var result = new long[longs.size()]; + for (int i = 0; i < longs.size(); i++) { + result[i] = longs.get(i); + } + return result; + } + + public Object[] buildRefs() { + if (!hasRefs) { + return null; + } + return refs.toArray(); + } + + public CallResult applyWithRefs(ExportFunction func) { + return func.applyWithRefs(build(), buildRefs()); + } } diff --git a/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback-indirect.approved.txt b/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback-indirect.approved.txt index e378ab761..f049c0d2d 100644 --- a/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback-indirect.approved.txt +++ b/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback-indirect.approved.txt @@ -16,4 +16,4 @@ run.endive.testing.Test3MachineFuncGroup_0.func_3 run.endive.testing.Test3MachineFuncGroup_0.call_3 run.endive.testing.Test3MachineMachineCall.call run.endive.testing.Test3Machine.call -run.endive.runtime.Instance$Exports.lambda$function$0 \ No newline at end of file +run.endive.runtime.Instance$Exports$1.apply \ No newline at end of file diff --git a/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback.approved.txt b/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback.approved.txt index a68c3e51e..12c12b07e 100644 --- a/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback.approved.txt +++ b/compiler-tests/src/test/resources/run/endive/testing/InterpreterFallbackTest.testSilentInterpreterFallback.approved.txt @@ -16,4 +16,4 @@ run.endive.testing.Test3MachineFuncGroup_0.func_3 run.endive.testing.Test3MachineFuncGroup_0.call_3 run.endive.testing.Test3MachineMachineCall.call run.endive.testing.Test3Machine.call -run.endive.runtime.Instance$Exports.lambda$function$0 \ No newline at end of file +run.endive.runtime.Instance$Exports$1.apply \ No newline at end of file diff --git a/compiler-tests/src/test/resources/run/endive/testing/MethodTooLargeTest.testBigFunc.approved.txt b/compiler-tests/src/test/resources/run/endive/testing/MethodTooLargeTest.testBigFunc.approved.txt index 58922d89c..223ded271 100644 --- a/compiler-tests/src/test/resources/run/endive/testing/MethodTooLargeTest.testBigFunc.approved.txt +++ b/compiler-tests/src/test/resources/run/endive/testing/MethodTooLargeTest.testBigFunc.approved.txt @@ -3,6 +3,8 @@ private final Lrun/endive/runtime/Instance; instance private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine } + public final static INNERCLASS run/endive/runtime/ConstantEvaluators$ConstantResult run/endive/runtime/ConstantEvaluators ConstantResult + private final static Z memCopyWorkaround } diff --git a/compiler/src/main/java/run/endive/compiler/internal/Compiler.java b/compiler/src/main/java/run/endive/compiler/internal/Compiler.java index f3d3f4696..1b3144c49 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/Compiler.java +++ b/compiler/src/main/java/run/endive/compiler/internal/Compiler.java @@ -19,6 +19,7 @@ import static run.endive.compiler.internal.CompilerUtil.callIndirectMethodName; import static run.endive.compiler.internal.CompilerUtil.callIndirectMethodType; import static run.endive.compiler.internal.CompilerUtil.callMethodName; +import static run.endive.compiler.internal.CompilerUtil.callWithRefsMethodName; import static run.endive.compiler.internal.CompilerUtil.classNameForCallIndirect; import static run.endive.compiler.internal.CompilerUtil.classNameForDispatch; import static run.endive.compiler.internal.CompilerUtil.defaultValue; @@ -40,8 +41,11 @@ import static run.endive.compiler.internal.EmitterMap.EMITTERS; import static run.endive.compiler.internal.ShadedRefs.AOT_INTERPRETER_MACHINE_CALL; import static run.endive.compiler.internal.ShadedRefs.CALL_HOST_FUNCTION; +import static run.endive.compiler.internal.ShadedRefs.CALL_HOST_FUNCTION_WITH_REFS; import static run.endive.compiler.internal.ShadedRefs.CALL_INDIRECT; import static run.endive.compiler.internal.ShadedRefs.CALL_INDIRECT_ON_INTERPRETER; +import static run.endive.compiler.internal.ShadedRefs.CALL_INDIRECT_ON_INTERPRETER_WITH_REFS; +import static run.endive.compiler.internal.ShadedRefs.CALL_INDIRECT_WITH_REFS; import static run.endive.compiler.internal.ShadedRefs.CHECK_INTERRUPTION; import static run.endive.compiler.internal.ShadedRefs.INSTANCE_MEMORY; import static run.endive.compiler.internal.ShadedRefs.INSTANCE_TABLE; @@ -72,6 +76,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.commons.InstructionAdapter; import run.endive.compiler.InterpreterFallback; +import run.endive.runtime.CallResult; import run.endive.runtime.Instance; import run.endive.runtime.Machine; import run.endive.runtime.Memory; @@ -90,15 +95,35 @@ public final class Compiler { public static final String DEFAULT_CLASS_NAME = "run.endive.$gen.CompiledMachine"; private static final Type LONG_ARRAY_TYPE = Type.getType(long[].class); private static final Type INT_ARRAY_TYPE = Type.getType(int[].class); + private static final String CALL_RESULT_INTERNAL_NAME = Type.getInternalName(CallResult.class); private static final Type AOT_INTERPRETER_MACHINE_TYPE = Type.getType(CompilerInterpreterMachine.class); private static final Type INSTANCE_TYPE = Type.getType(Instance.class); - private static final MethodType CALL_METHOD_TYPE = - methodType(long[].class, Instance.class, Memory.class, long[].class); - - private static final MethodType MACHINE_CALL_METHOD_TYPE = - methodType(long[].class, Instance.class, Memory.class, int.class, long[].class); + private static final MethodType CALL_METHOD_TYPE_WITH_REFS = + methodType(long[].class, Instance.class, Memory.class, long[].class, Object[].class); + + private static final MethodType CALL_WITH_REFS_BRIDGE_METHOD_TYPE = + methodType( + CallResult.class, Instance.class, Memory.class, long[].class, Object[].class); + + private static final MethodType MACHINE_CALL_METHOD_TYPE_WITH_REFS = + methodType( + long[].class, + Instance.class, + Memory.class, + int.class, + long[].class, + Object[].class); + + private static final MethodType MACHINE_CALL_WITH_REFS_METHOD_TYPE = + methodType( + CallResult.class, + Instance.class, + Memory.class, + int.class, + long[].class, + Object[].class); // C2 JIT's HugeMethodLimit (default 8KB) — methods exceeding this get degraded optimization. // Dispatch chunks are sized to stay under this limit for full C2 compilation. @@ -129,6 +154,7 @@ public final class Compiler { private final boolean[] tailCallFunctions; private final boolean[] tailCallTypes; private final boolean moduleHasTailCalls; + private final boolean moduleHasObjectRefs; private boolean useBridgeClasses; private IntFunction callIndirectClassResolver; @@ -166,6 +192,9 @@ private Compiler( this.tailCallFunctions = analyzer.tailCallFunctions(); this.tailCallTypes = analyzer.tailCallTypes(); this.moduleHasTailCalls = analyzer.hasTailCalls(); + this.moduleHasObjectRefs = + this.functionTypes.stream() + .anyMatch(ft -> ft.hasObjectRefParams() || ft.hasObjectRefReturns()); this.maxFunctionsPerClass = maxFunctionsPerClass; } @@ -483,9 +512,19 @@ private Consumer emitFunctionGroup(int start, int end, String inte emitFunction( classWriter, callMethodName(funcId), - CALL_METHOD_TYPE, + CALL_METHOD_TYPE_WITH_REFS, true, asm -> compileCallFunction(funcId, type, asm)); + + // callWithRefs_xxx() bridges for functions with Object refs + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + emitFunction( + classWriter, + callWithRefsMethodName(funcId), + CALL_WITH_REFS_BRIDGE_METHOD_TYPE, + true, + asm -> compileCallWithRefsFunction(funcId, type, asm)); + } } } catch (MethodTooLargeException e) { throw handleMethodTooLarge(e, module); @@ -517,14 +556,12 @@ private byte[] compileClass() { null, null); - if (!interpretedFunctions.isEmpty()) { - classWriter.visitField( - Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, - "compilerInterpreterMachine", - getDescriptor(CompilerInterpreterMachine.class), - null, - null); - } + classWriter.visitField( + Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, + "compilerInterpreterMachine", + getDescriptor(CompilerInterpreterMachine.class), + null, + null); // constructor emitFunction( @@ -534,13 +571,38 @@ private byte[] compileClass() { false, asm -> compileConstructor(asm, internalClassName)); - // Machine.call() implementation + // Machine.call(int, long[]) implementation — 2-arg overload + // Locals: 0=this, 1=funcId, 2=args; store null refArgs at slot 3 emitFunction( classWriter, "call", methodType(long[].class, int.class, long[].class), false, - asm -> compileMachineCall(internalClassName, asm)); + asm -> { + asm.aconst(null); + asm.store(3, OBJECT_TYPE); // refArgs = null + compileMachineCall(internalClassName, asm, 3); + }); + + // Machine.call(int, long[], Object[]) implementation — 3-arg overload + // Locals: 0=this, 1=funcId, 2=args, 3=refArgs + emitFunction( + classWriter, + "call", + methodType(long[].class, int.class, long[].class, Object[].class), + false, + asm -> compileMachineCall(internalClassName, asm, 3)); + + // Machine.callWithRefs(int, long[], Object[]) implementation + // Dispatches directly to compiled callWithRefs_N bridges (or call_N for non-ref + // functions), bypassing the interpreter. Interpreted functions still fall back + // to compilerInterpreterMachine.callWithRefs(). + emitFunction( + classWriter, + "callWithRefs", + methodType(CallResult.class, int.class, long[].class, Object[].class), + false, + asm -> compileMachineCallWithRefs(internalClassName, asm)); // call_indirect_xxx() bridges for native CALL_INDIRECT // When using bridge classes, these methods are on separate classes @@ -576,13 +638,19 @@ private byte[] compileClass() { if (!seenValueMethods.add(methodName)) { continue; } + boolean hasGcRef = types.stream().anyMatch(ValType::isObjectRef); emitFunction( classWriter, methodName, valueMethodType(types), true, asm -> { - emitBoxArguments(asm, types); + if (hasGcRef) { + // Object[] return: box numerics as Long, keep refs as Object + emitBoxArgumentsAsObjectArray(asm, types); + } else { + emitBoxArguments(asm, types); + } asm.areturn(OBJECT_TYPE); }); } @@ -667,8 +735,11 @@ private void compileConstructor(InstructionAdapter asm, String internalClassName asm.load(1, OBJECT_TYPE); asm.putfield(internalClassName, "instance", getDescriptor(Instance.class)); + // Only create compilerInterpreterMachine when needed: + // - interpreted functions need it for fallback execution + // callWithRefs dispatches directly to compiled bridges now, + // so moduleHasObjectRefs alone no longer requires the interpreter if (!interpretedFunctions.isEmpty()) { - asm.load(0, OBJECT_TYPE); asm.anew(AOT_INTERPRETER_MACHINE_TYPE); asm.dup(); @@ -701,7 +772,12 @@ private void compileConstructor(InstructionAdapter asm, String internalClassName // implements the body of: // public long[] call(int var1, long[] var2) - private void compileMachineCall(String internalClassName, InstructionAdapter asm) { + // or + // public long[] call(int var1, long[] var2, Object[] var3) + // + // refArgsSlot: the local variable slot holding Object[] refArgs + private void compileMachineCall( + String internalClassName, InstructionAdapter asm, int refArgsSlot) { // handle modules with no functions if (functionTypes.isEmpty()) { @@ -743,13 +819,14 @@ private void compileMachineCall(String internalClassName, InstructionAdapter asm } if (moduleHasTailCalls) { - compileMachineCallWithTailCalls(internalClassName, asm); + compileMachineCallWithTailCalls(internalClassName, asm, refArgsSlot); } else { - compileMachineCallSimple(internalClassName, asm); + compileMachineCallSimple(internalClassName, asm, refArgsSlot); } } - private void compileMachineCallSimple(String internalClassName, InstructionAdapter asm) { + private void compileMachineCallSimple( + String internalClassName, InstructionAdapter asm, int refArgsSlot) { Label start = new Label(); Label end = new Label(); asm.visitTryCatchBlock(start, end, end, getInternalName(StackOverflowError.class)); @@ -761,11 +838,12 @@ private void compileMachineCallSimple(String internalClassName, InstructionAdapt emitInvokeVirtual(asm, INSTANCE_MEMORY); asm.load(1, INT_TYPE); asm.load(2, OBJECT_TYPE); + asm.load(refArgsSlot, OBJECT_TYPE); // refArgs (null for 2-arg, actual for 3-arg) asm.invokestatic( internalClassName + "MachineCall", "call", - MACHINE_CALL_METHOD_TYPE.toMethodDescriptorString(), + MACHINE_CALL_METHOD_TYPE_WITH_REFS.toMethodDescriptorString(), false); asm.areturn(OBJECT_TYPE); @@ -775,6 +853,105 @@ private void compileMachineCallSimple(String internalClassName, InstructionAdapt asm.athrow(); } + // implements the body of: + // public CallResult callWithRefs(int funcId, long[] args, Object[] refArgs) + // Locals: 0=this, 1=funcId, 2=args, 3=refArgs + private void compileMachineCallWithRefs(String internalClassName, InstructionAdapter asm) { + + // handle modules with no functions + if (functionTypes.isEmpty()) { + asm.load(1, INT_TYPE); + emitInvokeStatic(asm, THROW_UNKNOWN_FUNCTION); + asm.athrow(); + return; + } + + // Interpreted functions: delegate to compilerInterpreterMachine.callWithRefs() + if (!interpretedFunctions.isEmpty()) { + Label invalid = new Label(); + int[] keys = interpretedFunctions.stream().mapToInt(x -> x).sorted().toArray(); + Label[] labels = + interpretedFunctions.stream().map(x -> new Label()).toArray(Label[]::new); + asm.load(1, INT_TYPE); + asm.lookupswitch(invalid, keys, labels); + for (int i = 0; i < interpretedFunctions.size(); i++) { + asm.mark(labels[i]); + asm.load(0, OBJECT_TYPE); + asm.getfield( + internalClassName, + "compilerInterpreterMachine", + getDescriptor(CompilerInterpreterMachine.class)); + asm.load(1, INT_TYPE); + asm.load(2, OBJECT_TYPE); + asm.load(3, OBJECT_TYPE); + asm.invokevirtual( + AOT_INTERPRETER_MACHINE_TYPE.getInternalName(), + "callWithRefs", + Type.getMethodDescriptor( + Type.getType(CallResult.class), + INT_TYPE, + LONG_ARRAY_TYPE, + Type.getType(Object[].class)), + false); + asm.areturn(OBJECT_TYPE); + } + asm.mark(invalid); + } + + if (moduleHasObjectRefs) { + // Module has Object refs: dispatch through MachineCall.callWithRefs() + // which routes to callWithRefs_N for ref functions and wraps call_N for non-ref + Label start = new Label(); + Label end = new Label(); + asm.visitTryCatchBlock(start, end, end, getInternalName(StackOverflowError.class)); + asm.mark(start); + + asm.load(0, OBJECT_TYPE); + asm.getfield(internalClassName, "instance", getDescriptor(Instance.class)); + asm.dup(); + emitInvokeVirtual(asm, INSTANCE_MEMORY); + asm.load(1, INT_TYPE); + asm.load(2, OBJECT_TYPE); + asm.load(3, OBJECT_TYPE); + + asm.invokestatic( + internalClassName + "MachineCall", + "callWithRefs", + MACHINE_CALL_WITH_REFS_METHOD_TYPE.toMethodDescriptorString(), + false); + asm.areturn(OBJECT_TYPE); + + asm.mark(end); + emitInvokeStatic(asm, THROW_CALL_STACK_EXHAUSTED); + asm.athrow(); + } else { + // No Object refs in module: wrap call() result in CallResult(longs, null) + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.load(0, OBJECT_TYPE); + asm.load(1, INT_TYPE); + asm.load(2, OBJECT_TYPE); + asm.load(3, OBJECT_TYPE); + asm.invokevirtual( + internalClassName, + "call", + Type.getMethodDescriptor( + LONG_ARRAY_TYPE, + INT_TYPE, + LONG_ARRAY_TYPE, + Type.getType(Object[].class)), + false); + asm.aconst(null); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + asm.areturn(OBJECT_TYPE); + } + } + // Generates a trampoline loop equivalent to: // // Instance inst = this.instance; @@ -789,7 +966,8 @@ private void compileMachineCallSimple(String internalClassName, InstructionAdapt // throw throwCallStackExhausted(e); // } // } - private void compileMachineCallWithTailCalls(String internalClassName, InstructionAdapter asm) { + private void compileMachineCallWithTailCalls( + String internalClassName, InstructionAdapter asm, int refArgsSlot) { Label start = new Label(); Label end = new Label(); Label soeCatch = new Label(); @@ -798,24 +976,27 @@ private void compileMachineCallWithTailCalls(String internalClassName, Instructi asm.load(0, OBJECT_TYPE); asm.getfield(internalClassName, "instance", getDescriptor(Instance.class)); - asm.store(3, OBJECT_TYPE); + // instance stored at local 4 for 2-arg, local 5 for 3-arg + int instanceLocal = refArgsSlot + 1; + asm.store(instanceLocal, OBJECT_TYPE); Label loopStart = new Label(); asm.mark(loopStart); - asm.load(3, OBJECT_TYPE); + asm.load(instanceLocal, OBJECT_TYPE); asm.dup(); emitInvokeVirtual(asm, INSTANCE_MEMORY); asm.load(1, INT_TYPE); asm.load(2, OBJECT_TYPE); + asm.load(refArgsSlot, OBJECT_TYPE); // refArgs asm.invokestatic( internalClassName + "MachineCall", "call", - MACHINE_CALL_METHOD_TYPE.toMethodDescriptorString(), + MACHINE_CALL_METHOD_TYPE_WITH_REFS.toMethodDescriptorString(), false); - asm.load(3, OBJECT_TYPE); + asm.load(instanceLocal, OBJECT_TYPE); asm.invokevirtual( getInternalName(Instance.class), "isTailCallPending", @@ -825,21 +1006,29 @@ private void compileMachineCallWithTailCalls(String internalClassName, Instructi asm.ifeq(returnResult); asm.pop(); - asm.load(3, OBJECT_TYPE); + asm.load(instanceLocal, OBJECT_TYPE); asm.invokevirtual( getInternalName(Instance.class), "tailCallFuncId", getMethodDescriptor(INT_TYPE), false); asm.store(1, INT_TYPE); - asm.load(3, OBJECT_TYPE); + asm.load(instanceLocal, OBJECT_TYPE); asm.invokevirtual( getInternalName(Instance.class), "tailCallArgs", getMethodDescriptor(getType(long[].class)), false); asm.store(2, OBJECT_TYPE); - asm.load(3, OBJECT_TYPE); + // Load refArgs from tail call pending + asm.load(instanceLocal, OBJECT_TYPE); + asm.invokevirtual( + getInternalName(Instance.class), + "tailCallRefArgs", + getMethodDescriptor(getType(Object[].class)), + false); + asm.store(refArgsSlot, OBJECT_TYPE); + asm.load(instanceLocal, OBJECT_TYPE); asm.invokevirtual( getInternalName(Instance.class), "clearTailCall", @@ -900,14 +1089,31 @@ private void compileMachineCallClass() { emitFunction( cw, callDispatchMethodName(start), - MACHINE_CALL_METHOD_TYPE, + MACHINE_CALL_METHOD_TYPE_WITH_REFS, true, asm -> compileMachineCallInvoke( asm, start, end)))); callMethod = compileMachineCallDispatch(maxMachineCallMethods); } - emitFunction(classWriter, "call", MACHINE_CALL_METHOD_TYPE, true, callMethod); + emitFunction(classWriter, "call", MACHINE_CALL_METHOD_TYPE_WITH_REFS, true, callMethod); + + // static implementation for Machine.callWithRefs() — only when module has Object refs + if (moduleHasObjectRefs) { + Consumer callWithRefsMethod; + if (functionTypes.size() < MAX_DISPATCH_METHODS) { + callWithRefsMethod = + asm -> compileMachineCallWithRefsInvoke(asm, 0, functionTypes.size()); + } else { + callWithRefsMethod = compileMachineCallWithRefsDispatch(MAX_DISPATCH_METHODS << 2); + } + emitFunction( + classWriter, + "callWithRefs", + MACHINE_CALL_WITH_REFS_METHOD_TYPE, + true, + callWithRefsMethod); + } collector.put(machineCallClassName, binaryWriter.toByteArray()); } @@ -931,11 +1137,12 @@ private void compileExtraClass( private Consumer compileMachineCallDispatch(int maxMachineCallMethods) { return (asm) -> { - // load arguments + // load arguments: instance, memory, funcId, args, refArgs asm.load(0, OBJECT_TYPE); asm.load(1, OBJECT_TYPE); asm.load(2, INT_TYPE); asm.load(3, OBJECT_TYPE); + asm.load(4, OBJECT_TYPE); // refArgs assert Integer.bitCount(maxMachineCallMethods) == 1; // power of two int shift = Integer.numberOfTrailingZeros(maxMachineCallMethods); @@ -951,13 +1158,13 @@ private Consumer compileMachineCallDispatch(int maxMachineCa asm.shr(INT_TYPE); asm.tableswitch(0, labels.length - 1, labels[0], labels); - // return call_dispatch_xxx(instance, memory, funcId, args); + // return call_dispatch_xxx(instance, memory, funcId, args, refArgs); for (int i = 0; i < labels.length; i++) { asm.mark(labels[i]); asm.invokestatic( internalClassName(classNameForDispatch(className, i << shift)), callDispatchMethodName(i << shift), - MACHINE_CALL_METHOD_TYPE.toMethodDescriptorString(), + MACHINE_CALL_METHOD_TYPE_WITH_REFS.toMethodDescriptorString(), false); asm.areturn(OBJECT_TYPE); } @@ -965,10 +1172,11 @@ private Consumer compileMachineCallDispatch(int maxMachineCa } private void compileMachineCallInvoke(InstructionAdapter asm, int start, int end) { - // load arguments + // load arguments: instance, memory, args, refArgs asm.load(0, OBJECT_TYPE); asm.load(1, OBJECT_TYPE); asm.load(3, OBJECT_TYPE); + asm.load(4, OBJECT_TYPE); // refArgs // switch (funcId) Label defaultLabel = new Label(); @@ -982,25 +1190,152 @@ private void compileMachineCallInvoke(InstructionAdapter asm, int start, int end asm.load(2, INT_TYPE); asm.tableswitch(start, end - 1, defaultLabel, labels); - // return call_xxx(instance, memory, args); + // return call_xxx(instance, memory, args, refArgs); for (int id = max(start, functionImports); id < end; id++) { asm.mark(labels[id - start]); asm.invokestatic( internalClassName(classNameForFuncGroup(className, id)), callMethodName(id), - CALL_METHOD_TYPE.toMethodDescriptorString(), + CALL_METHOD_TYPE_WITH_REFS.toMethodDescriptorString(), false); asm.areturn(OBJECT_TYPE); } - // return instance.callHostFunction(funcId, args); + // return instance.callHostFunctionWithRefs(funcId, args, refArgs) + // We always use WithRefs here since the generic dispatch doesn't know + // at emit time whether the function type has Object refs. + // CallResult.longs() is returned to the Machine.call() caller. if (functionImports > start) { asm.mark(hostLabel); + // stack: instance, memory, args, refArgs + asm.pop(); // pop refArgs -> save it + // Actually we need the args: instance, memory, args, refArgs are local 0-4 + // Pop all stacked values (instance, memory, args, refArgs were loaded) asm.pop(); asm.pop(); + asm.pop(); + // Use locals directly + asm.load(0, OBJECT_TYPE); // instance + asm.load(2, INT_TYPE); // funcId + asm.load(3, OBJECT_TYPE); // args + asm.load(4, OBJECT_TYPE); // refArgs + emitInvokeStatic(asm, CALL_HOST_FUNCTION_WITH_REFS); + // CallResult on stack, extract longs() for the long[] return + asm.invokevirtual(CALL_RESULT_INTERNAL_NAME, "longs", "()[J", false); + asm.areturn(OBJECT_TYPE); + } + + // throw new InvalidException("unknown function " + funcId); + asm.mark(defaultLabel); + asm.load(2, INT_TYPE); + emitInvokeStatic(asm, THROW_UNKNOWN_FUNCTION); + asm.athrow(); + } + + private Consumer compileMachineCallWithRefsDispatch( + int maxMachineCallMethods) { + return (asm) -> { + // load arguments: instance, memory, funcId, args, refArgs + asm.load(0, OBJECT_TYPE); + asm.load(1, OBJECT_TYPE); asm.load(2, INT_TYPE); asm.load(3, OBJECT_TYPE); - emitInvokeStatic(asm, CALL_HOST_FUNCTION); + asm.load(4, OBJECT_TYPE); + + assert Integer.bitCount(maxMachineCallMethods) == 1; // power of two + int shift = Integer.numberOfTrailingZeros(maxMachineCallMethods); + + Label[] labels = new Label[((functionTypes.size() - 1) >> shift) + 1]; + for (int i = 0; i < labels.length; i++) { + labels[i] = new Label(); + } + + asm.load(2, INT_TYPE); + asm.iconst(shift); + asm.shr(INT_TYPE); + asm.tableswitch(0, labels.length - 1, labels[0], labels); + + // For large modules, delegate to the same single invoke method + // since all the logic (host vs compiled vs callWithRefs_N) is there + for (int i = 0; i < labels.length; i++) { + asm.mark(labels[i]); + int chunkStart = i << shift; + int chunkEnd = min(chunkStart + maxMachineCallMethods, functionTypes.size()); + compileMachineCallWithRefsInvoke(asm, chunkStart, chunkEnd); + // compileMachineCallWithRefsInvoke ends with areturn, no fall-through + } + }; + } + + private void compileMachineCallWithRefsInvoke(InstructionAdapter asm, int start, int end) { + + // load arguments: instance, memory, args, refArgs + asm.load(0, OBJECT_TYPE); + asm.load(1, OBJECT_TYPE); + asm.load(3, OBJECT_TYPE); + asm.load(4, OBJECT_TYPE); + + // switch (funcId) + Label defaultLabel = new Label(); + Label hostLabel = new Label(); + Label[] labels = new Label[end - start]; + + for (int id = start; id < end; id++) { + labels[id - start] = (id < functionImports) ? hostLabel : new Label(); + } + + asm.load(2, INT_TYPE); + asm.tableswitch(start, end - 1, defaultLabel, labels); + + // For each compiled function: + // - If it has Object refs: call callWithRefs_N -> returns CallResult directly + // - Otherwise: call call_N -> wrap result in CallResult(longs, null) + for (int id = max(start, functionImports); id < end; id++) { + asm.mark(labels[id - start]); + var type = functionTypes.get(id); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + // return callWithRefs_N(instance, memory, args, refArgs) + asm.invokestatic( + internalClassName(classNameForFuncGroup(className, id)), + callWithRefsMethodName(id), + CALL_WITH_REFS_BRIDGE_METHOD_TYPE.toMethodDescriptorString(), + false); + } else { + // return new CallResult(call_N(instance, memory, args, refArgs), null) + asm.invokestatic( + internalClassName(classNameForFuncGroup(className, id)), + callMethodName(id), + CALL_METHOD_TYPE_WITH_REFS.toMethodDescriptorString(), + false); + // Wrap long[] in CallResult + int resultSlot = 5; + asm.store(resultSlot, OBJECT_TYPE); + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.load(resultSlot, OBJECT_TYPE); + asm.aconst(null); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } + asm.areturn(OBJECT_TYPE); + } + + // Host function: return callHostFunctionWithRefs(instance, funcId, args, refArgs) + if (functionImports > start) { + asm.mark(hostLabel); + asm.pop(); + asm.pop(); + asm.pop(); + asm.pop(); + asm.load(0, OBJECT_TYPE); // instance + asm.load(2, INT_TYPE); // funcId + asm.load(3, OBJECT_TYPE); // args + asm.load(4, OBJECT_TYPE); // refArgs + emitInvokeStatic(asm, CALL_HOST_FUNCTION_WITH_REFS); asm.areturn(OBJECT_TYPE); } @@ -1012,19 +1347,36 @@ private void compileMachineCallInvoke(InstructionAdapter asm, int start, int end } // implements the body of: - // public static long[] call_xxx(Memory memory, Instance instance, long[] args) + // public static long[] call_xxx(Instance instance, Memory memory, long[] args, Object[] + // refArgs) private void compileCallFunction(int funcId, FunctionType type, InstructionAdapter asm) { if (hasTooManyParameters(type)) { asm.load(2, LONG_ARRAY_TYPE); } else { - // unbox the arguments from long[] + // unbox the arguments from long[] (and Object[] refArgs for GC refs) for (int i = 0; i < type.params().size(); i++) { var param = type.params().get(i); - asm.load(2, OBJECT_TYPE); - asm.iconst(i); - asm.aload(LONG_TYPE); - emitLongToJvm(asm, param); + if (param.isObjectRef()) { + // GC ref args: load from Object[] refArgs (null-safe) + asm.load(3, OBJECT_TYPE); // refArgs + Label hasRefArgs = new Label(); + Label refDone = new Label(); + asm.dup(); + asm.ifnonnull(hasRefArgs); + asm.pop(); + asm.aconst(null); // default null for missing refArgs + asm.goTo(refDone); + asm.mark(hasRefArgs); + asm.iconst(i); + asm.aload(OBJECT_TYPE); + asm.mark(refDone); + } else { + asm.load(2, OBJECT_TYPE); + asm.iconst(i); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, param); + } } } @@ -1038,16 +1390,215 @@ private void compileCallFunction(int funcId, FunctionType type, InstructionAdapt Class returnType = jvmReturnType(type); if (returnType == void.class) { asm.aconst(null); - } else if (returnType != long[].class) { + } else if (returnType == Object.class) { + // GC ref return: discard the Object result and return a dummy long[]{0}. + // GC ref results should be accessed via callWithRefs() which goes through the + // interpreter path and handles Object results natively. + asm.pop(); + asm.iconst(1); + asm.newarray(LONG_TYPE); + } else if (returnType != long[].class && returnType != Object[].class) { emitJvmToLong(asm, type.returns().get(0)); - asm.store(3, LONG_TYPE); + asm.store(4, LONG_TYPE); asm.iconst(1); asm.newarray(LONG_TYPE); asm.dup(); asm.iconst(0); - asm.load(3, LONG_TYPE); + asm.load(4, LONG_TYPE); asm.astore(LONG_TYPE); } + if (returnType == Object[].class) { + // Multi-value with GC refs: the internal function returns Object[], + // but the bridge must return long[]. Convert: longs for numerics, 0L for refs. + int objArraySlot = 4; + asm.store(objArraySlot, OBJECT_TYPE); // save Object[] + asm.iconst(type.returns().size()); + asm.newarray(LONG_TYPE); // create long[] + for (int i = 0; i < type.returns().size(); i++) { + var retType = type.returns().get(i); + asm.dup(); + asm.iconst(i); + if (retType.isObjectRef()) { + asm.lconst(0L); + } else { + asm.load(objArraySlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(OBJECT_TYPE); // get from Object[] + asm.checkcast(Type.getType(Long.class)); + asm.invokevirtual("java/lang/Long", "longValue", "()J", false); + } + asm.astore(LONG_TYPE); + } + } + // For long[] multi-value returns, leave as-is + asm.areturn(OBJECT_TYPE); + } + + // implements the body of: + // public static CallResult callWithRefs_xxx(Instance instance, Memory memory, long[] args, + // Object[] refArgs) + // Same parameter unboxing as call_xxx, but returns CallResult with split long[]+Object[] + private void compileCallWithRefsFunction( + int funcId, FunctionType type, InstructionAdapter asm) { + + if (hasTooManyParameters(type)) { + asm.load(2, LONG_ARRAY_TYPE); + } else { + // unbox the arguments from long[] (and Object[] refArgs for GC refs) + for (int i = 0; i < type.params().size(); i++) { + var param = type.params().get(i); + if (param.isObjectRef()) { + // GC ref args: load from Object[] refArgs (null-safe) + asm.load(3, OBJECT_TYPE); // refArgs + Label hasRefArgs = new Label(); + Label refDone = new Label(); + asm.dup(); + asm.ifnonnull(hasRefArgs); + asm.pop(); + asm.aconst(null); // default null for missing refArgs + asm.goTo(refDone); + asm.mark(hasRefArgs); + asm.iconst(i); + asm.aload(OBJECT_TYPE); + asm.mark(refDone); + } else { + asm.load(2, OBJECT_TYPE); + asm.iconst(i); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, param); + } + } + } + + asm.load(1, OBJECT_TYPE); + asm.load(0, OBJECT_TYPE); + + emitInvokeFunction( + asm, internalClassName(classNameForFuncGroup(className, funcId)), funcId, type); + + // Build CallResult from the function's JVM return value + Class returnType = jvmReturnType(type); + if (returnType == void.class) { + // new CallResult(null, null) + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.aconst(null); + asm.aconst(null); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } else if (returnType == Object.class) { + // Single Object ref return: new CallResult(null, new Object[]{result}) + int refSlot = 4; + asm.store(refSlot, OBJECT_TYPE); // save the Object result + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.aconst(null); // longs = null + asm.iconst(1); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + asm.dup(); + asm.iconst(0); + asm.load(refSlot, OBJECT_TYPE); + asm.astore(OBJECT_TYPE); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } else if (returnType == Object[].class) { + // Multi-value with GC refs: split Object[] into positional long[] + Object[] + // Both arrays use type.returns().size() as length, matching the interpreter + // convention where index i corresponds to result position i. + int totalResults = type.returns().size(); + int objArraySlot = 4; + asm.store(objArraySlot, OBJECT_TYPE); // save Object[] + + // Build long[totalResults] for numeric values (0L for ref slots) + int longArraySlot = 5; + asm.iconst(totalResults); + asm.newarray(LONG_TYPE); + for (int i = 0; i < totalResults; i++) { + var retType = type.returns().get(i); + if (!retType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(objArraySlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(OBJECT_TYPE); + asm.checkcast(Type.getType(Long.class)); + asm.invokevirtual("java/lang/Long", "longValue", "()J", false); + asm.astore(LONG_TYPE); + } + } + asm.store(longArraySlot, OBJECT_TYPE); + + // Build Object[totalResults] for ref values (null for numeric slots) + int refArraySlot = 6; + asm.iconst(totalResults); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + for (int i = 0; i < totalResults; i++) { + var retType = type.returns().get(i); + if (retType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(objArraySlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(OBJECT_TYPE); + asm.astore(OBJECT_TYPE); + } + } + asm.store(refArraySlot, OBJECT_TYPE); + + // new CallResult(longs, refs) + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.load(longArraySlot, OBJECT_TYPE); + asm.load(refArraySlot, OBJECT_TYPE); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } else if (returnType == long[].class) { + // Multi-value, no Object refs: new CallResult(longArray, null) + int longArraySlot = 4; + asm.store(longArraySlot, OBJECT_TYPE); + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.load(longArraySlot, OBJECT_TYPE); + asm.aconst(null); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } else { + // Single numeric return: new CallResult(new long[]{value}, null) + emitJvmToLong(asm, type.returns().get(0)); + int longSlot = 4; + asm.store(longSlot, LONG_TYPE); + asm.anew(Type.getType(CallResult.class)); + asm.dup(); + asm.iconst(1); + asm.newarray(LONG_TYPE); + asm.dup(); + asm.iconst(0); + asm.load(longSlot, LONG_TYPE); + asm.astore(LONG_TYPE); + asm.aconst(null); + asm.invokespecial( + CALL_RESULT_INTERNAL_NAME, + "", + Type.getMethodDescriptor( + VOID_TYPE, LONG_ARRAY_TYPE, Type.getType(Object[].class)), + false); + } asm.areturn(OBJECT_TYPE); } @@ -1225,18 +1776,35 @@ private void compileCallIndirect( // other: call function in another module asm.mark(other); - if (hasTooManyParameters(type)) { - asm.load(0, LONG_ARRAY_TYPE); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + if (hasTooManyParameters(type)) { + asm.load(0, LONG_ARRAY_TYPE); + asm.aconst(null); // Object[] refArgs + } else { + emitBoxArgumentsWithRefs(asm, type.params()); + // stack: long[], Object[] + } + asm.iconst(typeId); + asm.load(funcId, INT_TYPE); + asm.load(refInstance, OBJECT_TYPE); + + emitInvokeStatic(asm, CALL_INDIRECT_WITH_REFS); + // returns CallResult + emitUnboxCallResult(type, asm); } else { - emitBoxArguments(asm, type.params()); - } - asm.iconst(typeId); - asm.load(funcId, INT_TYPE); - asm.load(refInstance, OBJECT_TYPE); + if (hasTooManyParameters(type)) { + asm.load(0, LONG_ARRAY_TYPE); + } else { + emitBoxArguments(asm, type.params()); + } + asm.iconst(typeId); + asm.load(funcId, INT_TYPE); + asm.load(refInstance, OBJECT_TYPE); - emitInvokeStatic(asm, CALL_INDIRECT); + emitInvokeStatic(asm, CALL_INDIRECT); - emitUnboxResult(type, asm); + emitUnboxResult(type, asm); + } } private void compileCallIndirectApply( @@ -1302,19 +1870,79 @@ private void compileCallIndirectApply( private static void compileHostFunction(int funcId, FunctionType type, InstructionAdapter asm) { int slot = type.params().stream().mapToInt(CompilerUtil::slotCount).sum(); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + asm.load(slot + 1, OBJECT_TYPE); // instance + asm.iconst(funcId); + emitBoxArgumentsWithRefs(asm, type.params()); + // stack: instance, funcId, long[], Object[] + emitInvokeStatic(asm, CALL_HOST_FUNCTION_WITH_REFS); + // returns CallResult + emitUnboxCallResult(type, asm); + } else { + asm.load(slot + 1, OBJECT_TYPE); // instance + asm.iconst(funcId); + emitBoxArguments(asm, type.params()); + emitInvokeStatic(asm, CALL_HOST_FUNCTION); + emitUnboxResult(type, asm); + } + } - asm.load(slot + 1, OBJECT_TYPE); // instance - asm.iconst(funcId); - emitBoxArguments(asm, type.params()); - - emitInvokeStatic(asm, CALL_HOST_FUNCTION); + private static void emitBoxArguments(InstructionAdapter asm, List types) { + int slot = 0; + asm.iconst(types.size()); + asm.newarray(LONG_TYPE); + for (int i = 0; i < types.size(); i++) { + asm.dup(); + asm.iconst(i); + ValType valType = types.get(i); + asm.load(slot, asmType(valType)); + if (valType.isObjectRef()) { + asm.visitInsn(Opcodes.POP); + asm.lconst(0L); + } else { + emitJvmToLong(asm, valType); + } + asm.astore(LONG_TYPE); + slot += slotCount(valType); + } + } - emitUnboxResult(type, asm); + /** + * Boxes JVM locals into a single Object[] array. + * Numeric values are boxed as Long, Object refs stay as Object. + * Used for multi-value returns (value_xxx methods) when any return is a GC ref. + */ + private static void emitBoxArgumentsAsObjectArray(InstructionAdapter asm, List types) { + int slot = 0; + asm.iconst(types.size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + for (int i = 0; i < types.size(); i++) { + asm.dup(); + asm.iconst(i); + ValType valType = types.get(i); + asm.load(slot, asmType(valType)); + if (valType.isObjectRef()) { + // Object ref: already on stack as Object + } else { + // Numeric: convert to long then box into Long + emitJvmToLong(asm, valType); + asm.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } + asm.astore(OBJECT_TYPE); + slot += slotCount(valType); + } } - private static void emitBoxArguments(InstructionAdapter asm, List types) { + /** + * Boxes JVM locals into dual long[] + Object[] arrays. + * After this method, the stack has: [..., long[], Object[]] + * If no values are Object refs, Object[] will be null (zero overhead). + */ + private static void emitBoxArgumentsWithRefs(InstructionAdapter asm, List types) { + boolean hasObjectRefs = types.stream().anyMatch(ValType::isObjectRef); + + // Build long[] int slot = 0; - // box the arguments into long[] asm.iconst(types.size()); asm.newarray(LONG_TYPE); for (int i = 0; i < types.size(); i++) { @@ -1322,17 +1950,48 @@ private static void emitBoxArguments(InstructionAdapter asm, List types asm.iconst(i); ValType valType = types.get(i); asm.load(slot, asmType(valType)); - emitJvmToLong(asm, valType); + if (valType.isObjectRef()) { + asm.visitInsn(Opcodes.POP); + asm.lconst(0L); + } else { + emitJvmToLong(asm, valType); + } asm.astore(LONG_TYPE); slot += slotCount(valType); } + + // Build Object[] (or push null) + if (hasObjectRefs) { + asm.iconst(types.size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + slot = 0; + for (int i = 0; i < types.size(); i++) { + ValType valType = types.get(i); + if (valType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + asm.astore(OBJECT_TYPE); + } + slot += slotCount(valType); + } + } else { + asm.aconst(null); + } } private static void emitUnboxResult(FunctionType type, InstructionAdapter asm) { Class returnType = jvmReturnType(type); if (returnType == void.class) { asm.areturn(VOID_TYPE); - } else if (returnType == long[].class) { + } else if (returnType == long[].class || returnType == Object[].class) { + asm.areturn(OBJECT_TYPE); + } else if (returnType == Object.class) { + // Single Object return from cross-module call that returns long[] + // The Object result is in the long[] as 0, can't extract it + // Callers with Object refs should use emitUnboxCallResult instead + asm.visitInsn(Opcodes.POP); // pop the long[] result + asm.aconst(null); asm.areturn(OBJECT_TYPE); } else { // unbox the result from long[0] @@ -1343,6 +2002,71 @@ private static void emitUnboxResult(FunctionType type, InstructionAdapter asm) { } } + /** + * Unboxes a CallResult into the proper JVM return value. + * Used for cross-module/host calls that go through callWithRefs / callHostFunctionWithRefs. + * The CallResult has .longs() for numerics and .refs() for Object refs. + */ + private static void emitUnboxCallResult(FunctionType type, InstructionAdapter asm) { + Class returnType = jvmReturnType(type); + if (returnType == void.class) { + asm.pop(); // discard CallResult + asm.areturn(VOID_TYPE); + } else if (returnType == Object.class) { + // Single Object return: extract from CallResult.refResult(0) + asm.iconst(0); + asm.invokevirtual( + CALL_RESULT_INTERNAL_NAME, "refResult", "(I)Ljava/lang/Object;", false); + asm.areturn(OBJECT_TYPE); + } else if (returnType == long[].class) { + // Multi-value, no Object refs: extract longs() + asm.invokevirtual(CALL_RESULT_INTERNAL_NAME, "longs", "()[J", false); + asm.areturn(OBJECT_TYPE); + } else if (returnType == Object[].class) { + // Multi-value with Object refs: build Object[] from CallResult + // store CallResult in a temp + int crSlot = type.params().stream().mapToInt(CompilerUtil::slotCount).sum() + 2; + asm.store(crSlot, OBJECT_TYPE); + + asm.iconst(type.returns().size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + int slot = 0; + for (int i = 0; i < type.returns().size(); i++) { + asm.dup(); + asm.iconst(i); + ValType retType = type.returns().get(i); + if (retType.isObjectRef()) { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual( + CALL_RESULT_INTERNAL_NAME, "refResult", "(I)Ljava/lang/Object;", false); + slot++; + } else if (retType.equals(ValType.V128)) { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual(CALL_RESULT_INTERNAL_NAME, "longResult", "(I)J", false); + asm.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + slot += 2; + } else { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual(CALL_RESULT_INTERNAL_NAME, "longResult", "(I)J", false); + asm.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + slot++; + } + asm.astore(OBJECT_TYPE); + } + asm.areturn(OBJECT_TYPE); + } else { + // Single numeric return: extract from CallResult.longResult(0) + asm.iconst(0); + asm.invokevirtual(CALL_RESULT_INTERNAL_NAME, "longResult", "(I)J", false); + emitLongToJvm(asm, type.returns().get(0)); + asm.areturn(getType(returnType)); + } + } + // implements the body of: // public static func_xxx( ArgN..., Memory memory, Instance instance) private void compileFunction( @@ -1355,19 +2079,36 @@ private void compileFunction( if (interpretedFunctions.contains(funcId)) { var slots = 0; - if (hasTooManyParameters(type)) { - asm.load(0, LONG_ARRAY_TYPE); - slots = 1; + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + // WithRefs path: use dual long[] + Object[] boxing + if (hasTooManyParameters(type)) { + asm.load(0, LONG_ARRAY_TYPE); + asm.aconst(null); // Object[] refArgs + slots = 1; + } else { + emitBoxArgumentsWithRefs(asm, type.params()); + slots = type.params().stream().mapToInt(CompilerUtil::slotCount).sum(); + } + var refInstance = slots + 1; + asm.iconst(funcId); + asm.load(refInstance, OBJECT_TYPE); + emitInvokeStatic(asm, CALL_INDIRECT_ON_INTERPRETER_WITH_REFS); + // returns CallResult + emitUnboxCallResult(type, asm); } else { - emitBoxArguments(asm, type.params()); - slots = type.params().stream().mapToInt(CompilerUtil::slotCount).sum(); + if (hasTooManyParameters(type)) { + asm.load(0, LONG_ARRAY_TYPE); + slots = 1; + } else { + emitBoxArguments(asm, type.params()); + slots = type.params().stream().mapToInt(CompilerUtil::slotCount).sum(); + } + var refInstance = slots + 1; + asm.iconst(funcId); + asm.load(refInstance, OBJECT_TYPE); + emitInvokeStatic(asm, CALL_INDIRECT_ON_INTERPRETER); + emitUnboxResult(type, asm); } - var refInstance = slots + 1; - - asm.iconst(funcId); - asm.load(refInstance, OBJECT_TYPE); - emitInvokeStatic(asm, CALL_INDIRECT_ON_INTERPRETER); - emitUnboxResult(type, asm); return; } @@ -1394,10 +2135,15 @@ private void compileFunction( // unbox the arguments from long[] for (int i = 0; i < type.params().size(); i++) { var param = type.params().get(i); - asm.load(0, OBJECT_TYPE); - asm.iconst(i); - asm.aload(LONG_TYPE); - emitLongToJvm(asm, param); + if (param.isObjectRef()) { + // Object refs can't be stored in long[] — use null as default + asm.aconst(null); + } else { + asm.load(0, OBJECT_TYPE); + asm.iconst(i); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, param); + } asm.store(ctx.localSlotIndex(i), asmType(param)); } // since we just converted the arguments to long[]. @@ -1408,7 +2154,13 @@ private void compileFunction( localsCount += body.localTypes().size(); for (int i = type.params().size(); i < localsCount; i++) { var localType = localType(type, body, i); - asm.visitLdcInsn(defaultValue(localType)); + var defVal = defaultValue(localType); + if (defVal == null) { + // GC ref types: default to null (Object) + asm.aconst(null); + } else { + asm.visitLdcInsn(defVal); + } asm.store(ctx.localSlotIndex(i), asmType(localType)); } diff --git a/compiler/src/main/java/run/endive/compiler/internal/CompilerUtil.java b/compiler/src/main/java/run/endive/compiler/internal/CompilerUtil.java index ef6f8d878..95ba36377 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/CompilerUtil.java +++ b/compiler/src/main/java/run/endive/compiler/internal/CompilerUtil.java @@ -54,10 +54,11 @@ private CompilerUtil() {} public static Class jvmType(ValType type) { switch (type.opcode()) { case ValType.ID.I32: - case ValType.ID.Ref: - case ValType.ID.RefNull: case ValType.ID.ExnRef: return int.class; + case ValType.ID.Ref: + case ValType.ID.RefNull: + return type.isObjectRef() ? Object.class : int.class; case ValType.ID.I64: return long.class; case ValType.ID.F32: @@ -69,13 +70,16 @@ public static Class jvmType(ValType type) { } } + private static final Type OBJECT_ASM_TYPE = Type.getType(Object.class); + public static Type asmType(ValType type) { switch (type.opcode()) { case ValType.ID.I32: - case ValType.ID.Ref: - case ValType.ID.RefNull: case ValType.ID.ExnRef: return INT_TYPE; + case ValType.ID.Ref: + case ValType.ID.RefNull: + return type.isObjectRef() ? OBJECT_ASM_TYPE : INT_TYPE; case ValType.ID.I64: return LONG_TYPE; case ValType.ID.F32: @@ -98,9 +102,16 @@ public static ValType localType(FunctionType type, FunctionBody body, int localI public static void emitLongToJvm(MethodVisitor asm, ValType type) { switch (type.opcode()) { case ValType.ID.I32: + case ValType.ID.ExnRef: + asm.visitInsn(Opcodes.L2I); + return; case ValType.ID.Ref: case ValType.ID.RefNull: - case ValType.ID.ExnRef: + if (type.isObjectRef()) { + // GC refs are Objects - this conversion should not be called for them + // when unboxing from long[]. They come from Object[] instead. + return; + } asm.visitInsn(Opcodes.L2I); return; case ValType.ID.I64: @@ -119,9 +130,16 @@ public static void emitLongToJvm(MethodVisitor asm, ValType type) { public static void emitJvmToLong(MethodVisitor asm, ValType type) { switch (type.opcode()) { case ValType.ID.I32: + case ValType.ID.ExnRef: + asm.visitInsn(Opcodes.I2L); + return; case ValType.ID.Ref: case ValType.ID.RefNull: - case ValType.ID.ExnRef: + if (type.isObjectRef()) { + // GC refs are Objects - this conversion should not be called for them + // when boxing into long[]. They go into Object[] instead. + return; + } asm.visitInsn(Opcodes.I2L); return; case ValType.ID.I64: @@ -138,7 +156,9 @@ public static void emitJvmToLong(MethodVisitor asm, ValType type) { } public static MethodType valueMethodType(List types) { - return methodType(long[].class, jvmTypes(types)); + boolean hasGcRef = types.stream().anyMatch(t -> t.isObjectRef()); + Class returnType = hasGcRef ? Object[].class : long[].class; + return methodType(returnType, jvmTypes(types)); } public static MethodType callIndirectMethodType(FunctionType functionType) { @@ -161,7 +181,7 @@ public static MethodType rawMethodTypeFor(FunctionType type) { } public static Class[] jvmTypes(List types) { - return types.stream().map(CompilerUtil::jvmType).toArray(Class[]::new); + return types.stream().map(t -> jvmType(t)).toArray(Class[]::new); } public static Class[] jvmParameterTypes(FunctionType type) { @@ -175,6 +195,12 @@ public static Class jvmReturnType(FunctionType type) { case 1: return jvmType(type.returns().get(0)); default: + // If any return value is a GC ref (Object), use Object[] instead of long[] + for (ValType ret : type.returns()) { + if (ret.isObjectRef()) { + return Object[].class; + } + } return long[].class; } } @@ -191,6 +217,10 @@ public static Object defaultValue(ValType type) { return 0.0d; case ValType.ID.Ref: case ValType.ID.RefNull: + if (type.isObjectRef()) { + return null; // GC refs use null as their default + } + return REF_NULL_VALUE; case ValType.ID.ExnRef: return REF_NULL_VALUE; default: @@ -220,7 +250,11 @@ public static int slotCount(long valTypeId) { } public static void emitPop(MethodVisitor asm, ValType type) { - asm.visitInsn(slotCount(type) == 1 ? Opcodes.POP : Opcodes.POP2); + if (type.isObjectRef()) { + asm.visitInsn(Opcodes.POP); // Object refs are always 1 slot + } else { + asm.visitInsn(slotCount(type) == 1 ? Opcodes.POP : Opcodes.POP2); + } } public static void emitInvokeStatic(MethodVisitor asm, Method method) { @@ -269,6 +303,10 @@ static String callMethodName(int funcId) { return "call_" + funcId; } + static String callWithRefsMethodName(int funcId) { + return "callWithRefs_" + funcId; + } + public static String callIndirectMethodName(int typeId) { return "call_indirect_" + typeId; } diff --git a/compiler/src/main/java/run/endive/compiler/internal/Emitters.java b/compiler/src/main/java/run/endive/compiler/internal/Emitters.java index 40e768259..a00d34191 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/Emitters.java +++ b/compiler/src/main/java/run/endive/compiler/internal/Emitters.java @@ -83,6 +83,20 @@ public static ValType valType(long id, Context ctx) { return ValType.builder().fromId(id).build().resolve(ctx.typeSection()); } + /** + * Determines whether a source heap type represents a GC reference (Object on JVM stack) + * or a non-GC reference (int on JVM stack, e.g. funcref/externref). + */ + private static boolean isSourceGcRef(int srcHeapType, Context ctx) { + var srcType = + ValType.builder() + .withOpcode(ValType.ID.RefNull) + .withTypeIdx(srcHeapType) + .build() + .resolve(ctx.typeSection()); + return srcType.isObjectRef(); + } + public static void DROP_KEEP(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int keepStart = (int) ins.operand(0) + 1; @@ -164,12 +178,169 @@ private static void emitBoxValuesOnStack( for (int i = 0; i < types.size(); i++) { ValType valType = types.get(i); - asm.dup(); // Duplicate the array reference - asm.iconst(i); // Array index - asm.load(slot, asmType(valType)); // Load value from local + if (valType.isObjectRef()) { + // Object refs can't be stored in long[] — store 0L as placeholder + asm.dup(); + asm.iconst(i); + asm.lconst(0L); + asm.astore(LONG_TYPE); + } else { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + emitJvmToLong(asm, valType); + asm.astore(LONG_TYPE); + } + slot += slotCount(valType); + } + } + + /** + * Boxes values from the JVM operand stack into dual long[] + Object[] arrays. + * Numeric values go into long[], GC ref values go into Object[]. + * After this method, the stack has: [..., long[], Object[]] + * + * If no values are Object refs, Object[] will be null on stack (zero overhead). + */ + private static void emitBoxValuesOnStackWithRefs( + Context ctx, InstructionAdapter asm, List types) { + + boolean hasObjectRefs = types.stream().anyMatch(ValType::isObjectRef); + + // Store values from stack to locals in reverse order + int slot = ctx.tempSlot() + types.stream().mapToInt(CompilerUtil::slotCount).sum(); + for (int i = types.size() - 1; i >= 0; i--) { + ValType valType = types.get(i); + slot -= slotCount(valType); + asm.store(slot, asmType(valType)); + } + + // Create the long[] array + asm.iconst(types.size()); + asm.newarray(LONG_TYPE); + + // Load from locals and store in long[] (skip Object refs — store 0L) + slot = ctx.tempSlot(); + for (int i = 0; i < types.size(); i++) { + ValType valType = types.get(i); + + if (valType.isObjectRef()) { + // Object refs can't be stored in long[] — store 0L as placeholder + asm.dup(); + asm.iconst(i); + asm.lconst(0L); + asm.astore(LONG_TYPE); + } else { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + emitJvmToLong(asm, valType); + asm.astore(LONG_TYPE); + } + slot += slotCount(valType); + } + + // Create the Object[] array (or push null if no Object refs) + if (hasObjectRefs) { + asm.iconst(types.size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + slot = ctx.tempSlot(); + for (int i = 0; i < types.size(); i++) { + ValType valType = types.get(i); + if (valType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + asm.astore(OBJECT_TYPE); + } + slot += slotCount(valType); + } + } else { + asm.aconst(null); + } + } + + /** + * Builds both long[] and Object[] from field values on the JVM stack. + * Numeric fields go into long[], GC ref fields go into Object[]. + * Both arrays are indexed by field index. + * After this method, the stack has: [..., long[], Object[]] + */ + private static void emitBoxFieldsForStruct( + Context ctx, InstructionAdapter asm, List types) { + + // Store values from stack to locals in reverse order + int slot = ctx.tempSlot() + types.stream().mapToInt(CompilerUtil::slotCount).sum(); + for (int i = types.size() - 1; i >= 0; i--) { + ValType valType = types.get(i); + slot -= slotCount(valType); + asm.store(slot, asmType(valType)); + } + + // Create long[] for numeric fields + asm.iconst(types.size()); + asm.newarray(LONG_TYPE); + + slot = ctx.tempSlot(); + for (int i = 0; i < types.size(); i++) { + ValType valType = types.get(i); + if (!valType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + emitJvmToLong(asm, valType); + asm.astore(LONG_TYPE); + } slot += slotCount(valType); - emitJvmToLong(asm, valType); // Convert to long - asm.astore(LONG_TYPE); // Store in array + } + + // Create Object[] for ref fields (null if none) + boolean hasRefs = types.stream().anyMatch(ValType::isObjectRef); + if (hasRefs) { + asm.iconst(types.size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + slot = ctx.tempSlot(); + for (int i = 0; i < types.size(); i++) { + ValType valType = types.get(i); + if (valType.isObjectRef()) { + asm.dup(); + asm.iconst(i); + asm.load(slot, asmType(valType)); + asm.astore(OBJECT_TYPE); + } + slot += slotCount(valType); + } + } else { + asm.aconst(null); + } + } + + /** + * Boxes ref-typed element values into Object[]. + * After this method, the stack has: [..., Object[]] + */ + private static void emitBoxRefsOnStack(Context ctx, InstructionAdapter asm, int count) { + + // Store values from stack to locals in reverse order + int slot = ctx.tempSlot() + count; // Object refs are 1 slot each + for (int i = count - 1; i >= 0; i--) { + slot--; + asm.store(slot, OBJECT_TYPE); + } + + // Create Object[] + asm.iconst(count); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + slot = ctx.tempSlot(); + for (int i = 0; i < count; i++) { + asm.dup(); + asm.iconst(i); + asm.load(slot, OBJECT_TYPE); + asm.astore(OBJECT_TYPE); + slot++; } } @@ -233,21 +404,43 @@ public static void REF_FUNC(Context ctx, CompilerInstruction ins, InstructionAda } public static void REF_NULL(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.iconst(REF_NULL_VALUE); + // operand(0) is the heap type index + var type = + ValType.builder() + .withOpcode(ValType.ID.RefNull) + .withTypeIdx((int) ins.operand(0)) + .build() + .resolve(ctx.typeSection()); + if (type.isObjectRef()) { + asm.aconst(null); + } else { + asm.iconst(REF_NULL_VALUE); + } } public static void REF_IS_NULL(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - emitInvokeStatic(asm, ShadedRefs.REF_IS_NULL); + // operand(0) is the ValType id of the ref on stack + var type = valType(ins.operand(0), ctx); + if (type.isObjectRef()) { + emitInvokeStatic(asm, ShadedRefs.GC_REF_IS_NULL); + } else { + emitInvokeStatic(asm, ShadedRefs.REF_IS_NULL); + } } public static void REF_EQ(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.load(ctx.instanceSlot(), OBJECT_TYPE); + // stack: [Object, Object] -> [int] emitInvokeStatic(asm, ShadedRefs.REF_EQ); } public static void REF_AS_NON_NULL( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - emitInvokeStatic(asm, ShadedRefs.REF_AS_NON_NULL); + var type = valType(ins.operand(0), ctx); + if (type.isObjectRef()) { + emitInvokeStatic(asm, ShadedRefs.GC_REF_AS_NON_NULL); + } else { + emitInvokeStatic(asm, ShadedRefs.REF_AS_NON_NULL); + } } public static void LOCAL_GET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -279,10 +472,11 @@ public static void GLOBAL_GET(Context ctx, CompilerInstruction ins, InstructionA asm.iconst(globalIndex); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - if (globalType.isReference()) { - // Use readGlobalRef to handle i31 tagged-long values from constant initializers + if (globalType.isObjectRef()) { + // GC refs: returns Object directly emitInvokeStatic(asm, ShadedRefs.READ_GLOBAL_REF); } else { + // Numeric types and non-GC refs (funcref, externref, exnref) emitInvokeStatic(asm, ShadedRefs.READ_GLOBAL); emitLongToJvm(asm, globalType); } @@ -290,23 +484,35 @@ public static void GLOBAL_GET(Context ctx, CompilerInstruction ins, InstructionA public static void GLOBAL_SET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int globalIndex = (int) ins.operand(0); + var globalType = ctx.globalTypes().get(globalIndex); - emitJvmToLong(asm, ctx.globalTypes().get(globalIndex)); - asm.iconst(globalIndex); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.WRITE_GLOBAL); + if (globalType.isObjectRef()) { + // GC refs: Object on stack + asm.iconst(globalIndex); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.WRITE_GLOBAL_REF); + } else { + emitJvmToLong(asm, globalType); + asm.iconst(globalIndex); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.WRITE_GLOBAL); + } } public static void TABLE_GET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.iconst((int) ins.operand(0)); + int tableIdx = (int) ins.operand(0); + boolean isGcRef = ins.operand(1) != 0; + asm.iconst(tableIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.TABLE_GET); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.TABLE_GET_REF : ShadedRefs.TABLE_GET); } public static void TABLE_SET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.iconst((int) ins.operand(0)); + int tableIdx = (int) ins.operand(0); + boolean isGcRef = ins.operand(1) != 0; + asm.iconst(tableIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.TABLE_SET); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.TABLE_SET_REF : ShadedRefs.TABLE_SET); } public static void TABLE_SIZE(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -316,15 +522,19 @@ public static void TABLE_SIZE(Context ctx, CompilerInstruction ins, InstructionA } public static void TABLE_GROW(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.iconst((int) ins.operand(0)); + int tableIdx = (int) ins.operand(0); + boolean isGcRef = ins.operand(1) != 0; + asm.iconst(tableIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.TABLE_GROW); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.TABLE_GROW_REF : ShadedRefs.TABLE_GROW); } public static void TABLE_FILL(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - asm.iconst((int) ins.operand(0)); + int tableIdx = (int) ins.operand(0); + boolean isGcRef = ins.operand(1) != 0; + asm.iconst(tableIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.TABLE_FILL); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.TABLE_FILL_REF : ShadedRefs.TABLE_FILL); } public static void TABLE_COPY(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -1015,11 +1225,25 @@ public static void RETURN_CALL(Context ctx, CompilerInstruction ins, Instruction int funcId = (int) ins.operand(0); FunctionType calleeType = ctx.functionTypes().get(funcId); - emitBoxValuesOnStack(ctx, asm, calleeType.params()); - asm.iconst(funcId); - asm.swap(); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL); + if (calleeType.hasObjectRefParams()) { + // dual long[] + Object[] + emitBoxValuesOnStackWithRefs(ctx, asm, calleeType.params()); + // stack: long[], Object[] + int refArgsSlot = ctx.tempSlot(); + asm.store(refArgsSlot, OBJECT_TYPE); // save Object[] + // stack: long[] + asm.iconst(funcId); + asm.swap(); // funcId, long[] + asm.load(refArgsSlot, OBJECT_TYPE); // funcId, long[], Object[] + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL_WITH_REFS); + } else { + emitBoxValuesOnStack(ctx, asm, calleeType.params()); + asm.iconst(funcId); + asm.swap(); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL); + } emitDefaultReturn(ctx, asm); } @@ -1033,12 +1257,22 @@ public static void RETURN_CALL_INDIRECT( int savedSlot = ctx.tempSlot() + paramSlots; asm.store(savedSlot, INT_TYPE); - emitBoxValuesOnStack(ctx, asm, calleeType.params()); - asm.load(savedSlot, INT_TYPE); - asm.iconst(typeId); - asm.iconst(tableIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL_INDIRECT); + if (calleeType.hasObjectRefParams()) { + emitBoxValuesOnStackWithRefs(ctx, asm, calleeType.params()); + // stack: long[], Object[] + asm.load(savedSlot, INT_TYPE); + asm.iconst(typeId); + asm.iconst(tableIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL_INDIRECT_WITH_REFS); + } else { + emitBoxValuesOnStack(ctx, asm, calleeType.params()); + asm.load(savedSlot, INT_TYPE); + asm.iconst(typeId); + asm.iconst(tableIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL_INDIRECT); + } emitDefaultReturn(ctx, asm); } @@ -1060,11 +1294,23 @@ public static void RETURN_CALL_REF( asm.athrow(); asm.mark(notNull); - emitBoxValuesOnStack(ctx, asm, calleeType.params()); - asm.load(savedSlot, INT_TYPE); - asm.swap(); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL); + if (calleeType.hasObjectRefParams()) { + emitBoxValuesOnStackWithRefs(ctx, asm, calleeType.params()); + // stack: long[], Object[] + int refArgsSlot = ctx.tempSlot(); + asm.store(refArgsSlot, OBJECT_TYPE); // save Object[] + asm.load(savedSlot, INT_TYPE); + asm.swap(); // funcId, long[] + asm.load(refArgsSlot, OBJECT_TYPE); // funcId, long[], Object[] + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL_WITH_REFS); + } else { + emitBoxValuesOnStack(ctx, asm, calleeType.params()); + asm.load(savedSlot, INT_TYPE); + asm.swap(); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.SET_TAIL_CALL); + } emitDefaultReturn(ctx, asm); } @@ -1074,7 +1320,10 @@ private static void emitDefaultReturn(Context ctx, InstructionAdapter asm) { asm.areturn(getType(void.class)); } else if (type.returns().size() == 1) { Object defaultVal = CompilerUtil.defaultValue(type.returns().get(0)); - if (defaultVal instanceof Integer) { + if (defaultVal == null) { + // GC ref types default to null (Object) + asm.aconst(null); + } else if (defaultVal instanceof Integer) { asm.iconst((int) defaultVal); } else if (defaultVal instanceof Long) { asm.lconst((long) defaultVal); @@ -1098,6 +1347,7 @@ private static void emitTailCallCheck( asm.ifeq(noPending); List returns = functionType.returns(); + boolean hasObjectRefReturns = functionType.hasObjectRefReturns(); if (returns.size() == 1) { emitPop(asm, returns.get(0)); @@ -1105,17 +1355,76 @@ private static void emitTailCallCheck( asm.pop(); } - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.RESOLVE_TAIL_CALL); - - if (returns.isEmpty()) { - asm.pop(); - } else if (returns.size() == 1) { - asm.iconst(0); - asm.aload(LONG_TYPE); - emitLongToJvm(asm, returns.get(0)); + if (hasObjectRefReturns) { + // Use resolveTailCallWithRefs which returns CallResult + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.RESOLVE_TAIL_CALL_WITH_REFS); + + if (returns.isEmpty()) { + asm.pop(); + } else if (returns.size() == 1) { + // Single Object return: extract from CallResult.refResult(0) + asm.iconst(0); + asm.invokevirtual( + "run/endive/runtime/CallResult", + "refResult", + "(I)Ljava/lang/Object;", + false); + } else { + // Multi-value with refs: build Object[] from CallResult + int crSlot = ctx.tempSlot(); + asm.store(crSlot, OBJECT_TYPE); + + asm.iconst(returns.size()); + asm.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + int slot = 0; + for (int i = 0; i < returns.size(); i++) { + asm.dup(); + asm.iconst(i); + ValType retType = returns.get(i); + if (retType.isObjectRef()) { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual( + "run/endive/runtime/CallResult", + "refResult", + "(I)Ljava/lang/Object;", + false); + slot++; + } else if (retType.equals(ValType.V128)) { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual( + "run/endive/runtime/CallResult", "longResult", "(I)J", false); + asm.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + slot += 2; + } else { + asm.load(crSlot, OBJECT_TYPE); + asm.iconst(slot); + asm.invokevirtual( + "run/endive/runtime/CallResult", "longResult", "(I)J", false); + asm.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + slot++; + } + asm.astore(OBJECT_TYPE); + } + // Object[] left on stack — caller's emitUnboxResult will handle it + } + } else { + // Use standard resolveTailCall which returns long[] + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.RESOLVE_TAIL_CALL); + + if (returns.isEmpty()) { + asm.pop(); + } else if (returns.size() == 1) { + asm.iconst(0); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, returns.get(0)); + } + // For multi-value: leave long[] on stack — caller's emitUnboxResult will handle it } - // For multi-value: leave long[] on stack — caller's emitUnboxResult will handle it asm.mark(noPending); } @@ -1127,12 +1436,33 @@ private static void emitUnboxResult(InstructionAdapter asm, Context ctx, List types, int tempSlot) { + boolean hasObjectRefs = types.stream().anyMatch(ValType::isObjectRef); asm.store(tempSlot, OBJECT_TYPE); - for (int i = 0; i < types.size(); i++) { - asm.load(tempSlot, OBJECT_TYPE); - asm.iconst(i); - asm.aload(LONG_TYPE); - emitLongToJvm(asm, types.get(i)); + + if (hasObjectRefs) { + // Object[] on stack: numerics are boxed as Long, refs are Object + for (int i = 0; i < types.size(); i++) { + ValType type = types.get(i); + asm.load(tempSlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(OBJECT_TYPE); // AALOAD from Object[] + if (type.isObjectRef()) { + // Already the right type (Object) + } else { + // Unbox Long -> long -> JVM type + asm.checkcast(org.objectweb.asm.Type.getType(Long.class)); + asm.invokevirtual("java/lang/Long", "longValue", "()J", false); + emitLongToJvm(asm, type); + } + } + } else { + // long[] on stack: standard path + for (int i = 0; i < types.size(); i++) { + asm.load(tempSlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, types.get(i)); + } } } @@ -1176,12 +1506,17 @@ public static void THROW(Context ctx, CompilerInstruction ins, InstructionAdapte int tagNumber = (int) ins.operand(0); var type = ctx.tagFunctionType(tagNumber); - // emmit: - // call createWasmException(long[] args, int tagNumber, Instance instance) - emitBoxValuesOnStack(ctx, asm, type.params()); - asm.iconst(tagNumber); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.CREATE_WASM_EXCEPTION); + if (type.hasObjectRefParams()) { + emitBoxFieldsForStruct(ctx, asm, type.params()); + asm.iconst(tagNumber); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.CREATE_WASM_EXCEPTION_GC); + } else { + emitBoxValuesOnStack(ctx, asm, type.params()); + asm.iconst(tagNumber); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.CREATE_WASM_EXCEPTION); + } asm.athrow(); } @@ -1269,22 +1604,46 @@ public static void TRY_RESTORE_STACK( public static void CATCH_UNBOX_PARAMS( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { var tag = (int) ins.operand(0); - // Get the tag type to know what - // parameter types to unbox var tagFuncType = ctx.tagFunctionType(tag); if (!tagFuncType.params().isEmpty()) { - // unbox the exception args + boolean hasGcRefs = tagFuncType.hasObjectRefParams(); + int longArraySlot = ctx.tempSlot() + 1; + int refArraySlot = ctx.tempSlot() + 2; + + // Load long[] args asm.load(ctx.tempSlot(), OBJECT_TYPE); asm.invokevirtual( getInternalName(WasmException.class), "args", getMethodDescriptor(getType(long[].class)), false); + asm.store(longArraySlot, OBJECT_TYPE); + + // Load Object[] refArgs if needed + if (hasGcRefs) { + asm.load(ctx.tempSlot(), OBJECT_TYPE); + asm.invokevirtual( + getInternalName(WasmException.class), + "refArgs", + getMethodDescriptor(getType(Object[].class)), + false); + asm.store(refArraySlot, OBJECT_TYPE); + } - // Store the array in a local variable - // Unbox each argument from the - // long[] array and push onto stack - emitUnboxResult(asm, tagFuncType.params(), ctx.tempSlot() + 1); + // Unbox each argument + for (int i = 0; i < tagFuncType.params().size(); i++) { + var param = tagFuncType.params().get(i); + if (param.isObjectRef()) { + asm.load(refArraySlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(OBJECT_TYPE); + } else { + asm.load(longArraySlot, OBJECT_TYPE); + asm.iconst(i); + asm.aload(LONG_TYPE); + emitLongToJvm(asm, param); + } + } } } @@ -1359,7 +1718,7 @@ public static void STRUCT_NEW(Context ctx, CompilerInstruction ins, InstructionA var st = ctx.typeSection().getSubType(typeIdx).compType().structType(); var fieldCount = st.fieldTypes().length; - // Collect all field values into long[] + // Collect field types var fieldTypes = new java.util.ArrayList(fieldCount); for (int i = 0; i < fieldCount; i++) { var ft = st.fieldTypes()[i]; @@ -1370,7 +1729,9 @@ public static void STRUCT_NEW(Context ctx, CompilerInstruction ins, InstructionA fieldTypes.add(ValType.I32); } } - emitBoxValuesOnStack(ctx, asm, fieldTypes); + + // Build both long[] (numeric fields) and Object[] (ref fields) + emitBoxFieldsForStruct(ctx, asm, fieldTypes); asm.iconst(typeIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); @@ -1388,13 +1749,22 @@ public static void STRUCT_NEW_DEFAULT( public static void STRUCT_GET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); int fieldIdx = (int) ins.operand(1); - // stack: [ref] + // stack: [ref (Object)] + var st = ctx.typeSection().getSubType(typeIdx).compType().structType(); + var ft = st.fieldTypes()[fieldIdx]; + asm.iconst(typeIdx); asm.iconst(fieldIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.STRUCT_GET); - // returns long, convert to proper JVM type - emitStructGetResult(ctx, asm, typeIdx, fieldIdx); + + if (ft.storageType().isObjectRef()) { + // returns Object + emitInvokeStatic(asm, ShadedRefs.STRUCT_GET_REF); + } else { + // returns long, convert to proper JVM type + emitInvokeStatic(asm, ShadedRefs.STRUCT_GET); + emitStructGetResult(ctx, asm, typeIdx, fieldIdx); + } } public static void STRUCT_GET_S(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -1434,37 +1804,56 @@ private static void emitStructGetResult( public static void STRUCT_SET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); int fieldIdx = (int) ins.operand(1); - // stack: [ref, val] + // stack: [ref (Object), val] var st = ctx.typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; - if (ft.storageType().valType() != null) { - emitJvmToLong(asm, ft.storageType().valType()); + + if (ft.storageType().isObjectRef()) { + // val is Object on stack + asm.iconst(typeIdx); + asm.iconst(fieldIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.STRUCT_SET_REF); } else { - // packed types come as I32 - asm.visitInsn(Opcodes.I2L); + if (ft.storageType().valType() != null) { + emitJvmToLong(asm, ft.storageType().valType()); + } else { + // packed types come as I32 + asm.visitInsn(Opcodes.I2L); + } + asm.iconst(typeIdx); + asm.iconst(fieldIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.STRUCT_SET); } - asm.iconst(typeIdx); - asm.iconst(fieldIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.STRUCT_SET); } public static void ARRAY_NEW(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); - // stack: [initVal, len] - // save len to temp - asm.store(ctx.tempSlot(), INT_TYPE); - // convert initVal to long var at = ctx.typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().valType() != null) { - emitJvmToLong(asm, at.fieldType().storageType().valType()); + + if (at.fieldType().storageType().isObjectRef()) { + // stack: [initVal (Object), len] + asm.store(ctx.tempSlot(), INT_TYPE); + // initVal is Object on stack + asm.load(ctx.tempSlot(), INT_TYPE); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW_REF); } else { - asm.visitInsn(Opcodes.I2L); + // stack: [initVal, len] + asm.store(ctx.tempSlot(), INT_TYPE); + // convert initVal to long + if (at.fieldType().storageType().valType() != null) { + emitJvmToLong(asm, at.fieldType().storageType().valType()); + } else { + asm.visitInsn(Opcodes.I2L); + } + asm.load(ctx.tempSlot(), INT_TYPE); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW); } - asm.load(ctx.tempSlot(), INT_TYPE); - asm.iconst(typeIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW); } public static void ARRAY_NEW_DEFAULT( @@ -1481,20 +1870,29 @@ public static void ARRAY_NEW_FIXED( int typeIdx = (int) ins.operand(0); int len = (int) ins.operand(1); var at = ctx.typeSection().getSubType(typeIdx).compType().arrayType(); - ValType elemType; - if (at.fieldType().storageType().valType() != null) { - elemType = at.fieldType().storageType().valType(); + + if (at.fieldType().storageType().isObjectRef()) { + // Elements are Object refs, box into Object[] + emitBoxRefsOnStack(ctx, asm, len); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW_FIXED_REFS); } else { - elemType = ValType.I32; - } - var types = new java.util.ArrayList(len); - for (int i = 0; i < len; i++) { - types.add(elemType); + ValType elemType; + if (at.fieldType().storageType().valType() != null) { + elemType = at.fieldType().storageType().valType(); + } else { + elemType = ValType.I32; + } + var types = new java.util.ArrayList(len); + for (int i = 0; i < len; i++) { + types.add(elemType); + } + emitBoxValuesOnStack(ctx, asm, types); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW_FIXED); } - emitBoxValuesOnStack(ctx, asm, types); - asm.iconst(typeIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.ARRAY_NEW_FIXED); } public static void ARRAY_NEW_DATA( @@ -1528,11 +1926,17 @@ public static void ARRAY_NEW_ELEM( public static void ARRAY_GET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); - // stack: [ref, idx] + var at = ctx.typeSection().getSubType(typeIdx).compType().arrayType(); + // stack: [ref (Object), idx] asm.iconst(typeIdx); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.ARRAY_GET); - emitArrayGetResult(ctx, asm, typeIdx); + + if (at.fieldType().storageType().isObjectRef()) { + emitInvokeStatic(asm, ShadedRefs.ARRAY_GET_REF); + } else { + emitInvokeStatic(asm, ShadedRefs.ARRAY_GET); + emitArrayGetResult(ctx, asm, typeIdx); + } } public static void ARRAY_GET_S(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -1562,16 +1966,24 @@ private static void emitArrayGetResult(Context ctx, InstructionAdapter asm, int public static void ARRAY_SET(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); - // stack: [ref, idx, val] + // stack: [ref (Object), idx, val] var at = ctx.typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().valType() != null) { - emitJvmToLong(asm, at.fieldType().storageType().valType()); + + if (at.fieldType().storageType().isObjectRef()) { + // val is Object on stack + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_SET_REF); } else { - asm.visitInsn(Opcodes.I2L); + if (at.fieldType().storageType().valType() != null) { + emitJvmToLong(asm, at.fieldType().storageType().valType()); + } else { + asm.visitInsn(Opcodes.I2L); + } + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_SET); } - asm.iconst(typeIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.ARRAY_SET); } public static void ARRAY_LEN(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -1582,21 +1994,30 @@ public static void ARRAY_LEN(Context ctx, CompilerInstruction ins, InstructionAd public static void ARRAY_FILL(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int typeIdx = (int) ins.operand(0); - // stack: [ref, offset, val, len] - // save len to temp - asm.store(ctx.tempSlot(), INT_TYPE); - // stack: [ref, offset, val] var at = ctx.typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().valType() != null) { - emitJvmToLong(asm, at.fieldType().storageType().valType()); + + if (at.fieldType().storageType().isObjectRef()) { + // stack: [ref (Object), offset, val (Object), len] + asm.store(ctx.tempSlot(), INT_TYPE); + // stack: [ref, offset, val] + // val is already Object + asm.load(ctx.tempSlot(), INT_TYPE); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_FILL_REF); } else { - asm.visitInsn(Opcodes.I2L); + // stack: [ref (Object), offset, val, len] + asm.store(ctx.tempSlot(), INT_TYPE); + if (at.fieldType().storageType().valType() != null) { + emitJvmToLong(asm, at.fieldType().storageType().valType()); + } else { + asm.visitInsn(Opcodes.I2L); + } + asm.load(ctx.tempSlot(), INT_TYPE); + asm.iconst(typeIdx); + asm.load(ctx.instanceSlot(), OBJECT_TYPE); + emitInvokeStatic(asm, ShadedRefs.ARRAY_FILL); } - // stack: [ref, offset, val_as_long] - asm.load(ctx.tempSlot(), INT_TYPE); - asm.iconst(typeIdx); - asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.ARRAY_FILL); } public static void ARRAY_COPY(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { @@ -1630,81 +2051,88 @@ public static void ARRAY_INIT_ELEM( public static void REF_TEST(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int heapType = (int) ins.operand(0); int srcHeapType = (int) ins.operand(1); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); // stack: [ref] asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.REF_TEST); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.REF_TEST : ShadedRefs.REF_TEST_INT); } public static void REF_TEST_NULL(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int heapType = (int) ins.operand(0); int srcHeapType = (int) ins.operand(1); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.REF_TEST_NULL); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.REF_TEST_NULL : ShadedRefs.REF_TEST_NULL_INT); } public static void CAST_TEST(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int heapType = (int) ins.operand(0); int srcHeapType = (int) ins.operand(1); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.CAST_TEST); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.CAST_TEST : ShadedRefs.CAST_TEST_INT); } public static void CAST_TEST_NULL( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { int heapType = (int) ins.operand(0); int srcHeapType = (int) ins.operand(1); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.CAST_TEST_NULL); + emitInvokeStatic(asm, isGcRef ? ShadedRefs.CAST_TEST_NULL : ShadedRefs.CAST_TEST_NULL_INT); } public static void REF_I31(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // stack: [val] - asm.load(ctx.instanceSlot(), OBJECT_TYPE); + // stack: [val (int)] emitInvokeStatic(asm, ShadedRefs.REF_I31); + // returns Object (WasmI31Ref) } public static void I31_GET_S(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // stack: [ref] - asm.load(ctx.instanceSlot(), OBJECT_TYPE); + // stack: [ref (Object)] emitInvokeStatic(asm, ShadedRefs.I31_GET_S); } public static void I31_GET_U(Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // stack: [ref] - asm.load(ctx.instanceSlot(), OBJECT_TYPE); + // stack: [ref (Object)] emitInvokeStatic(asm, ShadedRefs.I31_GET_U); } public static void ANY_CONVERT_EXTERN( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // identity - no-op at runtime + // externref (int on JVM) -> anyref (Object on JVM) + // Box the int into an Object for use in the GC ref hierarchy. + emitInvokeStatic(asm, ShadedRefs.ANY_CONVERT_EXTERN); } public static void EXTERN_CONVERT_ANY( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // identity - no-op at runtime + // anyref (Object on JVM) -> externref (int on JVM) + // Unbox back to int. Non-null GC refs get a unique non-null int value. + emitInvokeStatic(asm, ShadedRefs.EXTERN_CONVERT_ANY); } public static void BR_ON_NULL_CHECK( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // stack: [ref] - // DUP the ref, compare against REF_NULL_VALUE + var type = valType(ins.operand(0), ctx); + boolean isGcRef = type.isObjectRef(); asm.dup(); - asm.iconst(REF_NULL_VALUE); - // result is used by following IFEQ/IFNE - // if ref == null -> 0 (equal), used with IFEQ to branch - // we want: push 1 if null, 0 if not null var isNull = new Label(); var end = new Label(); - asm.ificmpeq(isNull); + if (isGcRef) { + asm.ifnull(isNull); + } else { + asm.iconst(REF_NULL_VALUE); + asm.ificmpeq(isNull); + } asm.iconst(0); // not null asm.goTo(end); asm.mark(isNull); @@ -1714,12 +2142,17 @@ public static void BR_ON_NULL_CHECK( public static void BR_ON_NON_NULL_CHECK( Context ctx, CompilerInstruction ins, InstructionAdapter asm) { - // stack: [ref] + var type = valType(ins.operand(0), ctx); + boolean isGcRef = type.isObjectRef(); asm.dup(); - asm.iconst(REF_NULL_VALUE); var isNull = new Label(); var end = new Label(); - asm.ificmpeq(isNull); + if (isGcRef) { + asm.ifnull(isNull); + } else { + asm.iconst(REF_NULL_VALUE); + asm.ificmpeq(isNull); + } asm.iconst(1); // non-null asm.goTo(end); asm.mark(isNull); @@ -1733,13 +2166,15 @@ public static void BR_ON_CAST_CHECK( boolean nullable = ins.operand(0) != 0; int heapType = (int) ins.operand(1); int srcHeapType = (int) ins.operand(2); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); // stack: [ref] asm.dup(); asm.iconst(nullable ? 1 : 0); asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.HEAP_TYPE_MATCH); + emitInvokeStatic( + asm, isGcRef ? ShadedRefs.HEAP_TYPE_MATCH : ShadedRefs.HEAP_TYPE_MATCH_INT); // result: boolean on stack (1 if matches, 0 if not) } @@ -1749,13 +2184,15 @@ public static void BR_ON_CAST_FAIL_CHECK( boolean nullable = ins.operand(0) != 0; int heapType = (int) ins.operand(1); int srcHeapType = (int) ins.operand(2); + boolean isGcRef = isSourceGcRef(srcHeapType, ctx); // stack: [ref] asm.dup(); asm.iconst(nullable ? 1 : 0); asm.iconst(heapType); asm.iconst(srcHeapType); asm.load(ctx.instanceSlot(), OBJECT_TYPE); - emitInvokeStatic(asm, ShadedRefs.HEAP_TYPE_MATCH); + emitInvokeStatic( + asm, isGcRef ? ShadedRefs.HEAP_TYPE_MATCH : ShadedRefs.HEAP_TYPE_MATCH_INT); // invert: 1 if NOT matching (branch), 0 if matching (don't branch) var wasTrue = new Label(); var end = new Label(); diff --git a/compiler/src/main/java/run/endive/compiler/internal/Shaded.java b/compiler/src/main/java/run/endive/compiler/internal/Shaded.java index 38107d097..f27a9d5c7 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/Shaded.java +++ b/compiler/src/main/java/run/endive/compiler/internal/Shaded.java @@ -4,6 +4,7 @@ import static run.endive.wasm.types.Value.REF_NULL_VALUE; import java.util.Arrays; +import run.endive.runtime.CallResult; import run.endive.runtime.ConstantEvaluators; import run.endive.runtime.Instance; import run.endive.runtime.MemCopyWorkaround; @@ -20,7 +21,6 @@ import run.endive.wasm.InvalidException; import run.endive.wasm.WasmEngineException; import run.endive.wasm.types.ValType; -import run.endive.wasm.types.Value; /** * This class will get shaded into the compiled code. @@ -43,15 +43,42 @@ public static long[] callIndirect(long[] args, int funcId, Instance instance) { return instance.getMachine().call(funcId, args); } + public static CallResult callIndirectOnInterpreterWithRefs( + long[] args, Object[] refArgs, int funcId, Instance instance) { + return instance.getMachine().callWithRefs(funcId, args, refArgs); + } + + public static CallResult callIndirectWithRefs( + long[] args, Object[] refArgs, int typeId, int funcId, Instance instance) { + int actualTypeIdx = instance.functionType(funcId); + if (actualTypeIdx != typeId + && !ValType.heapTypeSubtype( + actualTypeIdx, typeId, instance.module().typeSection())) { + throw throwIndirectCallTypeMismatch(); + } + return instance.getMachine().callWithRefs(funcId, args, refArgs); + } + public static long[] callHostFunction(Instance instance, int funcId, long[] args) { var imprt = instance.imports().function(funcId); return imprt.handle().apply(instance, args); } + public static CallResult callHostFunctionWithRefs( + Instance instance, int funcId, long[] args, Object[] refArgs) { + var imprt = instance.imports().function(funcId); + return imprt.handle().applyWithRefs(instance, args, refArgs); + } + public static void setTailCall(int funcId, long[] args, Instance instance) { instance.setTailCall(funcId, args); } + public static void setTailCallWithRefs( + int funcId, long[] args, Object[] refArgs, Instance instance) { + instance.setTailCall(funcId, args, refArgs); + } + public static void setTailCallIndirect( long[] args, int funcTableIdx, int typeId, int tableIdx, Instance instance) { TableInstance table = instance.table(tableIdx); @@ -70,6 +97,29 @@ public static void setTailCallIndirect( instance.setTailCall(funcId, args); } + public static void setTailCallIndirectWithRefs( + long[] args, + Object[] refArgs, + int funcTableIdx, + int typeId, + int tableIdx, + Instance instance) { + TableInstance table = instance.table(tableIdx); + int funcId = table.requiredRef(funcTableIdx); + Instance refInstance = table.instance(funcTableIdx); + if (refInstance != null && refInstance != instance) { + throw new WasmEngineException( + "Indirect tail-call to a different Machine implementation is not supported"); + } + int actualTypeIdx = instance.functionType(funcId); + if (actualTypeIdx != typeId + && !ValType.heapTypeSubtype( + actualTypeIdx, typeId, instance.module().typeSection())) { + throw throwIndirectCallTypeMismatch(); + } + instance.setTailCall(funcId, args, refArgs); + } + public static boolean isTailCallPending(Instance instance) { return instance.isTailCallPending(); } @@ -81,10 +131,22 @@ public static long[] resolveTailCall(Instance instance) { return instance.getMachine().call(funcId, args); } + public static CallResult resolveTailCallWithRefs(Instance instance) { + int funcId = instance.tailCallFuncId(); + long[] args = instance.tailCallArgs(); + Object[] refArgs = instance.tailCallRefArgs(); + instance.clearTailCall(); + return instance.getMachine().callWithRefs(funcId, args, refArgs); + } + public static boolean isRefNull(int ref) { return ref == REF_NULL_VALUE; } + public static boolean isGcRefNull(Object ref) { + return ref == null; + } + public static int refAsNonNull(int ref) { if (ref == REF_NULL_VALUE) { throw new TrapException("null reference"); @@ -92,18 +154,48 @@ public static int refAsNonNull(int ref) { return ref; } + public static Object gcRefAsNonNull(Object ref) { + if (ref == null) { + throw new TrapException("null reference"); + } + return ref; + } + public static int tableGet(int index, int tableIndex, Instance instance) { return OpcodeImpl.TABLE_GET(instance, tableIndex, index); } + public static Object tableGetRef(int index, int tableIndex, Instance instance) { + return instance.table(tableIndex).objRef(index); + } + public static void tableSet(int index, int value, int tableIndex, Instance instance) { instance.table(tableIndex).setRef(index, value, instance); } + public static void tableSetRef(int index, Object value, int tableIndex, Instance instance) { + instance.table(tableIndex).setObjRef(index, value, instance); + } + public static int tableGrow(int value, int size, int tableIndex, Instance instance) { return instance.table(tableIndex).grow(size, value, instance); } + public static int tableGrowRef(Object value, int size, int tableIndex, Instance instance) { + return instance.table(tableIndex).growWithRef(size, value, instance); + } + + public static void tableFillRef( + int offset, Object value, int size, int tableIndex, Instance instance) { + var table = instance.table(tableIndex); + if ((long) offset + (long) size > table.size()) { + throw new TrapException("out of bounds table access"); + } + for (int i = 0; i < size; i++) { + table.setObjRef(offset + i, value, instance); + } + } + public static int tableSize(int tableIndex, Instance instance) { return instance.table(tableIndex).size(); } @@ -378,13 +470,12 @@ public static void writeGlobal(long value, int index, Instance instance) { instance.global(index).setValue(value); } - public static int readGlobalRef(int index, Instance instance) { - long val = instance.global(index).getValue(); - if (Value.isI31(val)) { - var i31 = new WasmI31Ref(Value.decodeI31U(val)); - return instance.registerGcRef(i31); - } - return (int) val; + public static void writeGlobalRef(Object value, int index, Instance instance) { + instance.global(index).setRefValue(value); + } + + public static Object readGlobalRef(int index, Instance instance) { + return instance.global(index).getRefValue(); } /** @@ -399,6 +490,16 @@ public static WasmException createWasmException(long[] args, int tagNumber, Inst return e; } + public static WasmException createWasmExceptionGc( + long[] args, Object[] refArgs, int tagNumber, Instance instance) { + if (args == null) { + args = new long[0]; + } + WasmException e = new WasmException(instance, tagNumber, args, refArgs); + instance.registerException(e); + return e; + } + public static boolean exceptionMatches(WasmException exception, int tag, Instance instance) { if (exception.instance() == instance && exception.tagIdx() == tag) { return true; @@ -810,37 +911,47 @@ public static void memoryAtomicFence(Memory memory) { // ========= GC Operations ========= - public static int structNew(long[] fields, int typeIdx, Instance instance) { - var struct = new WasmStruct(typeIdx, fields); - return instance.registerGcRef(struct); + public static Object structNew( + long[] fields, Object[] fieldRefs, int typeIdx, Instance instance) { + return new WasmStruct(typeIdx, fields, fieldRefs); } - public static int structNewDefault(int typeIdx, Instance instance) { + public static Object structNewDefault(int typeIdx, Instance instance) { var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var fields = new long[st.fieldTypes().length]; - for (int i = 0; i < fields.length; i++) { + var fieldRefs = new Object[st.fieldTypes().length]; + for (int i = 0; i < st.fieldTypes().length; i++) { var ft = st.fieldTypes()[i]; - if (ft.storageType().valType() != null && ft.storageType().valType().isReference()) { - fields[i] = Value.REF_NULL_VALUE; + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + fields[i] = REF_NULL_VALUE; } } - var struct = new WasmStruct(typeIdx, fields); - return instance.registerGcRef(struct); + return new WasmStruct(typeIdx, fields, fieldRefs); } - public static long structGet(int ref, int typeIdx, int fieldIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static long structGet(Object ref, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { throw new TrapException("null structure reference"); } - var struct = (WasmStruct) instance.gcRef(ref); + var struct = (WasmStruct) ref; return struct.field(fieldIdx); } - public static long structGetS(int ref, int typeIdx, int fieldIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static Object structGetRef(Object ref, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { throw new TrapException("null structure reference"); } - var struct = (WasmStruct) instance.gcRef(ref); + var struct = (WasmStruct) ref; + return struct.fieldRef(fieldIdx); + } + + public static long structGetS(Object ref, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { + throw new TrapException("null structure reference"); + } + var struct = (WasmStruct) ref; var val = struct.field(fieldIdx); var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; @@ -850,11 +961,11 @@ public static long structGetS(int ref, int typeIdx, int fieldIdx, Instance insta return val; } - public static long structGetU(int ref, int typeIdx, int fieldIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static long structGetU(Object ref, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { throw new TrapException("null structure reference"); } - var struct = (WasmStruct) instance.gcRef(ref); + var struct = (WasmStruct) ref; var val = struct.field(fieldIdx); var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; @@ -864,11 +975,12 @@ public static long structGetU(int ref, int typeIdx, int fieldIdx, Instance insta return val; } - public static void structSet(int ref, long val, int typeIdx, int fieldIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static void structSet( + Object ref, long val, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { throw new TrapException("null structure reference"); } - var struct = (WasmStruct) instance.gcRef(ref); + var struct = (WasmStruct) ref; var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; if (ft.storageType().packedType() != null) { @@ -877,30 +989,51 @@ public static void structSet(int ref, long val, int typeIdx, int fieldIdx, Insta struct.setField(fieldIdx, val); } - public static int arrayNew(long initVal, int len, int typeIdx, Instance instance) { + public static void structSetRef( + Object ref, Object val, int typeIdx, int fieldIdx, Instance instance) { + if (ref == null) { + throw new TrapException("null structure reference"); + } + var struct = (WasmStruct) ref; + struct.setFieldRef(fieldIdx, val); + } + + public static Object arrayNew(long initVal, int len, int typeIdx, Instance instance) { var elems = new long[len]; Arrays.fill(elems, initVal); - var arr = new WasmArray(typeIdx, elems); - return instance.registerGcRef(arr); + return new WasmArray(typeIdx, elems); } - public static int arrayNewDefault(int len, int typeIdx, Instance instance) { - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + public static Object arrayNewRef(Object initVal, int len, int typeIdx, Instance instance) { + var elems = new long[len]; + var elemRefs = new Object[len]; + Arrays.fill(elemRefs, initVal); + return new WasmArray(typeIdx, elems, elemRefs); + } + + public static Object arrayNewDefault(int len, int typeIdx, Instance instance) { var elems = new long[len]; - if (at.fieldType().storageType().valType() != null - && at.fieldType().storageType().valType().isReference()) { - Arrays.fill(elems, Value.REF_NULL_VALUE); + var elemRefs = new Object[len]; + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var ft = at.fieldType(); + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + Arrays.fill(elems, REF_NULL_VALUE); } - var arr = new WasmArray(typeIdx, elems); - return instance.registerGcRef(arr); + return new WasmArray(typeIdx, elems, elemRefs); + } + + public static Object arrayNewFixed(long[] vals, int typeIdx, Instance instance) { + return new WasmArray(typeIdx, vals); } - public static int arrayNewFixed(long[] vals, int typeIdx, Instance instance) { - var arr = new WasmArray(typeIdx, vals); - return instance.registerGcRef(arr); + public static Object arrayNewFixedRefs(Object[] vals, int typeIdx, Instance instance) { + var elems = new long[vals.length]; + return new WasmArray(typeIdx, elems, vals); } - public static int arrayNewData( + public static Object arrayNewData( int offset, int len, int typeIdx, int dataIdx, Instance instance) { var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); var elemSize = at.fieldType().storageType().byteSize(); @@ -913,41 +1046,58 @@ public static int arrayNewData( var byteOff = offset + i * elemSize; elems[i] = readFromData(data, byteOff, elemSize); } - var arr = new WasmArray(typeIdx, elems); - return instance.registerGcRef(arr); + return new WasmArray(typeIdx, elems); } - public static int arrayNewElem( + public static Object arrayNewElem( int offset, int len, int typeIdx, int elemIdx, Instance instance) { var element = instance.element(elemIdx); if (element == null || offset + len > element.elementCount()) { throw new TrapException("out of bounds table access"); } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); var elems = new long[len]; + var elemRefs = new Object[len]; for (int i = 0; i < len; i++) { - elems[i] = - elementValueToRef(computeElementValue(instance, elemIdx, offset + i), instance); + var init = element.initializers().get(offset + i); + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + elemRefs[i] = result.ref(); + } else { + elems[i] = result.longValue(); + } } - var arr = new WasmArray(typeIdx, elems); - return instance.registerGcRef(arr); + return new WasmArray(typeIdx, elems, elemRefs); } - public static long arrayGet(int ref, int idx, int typeIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static long arrayGet(Object ref, int idx, int typeIdx, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } return arr.get(idx); } - public static long arrayGetS(int ref, int idx, int typeIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static Object arrayGetRef(Object ref, int idx, int typeIdx, Instance instance) { + if (ref == null) { + throw new TrapException("null array reference"); + } + var arr = (WasmArray) ref; + if (idx < 0 || idx >= arr.length()) { + throw new TrapException("out of bounds array access"); + } + return arr.getRef(idx); + } + + public static long arrayGetS(Object ref, int idx, int typeIdx, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } @@ -959,11 +1109,11 @@ public static long arrayGetS(int ref, int idx, int typeIdx, Instance instance) { return val; } - public static long arrayGetU(int ref, int idx, int typeIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static long arrayGetU(Object ref, int idx, int typeIdx, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } @@ -975,11 +1125,11 @@ public static long arrayGetU(int ref, int idx, int typeIdx, Instance instance) { return val; } - public static void arraySet(int ref, int idx, long val, int typeIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static void arraySet(Object ref, int idx, long val, int typeIdx, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } @@ -990,20 +1140,32 @@ public static void arraySet(int ref, int idx, long val, int typeIdx, Instance in arr.set(idx, val); } - public static int arrayLen(int ref, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static void arraySetRef( + Object ref, int idx, Object val, int typeIdx, Instance instance) { + if (ref == null) { + throw new TrapException("null array reference"); + } + var arr = (WasmArray) ref; + if (idx < 0 || idx >= arr.length()) { + throw new TrapException("out of bounds array access"); + } + arr.setRef(idx, val); + } + + public static int arrayLen(Object ref, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; return arr.length(); } public static void arrayFill( - int ref, int offset, long val, int len, int typeIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + Object ref, int offset, long val, int len, int typeIdx, Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; if (offset + len > arr.length()) { throw new TrapException("out of bounds array access"); } @@ -1016,33 +1178,55 @@ public static void arrayFill( } } + public static void arrayFillRef( + Object ref, int offset, Object val, int len, int typeIdx, Instance instance) { + if (ref == null) { + throw new TrapException("null array reference"); + } + var arr = (WasmArray) ref; + if (offset + len > arr.length()) { + throw new TrapException("out of bounds array access"); + } + for (int i = 0; i < len; i++) { + arr.setRef(offset + i, val); + } + } + public static void arrayCopy( - int dstRef, int dstOff, int srcRef, int srcOff, int len, Instance instance) { - if (dstRef == REF_NULL_VALUE || srcRef == REF_NULL_VALUE) { + Object dstRef, int dstOff, Object srcRef, int srcOff, int len, Instance instance) { + if (dstRef == null || srcRef == null) { throw new TrapException("null array reference"); } - var dst = (WasmArray) instance.gcRef(dstRef); - var src = (WasmArray) instance.gcRef(srcRef); + var dst = (WasmArray) dstRef; + var src = (WasmArray) srcRef; if (dstOff + len > dst.length() || srcOff + len > src.length()) { throw new TrapException("out of bounds array access"); } if (dstOff <= srcOff) { for (int i = 0; i < len; i++) { dst.set(dstOff + i, src.get(srcOff + i)); + dst.setRef(dstOff + i, src.getRef(srcOff + i)); } } else { for (int i = len - 1; i >= 0; i--) { dst.set(dstOff + i, src.get(srcOff + i)); + dst.setRef(dstOff + i, src.getRef(srcOff + i)); } } } public static void arrayInitData( - int ref, int dstOff, int srcOff, int len, int typeIdx, int dataIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + Object ref, + int dstOff, + int srcOff, + int len, + int typeIdx, + int dataIdx, + Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); var elemSize = at.fieldType().storageType().byteSize(); var data = instance.dataSegmentData(dataIdx); @@ -1059,11 +1243,17 @@ public static void arrayInitData( } public static void arrayInitElem( - int ref, int dstOff, int srcOff, int len, int typeIdx, int elemIdx, Instance instance) { - if (ref == REF_NULL_VALUE) { + Object ref, + int dstOff, + int srcOff, + int len, + int typeIdx, + int elemIdx, + Instance instance) { + if (ref == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) ref; var element = instance.element(elemIdx); if (dstOff + len > arr.length()) { throw new TrapException("out of bounds array access"); @@ -1075,92 +1265,113 @@ public static void arrayInitElem( if (len == 0) { return; } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); for (int i = 0; i < len; i++) { - arr.set( - dstOff + i, - elementValueToRef( - computeElementValue(instance, elemIdx, srcOff + i), instance)); + var init = element.initializers().get(srcOff + i); + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + arr.setRef(dstOff + i, result.ref()); + } else { + arr.set(dstOff + i, result.longValue()); + } } } - public static int refTest(int ref, int heapType, int srcHeapType, Instance instance) { + // GC ref (Object on JVM stack) variants + public static int refTest(Object ref, int heapType, int srcHeapType, Instance instance) { + return instance.heapTypeMatchRef(ref, false, heapType, srcHeapType) ? 1 : 0; + } + + public static int refTestNull(Object ref, int heapType, int srcHeapType, Instance instance) { + return instance.heapTypeMatchRef(ref, true, heapType, srcHeapType) ? 1 : 0; + } + + public static Object castTest(Object ref, int heapType, int srcHeapType, Instance instance) { + if (!instance.heapTypeMatchRef(ref, false, heapType, srcHeapType)) { + throw new TrapException("cast failure"); + } + return ref; + } + + public static Object castTestNull( + Object ref, int heapType, int srcHeapType, Instance instance) { + if (!instance.heapTypeMatchRef(ref, true, heapType, srcHeapType)) { + throw new TrapException("cast failure"); + } + return ref; + } + + public static boolean heapTypeMatch( + Object ref, boolean nullable, int heapType, int srcHeapType, Instance instance) { + return instance.heapTypeMatchRef(ref, nullable, heapType, srcHeapType); + } + + // Non-GC ref (int on JVM stack) variants for funcref/externref + public static int refTestInt(int ref, int heapType, int srcHeapType, Instance instance) { return instance.heapTypeMatch(ref, false, heapType, srcHeapType) ? 1 : 0; } - public static int refTestNull(int ref, int heapType, int srcHeapType, Instance instance) { + public static int refTestNullInt(int ref, int heapType, int srcHeapType, Instance instance) { return instance.heapTypeMatch(ref, true, heapType, srcHeapType) ? 1 : 0; } - public static int castTest(int ref, int heapType, int srcHeapType, Instance instance) { + public static int castTestInt(int ref, int heapType, int srcHeapType, Instance instance) { if (!instance.heapTypeMatch(ref, false, heapType, srcHeapType)) { throw new TrapException("cast failure"); } return ref; } - public static int castTestNull(int ref, int heapType, int srcHeapType, Instance instance) { + public static int castTestNullInt(int ref, int heapType, int srcHeapType, Instance instance) { if (!instance.heapTypeMatch(ref, true, heapType, srcHeapType)) { throw new TrapException("cast failure"); } return ref; } - public static boolean heapTypeMatch( + public static boolean heapTypeMatchInt( int ref, boolean nullable, int heapType, int srcHeapType, Instance instance) { return instance.heapTypeMatch(ref, nullable, heapType, srcHeapType); } - public static int refI31(int val, Instance instance) { - var i31 = new WasmI31Ref(val & 0x7FFFFFFF); - return instance.registerGcRef(i31); + public static Object refI31(int val) { + return new WasmI31Ref(val & 0x7FFFFFFF); } - public static int i31GetS(int ref, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static int i31GetS(Object ref) { + if (ref == null) { throw new TrapException("null i31 reference"); } - var i31 = (WasmI31Ref) instance.gcRef(ref); + var i31 = (WasmI31Ref) ref; int val = i31.value(); // sign extend from 31 bits return (val << 1) >> 1; } - public static int i31GetU(int ref, Instance instance) { - if (ref == REF_NULL_VALUE) { + public static int i31GetU(Object ref) { + if (ref == null) { throw new TrapException("null i31 reference"); } - var i31 = (WasmI31Ref) instance.gcRef(ref); + var i31 = (WasmI31Ref) ref; return i31.value() & 0x7FFFFFFF; } - public static int refEq(int a, int b, Instance instance) { + public static int refEq(Object a, Object b) { if (a == b) { return 1; } - if (a == REF_NULL_VALUE || b == REF_NULL_VALUE) { + if (a == null || b == null) { return 0; } - var gcA = instance.gcRef(a); - var gcB = instance.gcRef(b); - if (gcA instanceof WasmI31Ref && gcB instanceof WasmI31Ref) { - return ((WasmI31Ref) gcA).value() == ((WasmI31Ref) gcB).value() ? 1 : 0; + if (a instanceof WasmI31Ref && b instanceof WasmI31Ref) { + return ((WasmI31Ref) a).value() == ((WasmI31Ref) b).value() ? 1 : 0; } return 0; } - private static long elementValueToRef(long val, Instance instance) { - if (Value.isI31(val)) { - var i31 = new WasmI31Ref(Value.decodeI31U(val)); - return instance.registerGcRef(i31); - } - return val; - } - - private static long computeElementValue(Instance instance, int elemIdx, int offset) { - var element = instance.element(elemIdx); - var init = element.initializers().get(offset); - return ConstantEvaluators.computeConstantValue(instance, init)[0]; - } + // elementValueToRef and computeElementValue removed: arrayNewElem/arrayInitElem now use + // ConstantEvaluators.computeConstant directly to properly handle GC ref elements. private static long readFromData(byte[] data, int offset, int size) { long val = 0; @@ -1170,6 +1381,14 @@ private static long readFromData(byte[] data, int offset, int size) { return val; } + public static Object anyConvertExtern(Object ref) { + return ref; + } + + public static Object externConvertAny(Object ref) { + return ref; + } + public static void dataDrop(int segment, Instance instance) { instance.dropDataSegment(segment); } diff --git a/compiler/src/main/java/run/endive/compiler/internal/ShadedRefs.java b/compiler/src/main/java/run/endive/compiler/internal/ShadedRefs.java index 89b5420b0..1414771dc 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/ShadedRefs.java +++ b/compiler/src/main/java/run/endive/compiler/internal/ShadedRefs.java @@ -13,12 +13,14 @@ public final class ShadedRefs { static final Method CHECK_INTERRUPTION; static final Method CALL_INDIRECT; static final Method CALL_INDIRECT_ON_INTERPRETER; + static final Method CALL_INDIRECT_ON_INTERPRETER_WITH_REFS; static final Method INSTANCE_MEMORY; static final Method INSTANCE_MEMORY_IDX; static final Method CALL_HOST_FUNCTION; static final Method READ_GLOBAL; static final Method READ_GLOBAL_REF; static final Method WRITE_GLOBAL; + static final Method WRITE_GLOBAL_REF; static final Method INSTANCE_SET_ELEMENT; static final Method INSTANCE_TABLE; static final Method MEMORY_COPY; @@ -49,12 +51,18 @@ public final class ShadedRefs { static final Method MEMORY_ATOMIC_LONG_READ; static final Method I32_GE_U; static final Method REF_IS_NULL; + static final Method GC_REF_IS_NULL; static final Method REF_AS_NON_NULL; + static final Method GC_REF_AS_NON_NULL; static final Method TABLE_GET; + static final Method TABLE_GET_REF; static final Method TABLE_SET; + static final Method TABLE_SET_REF; static final Method TABLE_SIZE; static final Method TABLE_GROW; + static final Method TABLE_GROW_REF; static final Method TABLE_FILL; + static final Method TABLE_FILL_REF; static final Method TABLE_COPY; static final Method TABLE_INIT; static final Method TABLE_REQUIRED_REF; @@ -69,6 +77,7 @@ public final class ShadedRefs { // Exception handling methods static final Method CREATE_WASM_EXCEPTION; + static final Method CREATE_WASM_EXCEPTION_GC; static final Method INSTANCE_GET_EXCEPTION; static final Method EXCEPTION_MATCHES; @@ -136,28 +145,42 @@ public final class ShadedRefs { // Tail calls static final Method SET_TAIL_CALL; + static final Method SET_TAIL_CALL_WITH_REFS; static final Method SET_TAIL_CALL_INDIRECT; + static final Method SET_TAIL_CALL_INDIRECT_WITH_REFS; static final Method IS_TAIL_CALL_PENDING; static final Method RESOLVE_TAIL_CALL; + static final Method RESOLVE_TAIL_CALL_WITH_REFS; + + // WithRefs overloads for cross-module/host calls + static final Method CALL_HOST_FUNCTION_WITH_REFS; + static final Method CALL_INDIRECT_WITH_REFS; // GC static final Method STRUCT_NEW; static final Method STRUCT_NEW_DEFAULT; static final Method STRUCT_GET; + static final Method STRUCT_GET_REF; static final Method STRUCT_GET_S; static final Method STRUCT_GET_U; static final Method STRUCT_SET; + static final Method STRUCT_SET_REF; static final Method ARRAY_NEW; + static final Method ARRAY_NEW_REF; static final Method ARRAY_NEW_DEFAULT; static final Method ARRAY_NEW_FIXED; + static final Method ARRAY_NEW_FIXED_REFS; static final Method ARRAY_NEW_DATA; static final Method ARRAY_NEW_ELEM; static final Method ARRAY_GET; + static final Method ARRAY_GET_REF; static final Method ARRAY_GET_S; static final Method ARRAY_GET_U; static final Method ARRAY_SET; + static final Method ARRAY_SET_REF; static final Method ARRAY_LEN; static final Method ARRAY_FILL; + static final Method ARRAY_FILL_REF; static final Method ARRAY_COPY; static final Method ARRAY_INIT_DATA; static final Method ARRAY_INIT_ELEM; @@ -166,10 +189,18 @@ public final class ShadedRefs { static final Method CAST_TEST; static final Method CAST_TEST_NULL; static final Method HEAP_TYPE_MATCH; + // int-based variants for funcref/externref (non-GC refs, int on JVM stack) + static final Method REF_TEST_INT; + static final Method REF_TEST_NULL_INT; + static final Method CAST_TEST_INT; + static final Method CAST_TEST_NULL_INT; + static final Method HEAP_TYPE_MATCH_INT; static final Method REF_EQ; static final Method REF_I31; static final Method I31_GET_S; static final Method I31_GET_U; + static final Method ANY_CONVERT_EXTERN; + static final Method EXTERN_CONVERT_ANY; static final Method DATA_DROP; static { @@ -180,6 +211,13 @@ public final class ShadedRefs { "callIndirect", long[].class, int.class, int.class, Instance.class); CALL_INDIRECT_ON_INTERPRETER = Shaded.class.getMethod("callIndirect", long[].class, int.class, Instance.class); + CALL_INDIRECT_ON_INTERPRETER_WITH_REFS = + Shaded.class.getMethod( + "callIndirectOnInterpreterWithRefs", + long[].class, + Object[].class, + int.class, + Instance.class); INSTANCE_MEMORY = Instance.class.getMethod("memory"); INSTANCE_MEMORY_IDX = Instance.class.getMethod("memory", int.class); CALL_HOST_FUNCTION = @@ -189,6 +227,9 @@ public final class ShadedRefs { READ_GLOBAL_REF = Shaded.class.getMethod("readGlobalRef", int.class, Instance.class); WRITE_GLOBAL = Shaded.class.getMethod("writeGlobal", long.class, int.class, Instance.class); + WRITE_GLOBAL_REF = + Shaded.class.getMethod( + "writeGlobalRef", Object.class, int.class, Instance.class); INSTANCE_SET_ELEMENT = Instance.class.getMethod("setElement", int.class, Element.class); INSTANCE_TABLE = Instance.class.getMethod("table", int.class); MEMORY_COPY = @@ -264,15 +305,25 @@ public final class ShadedRefs { "memoryAtomicLongIntRead", int.class, int.class, Memory.class); I32_GE_U = Shaded.class.getMethod("i32_ge_u", int.class, int.class); REF_IS_NULL = Shaded.class.getMethod("isRefNull", int.class); + GC_REF_IS_NULL = Shaded.class.getMethod("isGcRefNull", Object.class); REF_AS_NON_NULL = Shaded.class.getMethod("refAsNonNull", int.class); + GC_REF_AS_NON_NULL = Shaded.class.getMethod("gcRefAsNonNull", Object.class); TABLE_GET = Shaded.class.getMethod("tableGet", int.class, int.class, Instance.class); + TABLE_GET_REF = + Shaded.class.getMethod("tableGetRef", int.class, int.class, Instance.class); TABLE_SET = Shaded.class.getMethod( "tableSet", int.class, int.class, int.class, Instance.class); + TABLE_SET_REF = + Shaded.class.getMethod( + "tableSetRef", int.class, Object.class, int.class, Instance.class); TABLE_SIZE = Shaded.class.getMethod("tableSize", int.class, Instance.class); TABLE_GROW = Shaded.class.getMethod( "tableGrow", int.class, int.class, int.class, Instance.class); + TABLE_GROW_REF = + Shaded.class.getMethod( + "tableGrowRef", Object.class, int.class, int.class, Instance.class); TABLE_FILL = Shaded.class.getMethod( "tableFill", @@ -281,6 +332,14 @@ public final class ShadedRefs { int.class, int.class, Instance.class); + TABLE_FILL_REF = + Shaded.class.getMethod( + "tableFillRef", + int.class, + Object.class, + int.class, + int.class, + Instance.class); TABLE_COPY = Shaded.class.getMethod( "tableCopy", @@ -318,6 +377,13 @@ public final class ShadedRefs { CREATE_WASM_EXCEPTION = Shaded.class.getMethod( "createWasmException", long[].class, int.class, Instance.class); + CREATE_WASM_EXCEPTION_GC = + Shaded.class.getMethod( + "createWasmExceptionGc", + long[].class, + Object[].class, + int.class, + Instance.class); INSTANCE_GET_EXCEPTION = Instance.class.getMethod("exn", int.class); EXCEPTION_MATCHES = Shaded.class.getMethod( @@ -723,6 +789,13 @@ public final class ShadedRefs { // Tail calls SET_TAIL_CALL = Shaded.class.getMethod("setTailCall", int.class, long[].class, Instance.class); + SET_TAIL_CALL_WITH_REFS = + Shaded.class.getMethod( + "setTailCallWithRefs", + int.class, + long[].class, + Object[].class, + Instance.class); SET_TAIL_CALL_INDIRECT = Shaded.class.getMethod( "setTailCallIndirect", @@ -731,39 +804,85 @@ public final class ShadedRefs { int.class, int.class, Instance.class); + SET_TAIL_CALL_INDIRECT_WITH_REFS = + Shaded.class.getMethod( + "setTailCallIndirectWithRefs", + long[].class, + Object[].class, + int.class, + int.class, + int.class, + Instance.class); IS_TAIL_CALL_PENDING = Shaded.class.getMethod("isTailCallPending", Instance.class); RESOLVE_TAIL_CALL = Shaded.class.getMethod("resolveTailCall", Instance.class); + RESOLVE_TAIL_CALL_WITH_REFS = + Shaded.class.getMethod("resolveTailCallWithRefs", Instance.class); + + // WithRefs overloads for cross-module/host calls + CALL_HOST_FUNCTION_WITH_REFS = + Shaded.class.getMethod( + "callHostFunctionWithRefs", + Instance.class, + int.class, + long[].class, + Object[].class); + CALL_INDIRECT_WITH_REFS = + Shaded.class.getMethod( + "callIndirectWithRefs", + long[].class, + Object[].class, + int.class, + int.class, + Instance.class); // GC STRUCT_NEW = - Shaded.class.getMethod("structNew", long[].class, int.class, Instance.class); + Shaded.class.getMethod( + "structNew", long[].class, Object[].class, int.class, Instance.class); STRUCT_NEW_DEFAULT = Shaded.class.getMethod("structNewDefault", int.class, Instance.class); STRUCT_GET = Shaded.class.getMethod( - "structGet", int.class, int.class, int.class, Instance.class); + "structGet", Object.class, int.class, int.class, Instance.class); + STRUCT_GET_REF = + Shaded.class.getMethod( + "structGetRef", Object.class, int.class, int.class, Instance.class); STRUCT_GET_S = Shaded.class.getMethod( - "structGetS", int.class, int.class, int.class, Instance.class); + "structGetS", Object.class, int.class, int.class, Instance.class); STRUCT_GET_U = Shaded.class.getMethod( - "structGetU", int.class, int.class, int.class, Instance.class); + "structGetU", Object.class, int.class, int.class, Instance.class); STRUCT_SET = Shaded.class.getMethod( "structSet", - int.class, + Object.class, long.class, int.class, int.class, Instance.class); + STRUCT_SET_REF = + Shaded.class.getMethod( + "structSetRef", + Object.class, + Object.class, + int.class, + int.class, + Instance.class); ARRAY_NEW = Shaded.class.getMethod( "arrayNew", long.class, int.class, int.class, Instance.class); + ARRAY_NEW_REF = + Shaded.class.getMethod( + "arrayNewRef", Object.class, int.class, int.class, Instance.class); ARRAY_NEW_DEFAULT = Shaded.class.getMethod("arrayNewDefault", int.class, int.class, Instance.class); ARRAY_NEW_FIXED = Shaded.class.getMethod( "arrayNewFixed", long[].class, int.class, Instance.class); + ARRAY_NEW_FIXED_REFS = + Shaded.class.getMethod( + "arrayNewFixedRefs", Object[].class, int.class, Instance.class); ARRAY_NEW_DATA = Shaded.class.getMethod( "arrayNewData", @@ -782,44 +901,64 @@ public final class ShadedRefs { Instance.class); ARRAY_GET = Shaded.class.getMethod( - "arrayGet", int.class, int.class, int.class, Instance.class); + "arrayGet", Object.class, int.class, int.class, Instance.class); + ARRAY_GET_REF = + Shaded.class.getMethod( + "arrayGetRef", Object.class, int.class, int.class, Instance.class); ARRAY_GET_S = Shaded.class.getMethod( - "arrayGetS", int.class, int.class, int.class, Instance.class); + "arrayGetS", Object.class, int.class, int.class, Instance.class); ARRAY_GET_U = Shaded.class.getMethod( - "arrayGetU", int.class, int.class, int.class, Instance.class); + "arrayGetU", Object.class, int.class, int.class, Instance.class); ARRAY_SET = Shaded.class.getMethod( "arraySet", - int.class, + Object.class, int.class, long.class, int.class, Instance.class); - ARRAY_LEN = Shaded.class.getMethod("arrayLen", int.class, Instance.class); + ARRAY_SET_REF = + Shaded.class.getMethod( + "arraySetRef", + Object.class, + int.class, + Object.class, + int.class, + Instance.class); + ARRAY_LEN = Shaded.class.getMethod("arrayLen", Object.class, Instance.class); ARRAY_FILL = Shaded.class.getMethod( "arrayFill", - int.class, + Object.class, int.class, long.class, int.class, int.class, Instance.class); - ARRAY_COPY = + ARRAY_FILL_REF = Shaded.class.getMethod( - "arrayCopy", + "arrayFillRef", + Object.class, int.class, + Object.class, int.class, int.class, + Instance.class); + ARRAY_COPY = + Shaded.class.getMethod( + "arrayCopy", + Object.class, + int.class, + Object.class, int.class, int.class, Instance.class); ARRAY_INIT_DATA = Shaded.class.getMethod( "arrayInitData", - int.class, + Object.class, int.class, int.class, int.class, @@ -829,7 +968,7 @@ public final class ShadedRefs { ARRAY_INIT_ELEM = Shaded.class.getMethod( "arrayInitElem", - int.class, + Object.class, int.class, int.class, int.class, @@ -838,28 +977,50 @@ public final class ShadedRefs { Instance.class); REF_TEST = Shaded.class.getMethod( - "refTest", int.class, int.class, int.class, Instance.class); + "refTest", Object.class, int.class, int.class, Instance.class); REF_TEST_NULL = Shaded.class.getMethod( - "refTestNull", int.class, int.class, int.class, Instance.class); + "refTestNull", Object.class, int.class, int.class, Instance.class); CAST_TEST = Shaded.class.getMethod( - "castTest", int.class, int.class, int.class, Instance.class); + "castTest", Object.class, int.class, int.class, Instance.class); CAST_TEST_NULL = Shaded.class.getMethod( - "castTestNull", int.class, int.class, int.class, Instance.class); + "castTestNull", Object.class, int.class, int.class, Instance.class); HEAP_TYPE_MATCH = Shaded.class.getMethod( "heapTypeMatch", + Object.class, + boolean.class, + int.class, + int.class, + Instance.class); + REF_TEST_INT = + Shaded.class.getMethod( + "refTestInt", int.class, int.class, int.class, Instance.class); + REF_TEST_NULL_INT = + Shaded.class.getMethod( + "refTestNullInt", int.class, int.class, int.class, Instance.class); + CAST_TEST_INT = + Shaded.class.getMethod( + "castTestInt", int.class, int.class, int.class, Instance.class); + CAST_TEST_NULL_INT = + Shaded.class.getMethod( + "castTestNullInt", int.class, int.class, int.class, Instance.class); + HEAP_TYPE_MATCH_INT = + Shaded.class.getMethod( + "heapTypeMatchInt", int.class, boolean.class, int.class, int.class, Instance.class); - REF_EQ = Shaded.class.getMethod("refEq", int.class, int.class, Instance.class); - REF_I31 = Shaded.class.getMethod("refI31", int.class, Instance.class); - I31_GET_S = Shaded.class.getMethod("i31GetS", int.class, Instance.class); - I31_GET_U = Shaded.class.getMethod("i31GetU", int.class, Instance.class); + REF_EQ = Shaded.class.getMethod("refEq", Object.class, Object.class); + REF_I31 = Shaded.class.getMethod("refI31", int.class); + I31_GET_S = Shaded.class.getMethod("i31GetS", Object.class); + I31_GET_U = Shaded.class.getMethod("i31GetU", Object.class); + ANY_CONVERT_EXTERN = Shaded.class.getMethod("anyConvertExtern", Object.class); + EXTERN_CONVERT_ANY = Shaded.class.getMethod("externConvertAny", Object.class); DATA_DROP = Shaded.class.getMethod("dataDrop", int.class, Instance.class); } catch (NoSuchMethodException e) { diff --git a/compiler/src/main/java/run/endive/compiler/internal/WasmAnalyzer.java b/compiler/src/main/java/run/endive/compiler/internal/WasmAnalyzer.java index 6c8522384..cf13e2af9 100644 --- a/compiler/src/main/java/run/endive/compiler/internal/WasmAnalyzer.java +++ b/compiler/src/main/java/run/endive/compiler/internal/WasmAnalyzer.java @@ -526,7 +526,8 @@ public AnalysisResult analyze(int funcId) { // BR_ON_NULL_CHECK: DUPs ref, pushes 1 if null, 0 if not null // JVM stack after check: [ref, 0_or_1] - result.add(new CompilerInstruction(CompilerOpCode.BR_ON_NULL_CHECK)); + result.add( + new CompilerInstruction(CompilerOpCode.BR_ON_NULL_CHECK, ref.id())); // IFEQ: pops boolean; if 0 (not null) -> jump to notNullLabel // JVM stack after IFEQ: [ref] @@ -555,7 +556,9 @@ public AnalysisResult analyze(int funcId) { // BR_ON_NON_NULL_CHECK: DUPs ref, pushes 1 if non-null, 0 if null // JVM stack after check: [ref, 0_or_1] - result.add(new CompilerInstruction(CompilerOpCode.BR_ON_NON_NULL_CHECK)); + result.add( + new CompilerInstruction( + CompilerOpCode.BR_ON_NON_NULL_CHECK, ref.id())); // IFEQ: if 0 (null) -> jump to nullLabel var nullLabel = nextLabel++; @@ -733,6 +736,31 @@ public AnalysisResult analyze(int funcId) { CompilerOpCode.CAST_TEST_NULL, castNullOperands)); break; } + case REF_TEST: + { + // ref.test: [ref] -> [i32] + var srcType = stack.peek(); + stack.popRef(); + var heapType = (int) ins.operand(0); + stack.push(ValType.I32); + long[] refTestOperands = {heapType, srcType.typeIdx()}; + result.add( + new CompilerInstruction(CompilerOpCode.REF_TEST, refTestOperands)); + break; + } + case REF_TEST_NULL: + { + // ref.test null: [ref] -> [i32] + var srcType = stack.peek(); + stack.popRef(); + var heapType = (int) ins.operand(0); + stack.push(ValType.I32); + long[] refTestNullOperands = {heapType, srcType.typeIdx()}; + result.add( + new CompilerInstruction( + CompilerOpCode.REF_TEST_NULL, refTestNullOperands)); + break; + } default: analyzeSimple(result, stack, ins, functionType, body); } @@ -1275,10 +1303,14 @@ private void analyzeSimple( .resolve(module.typeSection())); break; case REF_IS_NULL: - // [ref] -> [I32] - stack.popRef(); - stack.push(ValType.I32); - break; + { + // [ref] -> [I32] + var refType = stack.peek(); + stack.popRef(); + stack.push(ValType.I32); + out.add(new CompilerInstruction(CompilerOpCode.REF_IS_NULL, refType.id())); + return; + } case MEMORY_COPY: case MEMORY_FILL: case MEMORY_INIT: @@ -1290,27 +1322,48 @@ private void analyzeSimple( stack.pop(ValType.I32); break; case TABLE_FILL: - // [I32 ref I32] -> [] - stack.pop(ValType.I32); - stack.pop(stack.peek()); - stack.pop(ValType.I32); - break; + { + // [I32 ref I32] -> [] + stack.pop(ValType.I32); + var fillValType = stack.peek().resolve(module.typeSection()); + stack.pop(fillValType); + stack.pop(ValType.I32); + long[] fillOps = {ins.operand(0), fillValType.isObjectRef() ? 1 : 0}; + out.add(new CompilerInstruction(CompilerOpCode.TABLE_FILL, fillOps)); + return; + } case TABLE_GET: - // [I32] -> [ref] - stack.pop(ValType.I32); - stack.push(tableTypes.get((int) ins.operand(0))); - break; + { + // [I32] -> [ref] + stack.pop(ValType.I32); + var tableElemType = tableTypes.get((int) ins.operand(0)); + tableElemType.resolve(module.typeSection()); + stack.push(tableElemType); + long[] getOps = {ins.operand(0), tableElemType.isObjectRef() ? 1 : 0}; + out.add(new CompilerInstruction(CompilerOpCode.TABLE_GET, getOps)); + return; + } case TABLE_GROW: - // [ref I32] -> [I32] - stack.pop(ValType.I32); - stack.pop(tableTypes.get((int) ins.operand(0))); - stack.push(ValType.I32); - break; + { + // [ref I32] -> [I32] + stack.pop(ValType.I32); + var growValType = stack.peek().resolve(module.typeSection()); + stack.pop(growValType); + stack.push(ValType.I32); + long[] growOps = {ins.operand(0), growValType.isObjectRef() ? 1 : 0}; + out.add(new CompilerInstruction(CompilerOpCode.TABLE_GROW, growOps)); + return; + } case TABLE_SET: - // [I32 ref] -> [] - stack.pop(tableTypes.get((int) ins.operand(0))); - stack.pop(ValType.I32); - break; + { + // [I32 ref] -> [] + var setValType = stack.peek().resolve(module.typeSection()); + stack.pop(setValType); + stack.pop(ValType.I32); + long[] setOps = {ins.operand(0), setValType.isObjectRef() ? 1 : 0}; + out.add(new CompilerInstruction(CompilerOpCode.TABLE_SET, setOps)); + return; + } case CALL: // [p*] -> [r*] updateStack(stack, functionTypes.get((int) ins.operand(0))); @@ -1413,7 +1466,8 @@ private void analyzeSimple( var rt = stack.peek(); stack.popRef(); stack.push(valType(ValType.ID.Ref, rt.typeIdx())); - break; + out.add(new CompilerInstruction(CompilerOpCode.REF_AS_NON_NULL, rt.id())); + return; } case STRUCT_NEW: { @@ -1571,7 +1625,8 @@ private void analyzeSimple( ValType.builder() .withOpcode(ValType.ID.Ref) .withTypeIdx(ValType.TypeIdxCode.I31.code()) - .build()); + .build() + .resolve(module.typeSection())); break; case I31_GET_S: case I31_GET_U: @@ -1711,7 +1766,7 @@ private static List getFunctionTypes(WasmModule module) { return Stream.concat(importedFunctions, moduleFunctions).collect(toUnmodifiableList()); } - private static List getTableTypes(WasmModule module) { + static List getTableTypes(WasmModule module) { var importedTables = module.importSection().stream() .filter(TableImport.class::isInstance) diff --git a/compiler/src/test/resources/ApprovalTest.verifyLotsOfArgs.approved.template b/compiler/src/test/resources/ApprovalTest.verifyLotsOfArgs.approved.template index ea06e41f9..ebe8561dd 100644 --- a/compiler/src/test/resources/ApprovalTest.verifyLotsOfArgs.approved.template +++ b/compiler/src/test/resources/ApprovalTest.verifyLotsOfArgs.approved.template @@ -2,16 +2,44 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V ALOAD 0 ALOAD 1 PUTFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + ALOAD 0 + NEW run/endive/runtime/internal/CompilerInterpreterMachine + DUP + ALOAD 1 + ICONST_0 + NEWARRAY T_INT + INVOKESPECIAL run/endive/runtime/internal/CompilerInterpreterMachine. (Lrun/endive/runtime/Instance;[I)V + PUTFIELD run/endive/$gen/CompiledMachine.compilerInterpreterMachine : Lrun/endive/runtime/internal/CompilerInterpreterMachine; RETURN public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +47,36 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.compilerInterpreterMachine : Lrun/endive/runtime/internal/CompilerInterpreterMachine; + DUP + IFNONNULL L0 + POP + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + L0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/runtime/internal/CompilerInterpreterMachine.callWithRefs (I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + ARETURN + public static call_indirect_0(IIIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 5 @@ -116,12 +168,13 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime ATHROW L1 ALOAD 0 + ACONST_NULL ICONST_1 ILOAD 6 ALOAD 7 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callIndirect ([JIILrun/endive/runtime/Instance;)[J + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callIndirectWithRefs ([J[Ljava/lang/Object;IILrun/endive/runtime/Instance;)Lrun/endive/runtime/CallResult; ICONST_0 - LALOAD + INVOKEVIRTUAL run/endive/runtime/CallResult.longResult (I)J L2I IRETURN } @@ -134,186 +187,3669 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { FCONST_0 DCONST_0 ICONST_M1 - ICONST_M1 -#foreach ($const in $iconst) + ACONST_NULL ICONST_0 -#end - ILOAD 1 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V -#foreach ($store in $istore) - #set($store = 305 - $store) - ISTORE $store -#end - DSTORE 8 - FSTORE 7 - LSTORE 5 - ISTORE 4 - SIPUSH 300 - NEWARRAY T_LONG - DUP ICONST_0 - ILOAD 4 - I2L - LASTORE - DUP - ICONST_1 - LLOAD 5 - LASTORE - DUP - ICONST_2 - FLOAD 7 - INVOKESTATIC run/endive/wasm/types/Value.floatToLong (F)J - LASTORE - DUP - ICONST_3 - DLOAD 8 - INVOKESTATIC run/endive/wasm/types/Value.doubleToLong (D)J - LASTORE - DUP - ICONST_4 - ILOAD 10 - I2L - LASTORE - DUP - ICONST_5 - ILOAD 11 - I2L - LASTORE -#foreach ($splat in $splats1) - #set($const = $splat) - #set($load = $splat + 6) - DUP - BIPUSH $const - ILOAD $load - I2L - LASTORE -#end -#foreach ($splat in $splats2) - #set($const = $splat) - #set($load = $splat + 6) - DUP - SIPUSH $const - ILOAD $load - I2L - LASTORE -#end - ALOAD 2 - ALOAD 3 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 ([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - IRETURN - - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J - ALOAD 2 ICONST_0 - LALOAD - L2I - ALOAD 2 - ICONST_1 - LALOAD - L2I - ALOAD 1 - ALOAD 0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - I2L - LSTORE 3 - ICONST_1 - NEWARRAY T_LONG - DUP ICONST_0 - LLOAD 3 - LASTORE - ARETURN - - public static func_1([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - ALOAD 0 ICONST_0 - LALOAD - L2I - ISTORE 3 - ALOAD 0 - ICONST_1 - LALOAD - LSTORE 4 - ALOAD 0 - ICONST_2 - LALOAD - INVOKESTATIC run/endive/wasm/types/Value.longToFloat (J)F - FSTORE 6 - ALOAD 0 - ICONST_3 - LALOAD - INVOKESTATIC run/endive/wasm/types/Value.longToDouble (J)D - DSTORE 7 - ALOAD 0 - ICONST_4 - LALOAD - L2I - ISTORE 9 - ALOAD 0 - ICONST_5 -#foreach ($splat in $splats3) - #set($bipush = $splat) - #set($store = $splat + 4) - LALOAD - L2I - ISTORE $store - ALOAD 0 - BIPUSH $bipush -#end -#foreach ($splat in $splats4) - #set($bipush = $splat) - #set($store = $splat + 4) - LALOAD - L2I - ISTORE $store - ALOAD 0 - SIPUSH $bipush -#end - LALOAD - L2I - ISTORE 304 - ILOAD 3 - ILOAD 304 - IADD - IRETURN - - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J - ALOAD 2 - ALOAD 1 - ALOAD 0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 ([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - I2L - LSTORE 3 - ICONST_1 - NEWARRAY T_LONG - DUP ICONST_0 - LLOAD 3 - LASTORE - ARETURN -} - -final class run/endive/$gen/CompiledMachineMachineCall { - - public ()V - ALOAD 0 - INVOKESPECIAL java/lang/Object. ()V - RETURN - - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J - ALOAD 0 - ALOAD 1 - ALOAD 3 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ICONST_0 + ILOAD 1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V + ISTORE 305 + ISTORE 304 + ISTORE 303 + ISTORE 302 + ISTORE 301 + ISTORE 300 + ISTORE 299 + ISTORE 298 + ISTORE 297 + ISTORE 296 + ISTORE 295 + ISTORE 294 + ISTORE 293 + ISTORE 292 + ISTORE 291 + ISTORE 290 + ISTORE 289 + ISTORE 288 + ISTORE 287 + ISTORE 286 + ISTORE 285 + ISTORE 284 + ISTORE 283 + ISTORE 282 + ISTORE 281 + ISTORE 280 + ISTORE 279 + ISTORE 278 + ISTORE 277 + ISTORE 276 + ISTORE 275 + ISTORE 274 + ISTORE 273 + ISTORE 272 + ISTORE 271 + ISTORE 270 + ISTORE 269 + ISTORE 268 + ISTORE 267 + ISTORE 266 + ISTORE 265 + ISTORE 264 + ISTORE 263 + ISTORE 262 + ISTORE 261 + ISTORE 260 + ISTORE 259 + ISTORE 258 + ISTORE 257 + ISTORE 256 + ISTORE 255 + ISTORE 254 + ISTORE 253 + ISTORE 252 + ISTORE 251 + ISTORE 250 + ISTORE 249 + ISTORE 248 + ISTORE 247 + ISTORE 246 + ISTORE 245 + ISTORE 244 + ISTORE 243 + ISTORE 242 + ISTORE 241 + ISTORE 240 + ISTORE 239 + ISTORE 238 + ISTORE 237 + ISTORE 236 + ISTORE 235 + ISTORE 234 + ISTORE 233 + ISTORE 232 + ISTORE 231 + ISTORE 230 + ISTORE 229 + ISTORE 228 + ISTORE 227 + ISTORE 226 + ISTORE 225 + ISTORE 224 + ISTORE 223 + ISTORE 222 + ISTORE 221 + ISTORE 220 + ISTORE 219 + ISTORE 218 + ISTORE 217 + ISTORE 216 + ISTORE 215 + ISTORE 214 + ISTORE 213 + ISTORE 212 + ISTORE 211 + ISTORE 210 + ISTORE 209 + ISTORE 208 + ISTORE 207 + ISTORE 206 + ISTORE 205 + ISTORE 204 + ISTORE 203 + ISTORE 202 + ISTORE 201 + ISTORE 200 + ISTORE 199 + ISTORE 198 + ISTORE 197 + ISTORE 196 + ISTORE 195 + ISTORE 194 + ISTORE 193 + ISTORE 192 + ISTORE 191 + ISTORE 190 + ISTORE 189 + ISTORE 188 + ISTORE 187 + ISTORE 186 + ISTORE 185 + ISTORE 184 + ISTORE 183 + ISTORE 182 + ISTORE 181 + ISTORE 180 + ISTORE 179 + ISTORE 178 + ISTORE 177 + ISTORE 176 + ISTORE 175 + ISTORE 174 + ISTORE 173 + ISTORE 172 + ISTORE 171 + ISTORE 170 + ISTORE 169 + ISTORE 168 + ISTORE 167 + ISTORE 166 + ISTORE 165 + ISTORE 164 + ISTORE 163 + ISTORE 162 + ISTORE 161 + ISTORE 160 + ISTORE 159 + ISTORE 158 + ISTORE 157 + ISTORE 156 + ISTORE 155 + ISTORE 154 + ISTORE 153 + ISTORE 152 + ISTORE 151 + ISTORE 150 + ISTORE 149 + ISTORE 148 + ISTORE 147 + ISTORE 146 + ISTORE 145 + ISTORE 144 + ISTORE 143 + ISTORE 142 + ISTORE 141 + ISTORE 140 + ISTORE 139 + ISTORE 138 + ISTORE 137 + ISTORE 136 + ISTORE 135 + ISTORE 134 + ISTORE 133 + ISTORE 132 + ISTORE 131 + ISTORE 130 + ISTORE 129 + ISTORE 128 + ISTORE 127 + ISTORE 126 + ISTORE 125 + ISTORE 124 + ISTORE 123 + ISTORE 122 + ISTORE 121 + ISTORE 120 + ISTORE 119 + ISTORE 118 + ISTORE 117 + ISTORE 116 + ISTORE 115 + ISTORE 114 + ISTORE 113 + ISTORE 112 + ISTORE 111 + ISTORE 110 + ISTORE 109 + ISTORE 108 + ISTORE 107 + ISTORE 106 + ISTORE 105 + ISTORE 104 + ISTORE 103 + ISTORE 102 + ISTORE 101 + ISTORE 100 + ISTORE 99 + ISTORE 98 + ISTORE 97 + ISTORE 96 + ISTORE 95 + ISTORE 94 + ISTORE 93 + ISTORE 92 + ISTORE 91 + ISTORE 90 + ISTORE 89 + ISTORE 88 + ISTORE 87 + ISTORE 86 + ISTORE 85 + ISTORE 84 + ISTORE 83 + ISTORE 82 + ISTORE 81 + ISTORE 80 + ISTORE 79 + ISTORE 78 + ISTORE 77 + ISTORE 76 + ISTORE 75 + ISTORE 74 + ISTORE 73 + ISTORE 72 + ISTORE 71 + ISTORE 70 + ISTORE 69 + ISTORE 68 + ISTORE 67 + ISTORE 66 + ISTORE 65 + ISTORE 64 + ISTORE 63 + ISTORE 62 + ISTORE 61 + ISTORE 60 + ISTORE 59 + ISTORE 58 + ISTORE 57 + ISTORE 56 + ISTORE 55 + ISTORE 54 + ISTORE 53 + ISTORE 52 + ISTORE 51 + ISTORE 50 + ISTORE 49 + ISTORE 48 + ISTORE 47 + ISTORE 46 + ISTORE 45 + ISTORE 44 + ISTORE 43 + ISTORE 42 + ISTORE 41 + ISTORE 40 + ISTORE 39 + ISTORE 38 + ISTORE 37 + ISTORE 36 + ISTORE 35 + ISTORE 34 + ISTORE 33 + ISTORE 32 + ISTORE 31 + ISTORE 30 + ISTORE 29 + ISTORE 28 + ISTORE 27 + ISTORE 26 + ISTORE 25 + ISTORE 24 + ISTORE 23 + ISTORE 22 + ISTORE 21 + ISTORE 20 + ISTORE 19 + ISTORE 18 + ISTORE 17 + ISTORE 16 + ISTORE 15 + ISTORE 14 + ISTORE 13 + ISTORE 12 + ASTORE 11 + ISTORE 10 + DSTORE 8 + FSTORE 7 + LSTORE 5 + ISTORE 4 + SIPUSH 300 + NEWARRAY T_LONG + DUP + ICONST_0 + ILOAD 4 + I2L + LASTORE + DUP + ICONST_1 + LLOAD 5 + LASTORE + DUP + ICONST_2 + FLOAD 7 + INVOKESTATIC run/endive/wasm/types/Value.floatToLong (F)J + LASTORE + DUP + ICONST_3 + DLOAD 8 + INVOKESTATIC run/endive/wasm/types/Value.doubleToLong (D)J + LASTORE + DUP + ICONST_4 + ILOAD 10 + I2L + LASTORE + DUP + ICONST_5 + LCONST_0 + LASTORE + DUP + BIPUSH 6 + ILOAD 12 + I2L + LASTORE + DUP + BIPUSH 7 + ILOAD 13 + I2L + LASTORE + DUP + BIPUSH 8 + ILOAD 14 + I2L + LASTORE + DUP + BIPUSH 9 + ILOAD 15 + I2L + LASTORE + DUP + BIPUSH 10 + ILOAD 16 + I2L + LASTORE + DUP + BIPUSH 11 + ILOAD 17 + I2L + LASTORE + DUP + BIPUSH 12 + ILOAD 18 + I2L + LASTORE + DUP + BIPUSH 13 + ILOAD 19 + I2L + LASTORE + DUP + BIPUSH 14 + ILOAD 20 + I2L + LASTORE + DUP + BIPUSH 15 + ILOAD 21 + I2L + LASTORE + DUP + BIPUSH 16 + ILOAD 22 + I2L + LASTORE + DUP + BIPUSH 17 + ILOAD 23 + I2L + LASTORE + DUP + BIPUSH 18 + ILOAD 24 + I2L + LASTORE + DUP + BIPUSH 19 + ILOAD 25 + I2L + LASTORE + DUP + BIPUSH 20 + ILOAD 26 + I2L + LASTORE + DUP + BIPUSH 21 + ILOAD 27 + I2L + LASTORE + DUP + BIPUSH 22 + ILOAD 28 + I2L + LASTORE + DUP + BIPUSH 23 + ILOAD 29 + I2L + LASTORE + DUP + BIPUSH 24 + ILOAD 30 + I2L + LASTORE + DUP + BIPUSH 25 + ILOAD 31 + I2L + LASTORE + DUP + BIPUSH 26 + ILOAD 32 + I2L + LASTORE + DUP + BIPUSH 27 + ILOAD 33 + I2L + LASTORE + DUP + BIPUSH 28 + ILOAD 34 + I2L + LASTORE + DUP + BIPUSH 29 + ILOAD 35 + I2L + LASTORE + DUP + BIPUSH 30 + ILOAD 36 + I2L + LASTORE + DUP + BIPUSH 31 + ILOAD 37 + I2L + LASTORE + DUP + BIPUSH 32 + ILOAD 38 + I2L + LASTORE + DUP + BIPUSH 33 + ILOAD 39 + I2L + LASTORE + DUP + BIPUSH 34 + ILOAD 40 + I2L + LASTORE + DUP + BIPUSH 35 + ILOAD 41 + I2L + LASTORE + DUP + BIPUSH 36 + ILOAD 42 + I2L + LASTORE + DUP + BIPUSH 37 + ILOAD 43 + I2L + LASTORE + DUP + BIPUSH 38 + ILOAD 44 + I2L + LASTORE + DUP + BIPUSH 39 + ILOAD 45 + I2L + LASTORE + DUP + BIPUSH 40 + ILOAD 46 + I2L + LASTORE + DUP + BIPUSH 41 + ILOAD 47 + I2L + LASTORE + DUP + BIPUSH 42 + ILOAD 48 + I2L + LASTORE + DUP + BIPUSH 43 + ILOAD 49 + I2L + LASTORE + DUP + BIPUSH 44 + ILOAD 50 + I2L + LASTORE + DUP + BIPUSH 45 + ILOAD 51 + I2L + LASTORE + DUP + BIPUSH 46 + ILOAD 52 + I2L + LASTORE + DUP + BIPUSH 47 + ILOAD 53 + I2L + LASTORE + DUP + BIPUSH 48 + ILOAD 54 + I2L + LASTORE + DUP + BIPUSH 49 + ILOAD 55 + I2L + LASTORE + DUP + BIPUSH 50 + ILOAD 56 + I2L + LASTORE + DUP + BIPUSH 51 + ILOAD 57 + I2L + LASTORE + DUP + BIPUSH 52 + ILOAD 58 + I2L + LASTORE + DUP + BIPUSH 53 + ILOAD 59 + I2L + LASTORE + DUP + BIPUSH 54 + ILOAD 60 + I2L + LASTORE + DUP + BIPUSH 55 + ILOAD 61 + I2L + LASTORE + DUP + BIPUSH 56 + ILOAD 62 + I2L + LASTORE + DUP + BIPUSH 57 + ILOAD 63 + I2L + LASTORE + DUP + BIPUSH 58 + ILOAD 64 + I2L + LASTORE + DUP + BIPUSH 59 + ILOAD 65 + I2L + LASTORE + DUP + BIPUSH 60 + ILOAD 66 + I2L + LASTORE + DUP + BIPUSH 61 + ILOAD 67 + I2L + LASTORE + DUP + BIPUSH 62 + ILOAD 68 + I2L + LASTORE + DUP + BIPUSH 63 + ILOAD 69 + I2L + LASTORE + DUP + BIPUSH 64 + ILOAD 70 + I2L + LASTORE + DUP + BIPUSH 65 + ILOAD 71 + I2L + LASTORE + DUP + BIPUSH 66 + ILOAD 72 + I2L + LASTORE + DUP + BIPUSH 67 + ILOAD 73 + I2L + LASTORE + DUP + BIPUSH 68 + ILOAD 74 + I2L + LASTORE + DUP + BIPUSH 69 + ILOAD 75 + I2L + LASTORE + DUP + BIPUSH 70 + ILOAD 76 + I2L + LASTORE + DUP + BIPUSH 71 + ILOAD 77 + I2L + LASTORE + DUP + BIPUSH 72 + ILOAD 78 + I2L + LASTORE + DUP + BIPUSH 73 + ILOAD 79 + I2L + LASTORE + DUP + BIPUSH 74 + ILOAD 80 + I2L + LASTORE + DUP + BIPUSH 75 + ILOAD 81 + I2L + LASTORE + DUP + BIPUSH 76 + ILOAD 82 + I2L + LASTORE + DUP + BIPUSH 77 + ILOAD 83 + I2L + LASTORE + DUP + BIPUSH 78 + ILOAD 84 + I2L + LASTORE + DUP + BIPUSH 79 + ILOAD 85 + I2L + LASTORE + DUP + BIPUSH 80 + ILOAD 86 + I2L + LASTORE + DUP + BIPUSH 81 + ILOAD 87 + I2L + LASTORE + DUP + BIPUSH 82 + ILOAD 88 + I2L + LASTORE + DUP + BIPUSH 83 + ILOAD 89 + I2L + LASTORE + DUP + BIPUSH 84 + ILOAD 90 + I2L + LASTORE + DUP + BIPUSH 85 + ILOAD 91 + I2L + LASTORE + DUP + BIPUSH 86 + ILOAD 92 + I2L + LASTORE + DUP + BIPUSH 87 + ILOAD 93 + I2L + LASTORE + DUP + BIPUSH 88 + ILOAD 94 + I2L + LASTORE + DUP + BIPUSH 89 + ILOAD 95 + I2L + LASTORE + DUP + BIPUSH 90 + ILOAD 96 + I2L + LASTORE + DUP + BIPUSH 91 + ILOAD 97 + I2L + LASTORE + DUP + BIPUSH 92 + ILOAD 98 + I2L + LASTORE + DUP + BIPUSH 93 + ILOAD 99 + I2L + LASTORE + DUP + BIPUSH 94 + ILOAD 100 + I2L + LASTORE + DUP + BIPUSH 95 + ILOAD 101 + I2L + LASTORE + DUP + BIPUSH 96 + ILOAD 102 + I2L + LASTORE + DUP + BIPUSH 97 + ILOAD 103 + I2L + LASTORE + DUP + BIPUSH 98 + ILOAD 104 + I2L + LASTORE + DUP + BIPUSH 99 + ILOAD 105 + I2L + LASTORE + DUP + BIPUSH 100 + ILOAD 106 + I2L + LASTORE + DUP + BIPUSH 101 + ILOAD 107 + I2L + LASTORE + DUP + BIPUSH 102 + ILOAD 108 + I2L + LASTORE + DUP + BIPUSH 103 + ILOAD 109 + I2L + LASTORE + DUP + BIPUSH 104 + ILOAD 110 + I2L + LASTORE + DUP + BIPUSH 105 + ILOAD 111 + I2L + LASTORE + DUP + BIPUSH 106 + ILOAD 112 + I2L + LASTORE + DUP + BIPUSH 107 + ILOAD 113 + I2L + LASTORE + DUP + BIPUSH 108 + ILOAD 114 + I2L + LASTORE + DUP + BIPUSH 109 + ILOAD 115 + I2L + LASTORE + DUP + BIPUSH 110 + ILOAD 116 + I2L + LASTORE + DUP + BIPUSH 111 + ILOAD 117 + I2L + LASTORE + DUP + BIPUSH 112 + ILOAD 118 + I2L + LASTORE + DUP + BIPUSH 113 + ILOAD 119 + I2L + LASTORE + DUP + BIPUSH 114 + ILOAD 120 + I2L + LASTORE + DUP + BIPUSH 115 + ILOAD 121 + I2L + LASTORE + DUP + BIPUSH 116 + ILOAD 122 + I2L + LASTORE + DUP + BIPUSH 117 + ILOAD 123 + I2L + LASTORE + DUP + BIPUSH 118 + ILOAD 124 + I2L + LASTORE + DUP + BIPUSH 119 + ILOAD 125 + I2L + LASTORE + DUP + BIPUSH 120 + ILOAD 126 + I2L + LASTORE + DUP + BIPUSH 121 + ILOAD 127 + I2L + LASTORE + DUP + BIPUSH 122 + ILOAD 128 + I2L + LASTORE + DUP + BIPUSH 123 + ILOAD 129 + I2L + LASTORE + DUP + BIPUSH 124 + ILOAD 130 + I2L + LASTORE + DUP + BIPUSH 125 + ILOAD 131 + I2L + LASTORE + DUP + BIPUSH 126 + ILOAD 132 + I2L + LASTORE + DUP + BIPUSH 127 + ILOAD 133 + I2L + LASTORE + DUP + SIPUSH 128 + ILOAD 134 + I2L + LASTORE + DUP + SIPUSH 129 + ILOAD 135 + I2L + LASTORE + DUP + SIPUSH 130 + ILOAD 136 + I2L + LASTORE + DUP + SIPUSH 131 + ILOAD 137 + I2L + LASTORE + DUP + SIPUSH 132 + ILOAD 138 + I2L + LASTORE + DUP + SIPUSH 133 + ILOAD 139 + I2L + LASTORE + DUP + SIPUSH 134 + ILOAD 140 + I2L + LASTORE + DUP + SIPUSH 135 + ILOAD 141 + I2L + LASTORE + DUP + SIPUSH 136 + ILOAD 142 + I2L + LASTORE + DUP + SIPUSH 137 + ILOAD 143 + I2L + LASTORE + DUP + SIPUSH 138 + ILOAD 144 + I2L + LASTORE + DUP + SIPUSH 139 + ILOAD 145 + I2L + LASTORE + DUP + SIPUSH 140 + ILOAD 146 + I2L + LASTORE + DUP + SIPUSH 141 + ILOAD 147 + I2L + LASTORE + DUP + SIPUSH 142 + ILOAD 148 + I2L + LASTORE + DUP + SIPUSH 143 + ILOAD 149 + I2L + LASTORE + DUP + SIPUSH 144 + ILOAD 150 + I2L + LASTORE + DUP + SIPUSH 145 + ILOAD 151 + I2L + LASTORE + DUP + SIPUSH 146 + ILOAD 152 + I2L + LASTORE + DUP + SIPUSH 147 + ILOAD 153 + I2L + LASTORE + DUP + SIPUSH 148 + ILOAD 154 + I2L + LASTORE + DUP + SIPUSH 149 + ILOAD 155 + I2L + LASTORE + DUP + SIPUSH 150 + ILOAD 156 + I2L + LASTORE + DUP + SIPUSH 151 + ILOAD 157 + I2L + LASTORE + DUP + SIPUSH 152 + ILOAD 158 + I2L + LASTORE + DUP + SIPUSH 153 + ILOAD 159 + I2L + LASTORE + DUP + SIPUSH 154 + ILOAD 160 + I2L + LASTORE + DUP + SIPUSH 155 + ILOAD 161 + I2L + LASTORE + DUP + SIPUSH 156 + ILOAD 162 + I2L + LASTORE + DUP + SIPUSH 157 + ILOAD 163 + I2L + LASTORE + DUP + SIPUSH 158 + ILOAD 164 + I2L + LASTORE + DUP + SIPUSH 159 + ILOAD 165 + I2L + LASTORE + DUP + SIPUSH 160 + ILOAD 166 + I2L + LASTORE + DUP + SIPUSH 161 + ILOAD 167 + I2L + LASTORE + DUP + SIPUSH 162 + ILOAD 168 + I2L + LASTORE + DUP + SIPUSH 163 + ILOAD 169 + I2L + LASTORE + DUP + SIPUSH 164 + ILOAD 170 + I2L + LASTORE + DUP + SIPUSH 165 + ILOAD 171 + I2L + LASTORE + DUP + SIPUSH 166 + ILOAD 172 + I2L + LASTORE + DUP + SIPUSH 167 + ILOAD 173 + I2L + LASTORE + DUP + SIPUSH 168 + ILOAD 174 + I2L + LASTORE + DUP + SIPUSH 169 + ILOAD 175 + I2L + LASTORE + DUP + SIPUSH 170 + ILOAD 176 + I2L + LASTORE + DUP + SIPUSH 171 + ILOAD 177 + I2L + LASTORE + DUP + SIPUSH 172 + ILOAD 178 + I2L + LASTORE + DUP + SIPUSH 173 + ILOAD 179 + I2L + LASTORE + DUP + SIPUSH 174 + ILOAD 180 + I2L + LASTORE + DUP + SIPUSH 175 + ILOAD 181 + I2L + LASTORE + DUP + SIPUSH 176 + ILOAD 182 + I2L + LASTORE + DUP + SIPUSH 177 + ILOAD 183 + I2L + LASTORE + DUP + SIPUSH 178 + ILOAD 184 + I2L + LASTORE + DUP + SIPUSH 179 + ILOAD 185 + I2L + LASTORE + DUP + SIPUSH 180 + ILOAD 186 + I2L + LASTORE + DUP + SIPUSH 181 + ILOAD 187 + I2L + LASTORE + DUP + SIPUSH 182 + ILOAD 188 + I2L + LASTORE + DUP + SIPUSH 183 + ILOAD 189 + I2L + LASTORE + DUP + SIPUSH 184 + ILOAD 190 + I2L + LASTORE + DUP + SIPUSH 185 + ILOAD 191 + I2L + LASTORE + DUP + SIPUSH 186 + ILOAD 192 + I2L + LASTORE + DUP + SIPUSH 187 + ILOAD 193 + I2L + LASTORE + DUP + SIPUSH 188 + ILOAD 194 + I2L + LASTORE + DUP + SIPUSH 189 + ILOAD 195 + I2L + LASTORE + DUP + SIPUSH 190 + ILOAD 196 + I2L + LASTORE + DUP + SIPUSH 191 + ILOAD 197 + I2L + LASTORE + DUP + SIPUSH 192 + ILOAD 198 + I2L + LASTORE + DUP + SIPUSH 193 + ILOAD 199 + I2L + LASTORE + DUP + SIPUSH 194 + ILOAD 200 + I2L + LASTORE + DUP + SIPUSH 195 + ILOAD 201 + I2L + LASTORE + DUP + SIPUSH 196 + ILOAD 202 + I2L + LASTORE + DUP + SIPUSH 197 + ILOAD 203 + I2L + LASTORE + DUP + SIPUSH 198 + ILOAD 204 + I2L + LASTORE + DUP + SIPUSH 199 + ILOAD 205 + I2L + LASTORE + DUP + SIPUSH 200 + ILOAD 206 + I2L + LASTORE + DUP + SIPUSH 201 + ILOAD 207 + I2L + LASTORE + DUP + SIPUSH 202 + ILOAD 208 + I2L + LASTORE + DUP + SIPUSH 203 + ILOAD 209 + I2L + LASTORE + DUP + SIPUSH 204 + ILOAD 210 + I2L + LASTORE + DUP + SIPUSH 205 + ILOAD 211 + I2L + LASTORE + DUP + SIPUSH 206 + ILOAD 212 + I2L + LASTORE + DUP + SIPUSH 207 + ILOAD 213 + I2L + LASTORE + DUP + SIPUSH 208 + ILOAD 214 + I2L + LASTORE + DUP + SIPUSH 209 + ILOAD 215 + I2L + LASTORE + DUP + SIPUSH 210 + ILOAD 216 + I2L + LASTORE + DUP + SIPUSH 211 + ILOAD 217 + I2L + LASTORE + DUP + SIPUSH 212 + ILOAD 218 + I2L + LASTORE + DUP + SIPUSH 213 + ILOAD 219 + I2L + LASTORE + DUP + SIPUSH 214 + ILOAD 220 + I2L + LASTORE + DUP + SIPUSH 215 + ILOAD 221 + I2L + LASTORE + DUP + SIPUSH 216 + ILOAD 222 + I2L + LASTORE + DUP + SIPUSH 217 + ILOAD 223 + I2L + LASTORE + DUP + SIPUSH 218 + ILOAD 224 + I2L + LASTORE + DUP + SIPUSH 219 + ILOAD 225 + I2L + LASTORE + DUP + SIPUSH 220 + ILOAD 226 + I2L + LASTORE + DUP + SIPUSH 221 + ILOAD 227 + I2L + LASTORE + DUP + SIPUSH 222 + ILOAD 228 + I2L + LASTORE + DUP + SIPUSH 223 + ILOAD 229 + I2L + LASTORE + DUP + SIPUSH 224 + ILOAD 230 + I2L + LASTORE + DUP + SIPUSH 225 + ILOAD 231 + I2L + LASTORE + DUP + SIPUSH 226 + ILOAD 232 + I2L + LASTORE + DUP + SIPUSH 227 + ILOAD 233 + I2L + LASTORE + DUP + SIPUSH 228 + ILOAD 234 + I2L + LASTORE + DUP + SIPUSH 229 + ILOAD 235 + I2L + LASTORE + DUP + SIPUSH 230 + ILOAD 236 + I2L + LASTORE + DUP + SIPUSH 231 + ILOAD 237 + I2L + LASTORE + DUP + SIPUSH 232 + ILOAD 238 + I2L + LASTORE + DUP + SIPUSH 233 + ILOAD 239 + I2L + LASTORE + DUP + SIPUSH 234 + ILOAD 240 + I2L + LASTORE + DUP + SIPUSH 235 + ILOAD 241 + I2L + LASTORE + DUP + SIPUSH 236 + ILOAD 242 + I2L + LASTORE + DUP + SIPUSH 237 + ILOAD 243 + I2L + LASTORE + DUP + SIPUSH 238 + ILOAD 244 + I2L + LASTORE + DUP + SIPUSH 239 + ILOAD 245 + I2L + LASTORE + DUP + SIPUSH 240 + ILOAD 246 + I2L + LASTORE + DUP + SIPUSH 241 + ILOAD 247 + I2L + LASTORE + DUP + SIPUSH 242 + ILOAD 248 + I2L + LASTORE + DUP + SIPUSH 243 + ILOAD 249 + I2L + LASTORE + DUP + SIPUSH 244 + ILOAD 250 + I2L + LASTORE + DUP + SIPUSH 245 + ILOAD 251 + I2L + LASTORE + DUP + SIPUSH 246 + ILOAD 252 + I2L + LASTORE + DUP + SIPUSH 247 + ILOAD 253 + I2L + LASTORE + DUP + SIPUSH 248 + ILOAD 254 + I2L + LASTORE + DUP + SIPUSH 249 + ILOAD 255 + I2L + LASTORE + DUP + SIPUSH 250 + ILOAD 256 + I2L + LASTORE + DUP + SIPUSH 251 + ILOAD 257 + I2L + LASTORE + DUP + SIPUSH 252 + ILOAD 258 + I2L + LASTORE + DUP + SIPUSH 253 + ILOAD 259 + I2L + LASTORE + DUP + SIPUSH 254 + ILOAD 260 + I2L + LASTORE + DUP + SIPUSH 255 + ILOAD 261 + I2L + LASTORE + DUP + SIPUSH 256 + ILOAD 262 + I2L + LASTORE + DUP + SIPUSH 257 + ILOAD 263 + I2L + LASTORE + DUP + SIPUSH 258 + ILOAD 264 + I2L + LASTORE + DUP + SIPUSH 259 + ILOAD 265 + I2L + LASTORE + DUP + SIPUSH 260 + ILOAD 266 + I2L + LASTORE + DUP + SIPUSH 261 + ILOAD 267 + I2L + LASTORE + DUP + SIPUSH 262 + ILOAD 268 + I2L + LASTORE + DUP + SIPUSH 263 + ILOAD 269 + I2L + LASTORE + DUP + SIPUSH 264 + ILOAD 270 + I2L + LASTORE + DUP + SIPUSH 265 + ILOAD 271 + I2L + LASTORE + DUP + SIPUSH 266 + ILOAD 272 + I2L + LASTORE + DUP + SIPUSH 267 + ILOAD 273 + I2L + LASTORE + DUP + SIPUSH 268 + ILOAD 274 + I2L + LASTORE + DUP + SIPUSH 269 + ILOAD 275 + I2L + LASTORE + DUP + SIPUSH 270 + ILOAD 276 + I2L + LASTORE + DUP + SIPUSH 271 + ILOAD 277 + I2L + LASTORE + DUP + SIPUSH 272 + ILOAD 278 + I2L + LASTORE + DUP + SIPUSH 273 + ILOAD 279 + I2L + LASTORE + DUP + SIPUSH 274 + ILOAD 280 + I2L + LASTORE + DUP + SIPUSH 275 + ILOAD 281 + I2L + LASTORE + DUP + SIPUSH 276 + ILOAD 282 + I2L + LASTORE + DUP + SIPUSH 277 + ILOAD 283 + I2L + LASTORE + DUP + SIPUSH 278 + ILOAD 284 + I2L + LASTORE + DUP + SIPUSH 279 + ILOAD 285 + I2L + LASTORE + DUP + SIPUSH 280 + ILOAD 286 + I2L + LASTORE + DUP + SIPUSH 281 + ILOAD 287 + I2L + LASTORE + DUP + SIPUSH 282 + ILOAD 288 + I2L + LASTORE + DUP + SIPUSH 283 + ILOAD 289 + I2L + LASTORE + DUP + SIPUSH 284 + ILOAD 290 + I2L + LASTORE + DUP + SIPUSH 285 + ILOAD 291 + I2L + LASTORE + DUP + SIPUSH 286 + ILOAD 292 + I2L + LASTORE + DUP + SIPUSH 287 + ILOAD 293 + I2L + LASTORE + DUP + SIPUSH 288 + ILOAD 294 + I2L + LASTORE + DUP + SIPUSH 289 + ILOAD 295 + I2L + LASTORE + DUP + SIPUSH 290 + ILOAD 296 + I2L + LASTORE + DUP + SIPUSH 291 + ILOAD 297 + I2L + LASTORE + DUP + SIPUSH 292 + ILOAD 298 + I2L + LASTORE + DUP + SIPUSH 293 + ILOAD 299 + I2L + LASTORE + DUP + SIPUSH 294 + ILOAD 300 + I2L + LASTORE + DUP + SIPUSH 295 + ILOAD 301 + I2L + LASTORE + DUP + SIPUSH 296 + ILOAD 302 + I2L + LASTORE + DUP + SIPUSH 297 + ILOAD 303 + I2L + LASTORE + DUP + SIPUSH 298 + ILOAD 304 + I2L + LASTORE + DUP + SIPUSH 299 + ILOAD 305 + I2L + LASTORE + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 ([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + IRETURN + + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J + ALOAD 2 + ICONST_0 + LALOAD + L2I + ALOAD 2 + ICONST_1 + LALOAD + L2I + ALOAD 1 + ALOAD 0 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + I2L + LSTORE 4 + ICONST_1 + NEWARRAY T_LONG + DUP + ICONST_0 + LLOAD 4 + LASTORE + ARETURN + + public static func_1([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + ALOAD 0 + ICONST_0 + LALOAD + L2I + ISTORE 3 + ALOAD 0 + ICONST_1 + LALOAD + LSTORE 4 + ALOAD 0 + ICONST_2 + LALOAD + INVOKESTATIC run/endive/wasm/types/Value.longToFloat (J)F + FSTORE 6 + ALOAD 0 + ICONST_3 + LALOAD + INVOKESTATIC run/endive/wasm/types/Value.longToDouble (J)D + DSTORE 7 + ALOAD 0 + ICONST_4 + LALOAD + L2I + ISTORE 9 + ACONST_NULL + ASTORE 10 + ALOAD 0 + BIPUSH 6 + LALOAD + L2I + ISTORE 11 + ALOAD 0 + BIPUSH 7 + LALOAD + L2I + ISTORE 12 + ALOAD 0 + BIPUSH 8 + LALOAD + L2I + ISTORE 13 + ALOAD 0 + BIPUSH 9 + LALOAD + L2I + ISTORE 14 + ALOAD 0 + BIPUSH 10 + LALOAD + L2I + ISTORE 15 + ALOAD 0 + BIPUSH 11 + LALOAD + L2I + ISTORE 16 + ALOAD 0 + BIPUSH 12 + LALOAD + L2I + ISTORE 17 + ALOAD 0 + BIPUSH 13 + LALOAD + L2I + ISTORE 18 + ALOAD 0 + BIPUSH 14 + LALOAD + L2I + ISTORE 19 + ALOAD 0 + BIPUSH 15 + LALOAD + L2I + ISTORE 20 + ALOAD 0 + BIPUSH 16 + LALOAD + L2I + ISTORE 21 + ALOAD 0 + BIPUSH 17 + LALOAD + L2I + ISTORE 22 + ALOAD 0 + BIPUSH 18 + LALOAD + L2I + ISTORE 23 + ALOAD 0 + BIPUSH 19 + LALOAD + L2I + ISTORE 24 + ALOAD 0 + BIPUSH 20 + LALOAD + L2I + ISTORE 25 + ALOAD 0 + BIPUSH 21 + LALOAD + L2I + ISTORE 26 + ALOAD 0 + BIPUSH 22 + LALOAD + L2I + ISTORE 27 + ALOAD 0 + BIPUSH 23 + LALOAD + L2I + ISTORE 28 + ALOAD 0 + BIPUSH 24 + LALOAD + L2I + ISTORE 29 + ALOAD 0 + BIPUSH 25 + LALOAD + L2I + ISTORE 30 + ALOAD 0 + BIPUSH 26 + LALOAD + L2I + ISTORE 31 + ALOAD 0 + BIPUSH 27 + LALOAD + L2I + ISTORE 32 + ALOAD 0 + BIPUSH 28 + LALOAD + L2I + ISTORE 33 + ALOAD 0 + BIPUSH 29 + LALOAD + L2I + ISTORE 34 + ALOAD 0 + BIPUSH 30 + LALOAD + L2I + ISTORE 35 + ALOAD 0 + BIPUSH 31 + LALOAD + L2I + ISTORE 36 + ALOAD 0 + BIPUSH 32 + LALOAD + L2I + ISTORE 37 + ALOAD 0 + BIPUSH 33 + LALOAD + L2I + ISTORE 38 + ALOAD 0 + BIPUSH 34 + LALOAD + L2I + ISTORE 39 + ALOAD 0 + BIPUSH 35 + LALOAD + L2I + ISTORE 40 + ALOAD 0 + BIPUSH 36 + LALOAD + L2I + ISTORE 41 + ALOAD 0 + BIPUSH 37 + LALOAD + L2I + ISTORE 42 + ALOAD 0 + BIPUSH 38 + LALOAD + L2I + ISTORE 43 + ALOAD 0 + BIPUSH 39 + LALOAD + L2I + ISTORE 44 + ALOAD 0 + BIPUSH 40 + LALOAD + L2I + ISTORE 45 + ALOAD 0 + BIPUSH 41 + LALOAD + L2I + ISTORE 46 + ALOAD 0 + BIPUSH 42 + LALOAD + L2I + ISTORE 47 + ALOAD 0 + BIPUSH 43 + LALOAD + L2I + ISTORE 48 + ALOAD 0 + BIPUSH 44 + LALOAD + L2I + ISTORE 49 + ALOAD 0 + BIPUSH 45 + LALOAD + L2I + ISTORE 50 + ALOAD 0 + BIPUSH 46 + LALOAD + L2I + ISTORE 51 + ALOAD 0 + BIPUSH 47 + LALOAD + L2I + ISTORE 52 + ALOAD 0 + BIPUSH 48 + LALOAD + L2I + ISTORE 53 + ALOAD 0 + BIPUSH 49 + LALOAD + L2I + ISTORE 54 + ALOAD 0 + BIPUSH 50 + LALOAD + L2I + ISTORE 55 + ALOAD 0 + BIPUSH 51 + LALOAD + L2I + ISTORE 56 + ALOAD 0 + BIPUSH 52 + LALOAD + L2I + ISTORE 57 + ALOAD 0 + BIPUSH 53 + LALOAD + L2I + ISTORE 58 + ALOAD 0 + BIPUSH 54 + LALOAD + L2I + ISTORE 59 + ALOAD 0 + BIPUSH 55 + LALOAD + L2I + ISTORE 60 + ALOAD 0 + BIPUSH 56 + LALOAD + L2I + ISTORE 61 + ALOAD 0 + BIPUSH 57 + LALOAD + L2I + ISTORE 62 + ALOAD 0 + BIPUSH 58 + LALOAD + L2I + ISTORE 63 + ALOAD 0 + BIPUSH 59 + LALOAD + L2I + ISTORE 64 + ALOAD 0 + BIPUSH 60 + LALOAD + L2I + ISTORE 65 + ALOAD 0 + BIPUSH 61 + LALOAD + L2I + ISTORE 66 + ALOAD 0 + BIPUSH 62 + LALOAD + L2I + ISTORE 67 + ALOAD 0 + BIPUSH 63 + LALOAD + L2I + ISTORE 68 + ALOAD 0 + BIPUSH 64 + LALOAD + L2I + ISTORE 69 + ALOAD 0 + BIPUSH 65 + LALOAD + L2I + ISTORE 70 + ALOAD 0 + BIPUSH 66 + LALOAD + L2I + ISTORE 71 + ALOAD 0 + BIPUSH 67 + LALOAD + L2I + ISTORE 72 + ALOAD 0 + BIPUSH 68 + LALOAD + L2I + ISTORE 73 + ALOAD 0 + BIPUSH 69 + LALOAD + L2I + ISTORE 74 + ALOAD 0 + BIPUSH 70 + LALOAD + L2I + ISTORE 75 + ALOAD 0 + BIPUSH 71 + LALOAD + L2I + ISTORE 76 + ALOAD 0 + BIPUSH 72 + LALOAD + L2I + ISTORE 77 + ALOAD 0 + BIPUSH 73 + LALOAD + L2I + ISTORE 78 + ALOAD 0 + BIPUSH 74 + LALOAD + L2I + ISTORE 79 + ALOAD 0 + BIPUSH 75 + LALOAD + L2I + ISTORE 80 + ALOAD 0 + BIPUSH 76 + LALOAD + L2I + ISTORE 81 + ALOAD 0 + BIPUSH 77 + LALOAD + L2I + ISTORE 82 + ALOAD 0 + BIPUSH 78 + LALOAD + L2I + ISTORE 83 + ALOAD 0 + BIPUSH 79 + LALOAD + L2I + ISTORE 84 + ALOAD 0 + BIPUSH 80 + LALOAD + L2I + ISTORE 85 + ALOAD 0 + BIPUSH 81 + LALOAD + L2I + ISTORE 86 + ALOAD 0 + BIPUSH 82 + LALOAD + L2I + ISTORE 87 + ALOAD 0 + BIPUSH 83 + LALOAD + L2I + ISTORE 88 + ALOAD 0 + BIPUSH 84 + LALOAD + L2I + ISTORE 89 + ALOAD 0 + BIPUSH 85 + LALOAD + L2I + ISTORE 90 + ALOAD 0 + BIPUSH 86 + LALOAD + L2I + ISTORE 91 + ALOAD 0 + BIPUSH 87 + LALOAD + L2I + ISTORE 92 + ALOAD 0 + BIPUSH 88 + LALOAD + L2I + ISTORE 93 + ALOAD 0 + BIPUSH 89 + LALOAD + L2I + ISTORE 94 + ALOAD 0 + BIPUSH 90 + LALOAD + L2I + ISTORE 95 + ALOAD 0 + BIPUSH 91 + LALOAD + L2I + ISTORE 96 + ALOAD 0 + BIPUSH 92 + LALOAD + L2I + ISTORE 97 + ALOAD 0 + BIPUSH 93 + LALOAD + L2I + ISTORE 98 + ALOAD 0 + BIPUSH 94 + LALOAD + L2I + ISTORE 99 + ALOAD 0 + BIPUSH 95 + LALOAD + L2I + ISTORE 100 + ALOAD 0 + BIPUSH 96 + LALOAD + L2I + ISTORE 101 + ALOAD 0 + BIPUSH 97 + LALOAD + L2I + ISTORE 102 + ALOAD 0 + BIPUSH 98 + LALOAD + L2I + ISTORE 103 + ALOAD 0 + BIPUSH 99 + LALOAD + L2I + ISTORE 104 + ALOAD 0 + BIPUSH 100 + LALOAD + L2I + ISTORE 105 + ALOAD 0 + BIPUSH 101 + LALOAD + L2I + ISTORE 106 + ALOAD 0 + BIPUSH 102 + LALOAD + L2I + ISTORE 107 + ALOAD 0 + BIPUSH 103 + LALOAD + L2I + ISTORE 108 + ALOAD 0 + BIPUSH 104 + LALOAD + L2I + ISTORE 109 + ALOAD 0 + BIPUSH 105 + LALOAD + L2I + ISTORE 110 + ALOAD 0 + BIPUSH 106 + LALOAD + L2I + ISTORE 111 + ALOAD 0 + BIPUSH 107 + LALOAD + L2I + ISTORE 112 + ALOAD 0 + BIPUSH 108 + LALOAD + L2I + ISTORE 113 + ALOAD 0 + BIPUSH 109 + LALOAD + L2I + ISTORE 114 + ALOAD 0 + BIPUSH 110 + LALOAD + L2I + ISTORE 115 + ALOAD 0 + BIPUSH 111 + LALOAD + L2I + ISTORE 116 + ALOAD 0 + BIPUSH 112 + LALOAD + L2I + ISTORE 117 + ALOAD 0 + BIPUSH 113 + LALOAD + L2I + ISTORE 118 + ALOAD 0 + BIPUSH 114 + LALOAD + L2I + ISTORE 119 + ALOAD 0 + BIPUSH 115 + LALOAD + L2I + ISTORE 120 + ALOAD 0 + BIPUSH 116 + LALOAD + L2I + ISTORE 121 + ALOAD 0 + BIPUSH 117 + LALOAD + L2I + ISTORE 122 + ALOAD 0 + BIPUSH 118 + LALOAD + L2I + ISTORE 123 + ALOAD 0 + BIPUSH 119 + LALOAD + L2I + ISTORE 124 + ALOAD 0 + BIPUSH 120 + LALOAD + L2I + ISTORE 125 + ALOAD 0 + BIPUSH 121 + LALOAD + L2I + ISTORE 126 + ALOAD 0 + BIPUSH 122 + LALOAD + L2I + ISTORE 127 + ALOAD 0 + BIPUSH 123 + LALOAD + L2I + ISTORE 128 + ALOAD 0 + BIPUSH 124 + LALOAD + L2I + ISTORE 129 + ALOAD 0 + BIPUSH 125 + LALOAD + L2I + ISTORE 130 + ALOAD 0 + BIPUSH 126 + LALOAD + L2I + ISTORE 131 + ALOAD 0 + BIPUSH 127 + LALOAD + L2I + ISTORE 132 + ALOAD 0 + SIPUSH 128 + LALOAD + L2I + ISTORE 133 + ALOAD 0 + SIPUSH 129 + LALOAD + L2I + ISTORE 134 + ALOAD 0 + SIPUSH 130 + LALOAD + L2I + ISTORE 135 + ALOAD 0 + SIPUSH 131 + LALOAD + L2I + ISTORE 136 + ALOAD 0 + SIPUSH 132 + LALOAD + L2I + ISTORE 137 + ALOAD 0 + SIPUSH 133 + LALOAD + L2I + ISTORE 138 + ALOAD 0 + SIPUSH 134 + LALOAD + L2I + ISTORE 139 + ALOAD 0 + SIPUSH 135 + LALOAD + L2I + ISTORE 140 + ALOAD 0 + SIPUSH 136 + LALOAD + L2I + ISTORE 141 + ALOAD 0 + SIPUSH 137 + LALOAD + L2I + ISTORE 142 + ALOAD 0 + SIPUSH 138 + LALOAD + L2I + ISTORE 143 + ALOAD 0 + SIPUSH 139 + LALOAD + L2I + ISTORE 144 + ALOAD 0 + SIPUSH 140 + LALOAD + L2I + ISTORE 145 + ALOAD 0 + SIPUSH 141 + LALOAD + L2I + ISTORE 146 + ALOAD 0 + SIPUSH 142 + LALOAD + L2I + ISTORE 147 + ALOAD 0 + SIPUSH 143 + LALOAD + L2I + ISTORE 148 + ALOAD 0 + SIPUSH 144 + LALOAD + L2I + ISTORE 149 + ALOAD 0 + SIPUSH 145 + LALOAD + L2I + ISTORE 150 + ALOAD 0 + SIPUSH 146 + LALOAD + L2I + ISTORE 151 + ALOAD 0 + SIPUSH 147 + LALOAD + L2I + ISTORE 152 + ALOAD 0 + SIPUSH 148 + LALOAD + L2I + ISTORE 153 + ALOAD 0 + SIPUSH 149 + LALOAD + L2I + ISTORE 154 + ALOAD 0 + SIPUSH 150 + LALOAD + L2I + ISTORE 155 + ALOAD 0 + SIPUSH 151 + LALOAD + L2I + ISTORE 156 + ALOAD 0 + SIPUSH 152 + LALOAD + L2I + ISTORE 157 + ALOAD 0 + SIPUSH 153 + LALOAD + L2I + ISTORE 158 + ALOAD 0 + SIPUSH 154 + LALOAD + L2I + ISTORE 159 + ALOAD 0 + SIPUSH 155 + LALOAD + L2I + ISTORE 160 + ALOAD 0 + SIPUSH 156 + LALOAD + L2I + ISTORE 161 + ALOAD 0 + SIPUSH 157 + LALOAD + L2I + ISTORE 162 + ALOAD 0 + SIPUSH 158 + LALOAD + L2I + ISTORE 163 + ALOAD 0 + SIPUSH 159 + LALOAD + L2I + ISTORE 164 + ALOAD 0 + SIPUSH 160 + LALOAD + L2I + ISTORE 165 + ALOAD 0 + SIPUSH 161 + LALOAD + L2I + ISTORE 166 + ALOAD 0 + SIPUSH 162 + LALOAD + L2I + ISTORE 167 + ALOAD 0 + SIPUSH 163 + LALOAD + L2I + ISTORE 168 + ALOAD 0 + SIPUSH 164 + LALOAD + L2I + ISTORE 169 + ALOAD 0 + SIPUSH 165 + LALOAD + L2I + ISTORE 170 + ALOAD 0 + SIPUSH 166 + LALOAD + L2I + ISTORE 171 + ALOAD 0 + SIPUSH 167 + LALOAD + L2I + ISTORE 172 + ALOAD 0 + SIPUSH 168 + LALOAD + L2I + ISTORE 173 + ALOAD 0 + SIPUSH 169 + LALOAD + L2I + ISTORE 174 + ALOAD 0 + SIPUSH 170 + LALOAD + L2I + ISTORE 175 + ALOAD 0 + SIPUSH 171 + LALOAD + L2I + ISTORE 176 + ALOAD 0 + SIPUSH 172 + LALOAD + L2I + ISTORE 177 + ALOAD 0 + SIPUSH 173 + LALOAD + L2I + ISTORE 178 + ALOAD 0 + SIPUSH 174 + LALOAD + L2I + ISTORE 179 + ALOAD 0 + SIPUSH 175 + LALOAD + L2I + ISTORE 180 + ALOAD 0 + SIPUSH 176 + LALOAD + L2I + ISTORE 181 + ALOAD 0 + SIPUSH 177 + LALOAD + L2I + ISTORE 182 + ALOAD 0 + SIPUSH 178 + LALOAD + L2I + ISTORE 183 + ALOAD 0 + SIPUSH 179 + LALOAD + L2I + ISTORE 184 + ALOAD 0 + SIPUSH 180 + LALOAD + L2I + ISTORE 185 + ALOAD 0 + SIPUSH 181 + LALOAD + L2I + ISTORE 186 + ALOAD 0 + SIPUSH 182 + LALOAD + L2I + ISTORE 187 + ALOAD 0 + SIPUSH 183 + LALOAD + L2I + ISTORE 188 + ALOAD 0 + SIPUSH 184 + LALOAD + L2I + ISTORE 189 + ALOAD 0 + SIPUSH 185 + LALOAD + L2I + ISTORE 190 + ALOAD 0 + SIPUSH 186 + LALOAD + L2I + ISTORE 191 + ALOAD 0 + SIPUSH 187 + LALOAD + L2I + ISTORE 192 + ALOAD 0 + SIPUSH 188 + LALOAD + L2I + ISTORE 193 + ALOAD 0 + SIPUSH 189 + LALOAD + L2I + ISTORE 194 + ALOAD 0 + SIPUSH 190 + LALOAD + L2I + ISTORE 195 + ALOAD 0 + SIPUSH 191 + LALOAD + L2I + ISTORE 196 + ALOAD 0 + SIPUSH 192 + LALOAD + L2I + ISTORE 197 + ALOAD 0 + SIPUSH 193 + LALOAD + L2I + ISTORE 198 + ALOAD 0 + SIPUSH 194 + LALOAD + L2I + ISTORE 199 + ALOAD 0 + SIPUSH 195 + LALOAD + L2I + ISTORE 200 + ALOAD 0 + SIPUSH 196 + LALOAD + L2I + ISTORE 201 + ALOAD 0 + SIPUSH 197 + LALOAD + L2I + ISTORE 202 + ALOAD 0 + SIPUSH 198 + LALOAD + L2I + ISTORE 203 + ALOAD 0 + SIPUSH 199 + LALOAD + L2I + ISTORE 204 + ALOAD 0 + SIPUSH 200 + LALOAD + L2I + ISTORE 205 + ALOAD 0 + SIPUSH 201 + LALOAD + L2I + ISTORE 206 + ALOAD 0 + SIPUSH 202 + LALOAD + L2I + ISTORE 207 + ALOAD 0 + SIPUSH 203 + LALOAD + L2I + ISTORE 208 + ALOAD 0 + SIPUSH 204 + LALOAD + L2I + ISTORE 209 + ALOAD 0 + SIPUSH 205 + LALOAD + L2I + ISTORE 210 + ALOAD 0 + SIPUSH 206 + LALOAD + L2I + ISTORE 211 + ALOAD 0 + SIPUSH 207 + LALOAD + L2I + ISTORE 212 + ALOAD 0 + SIPUSH 208 + LALOAD + L2I + ISTORE 213 + ALOAD 0 + SIPUSH 209 + LALOAD + L2I + ISTORE 214 + ALOAD 0 + SIPUSH 210 + LALOAD + L2I + ISTORE 215 + ALOAD 0 + SIPUSH 211 + LALOAD + L2I + ISTORE 216 + ALOAD 0 + SIPUSH 212 + LALOAD + L2I + ISTORE 217 + ALOAD 0 + SIPUSH 213 + LALOAD + L2I + ISTORE 218 + ALOAD 0 + SIPUSH 214 + LALOAD + L2I + ISTORE 219 + ALOAD 0 + SIPUSH 215 + LALOAD + L2I + ISTORE 220 + ALOAD 0 + SIPUSH 216 + LALOAD + L2I + ISTORE 221 + ALOAD 0 + SIPUSH 217 + LALOAD + L2I + ISTORE 222 + ALOAD 0 + SIPUSH 218 + LALOAD + L2I + ISTORE 223 + ALOAD 0 + SIPUSH 219 + LALOAD + L2I + ISTORE 224 + ALOAD 0 + SIPUSH 220 + LALOAD + L2I + ISTORE 225 + ALOAD 0 + SIPUSH 221 + LALOAD + L2I + ISTORE 226 + ALOAD 0 + SIPUSH 222 + LALOAD + L2I + ISTORE 227 + ALOAD 0 + SIPUSH 223 + LALOAD + L2I + ISTORE 228 + ALOAD 0 + SIPUSH 224 + LALOAD + L2I + ISTORE 229 + ALOAD 0 + SIPUSH 225 + LALOAD + L2I + ISTORE 230 + ALOAD 0 + SIPUSH 226 + LALOAD + L2I + ISTORE 231 + ALOAD 0 + SIPUSH 227 + LALOAD + L2I + ISTORE 232 + ALOAD 0 + SIPUSH 228 + LALOAD + L2I + ISTORE 233 + ALOAD 0 + SIPUSH 229 + LALOAD + L2I + ISTORE 234 + ALOAD 0 + SIPUSH 230 + LALOAD + L2I + ISTORE 235 + ALOAD 0 + SIPUSH 231 + LALOAD + L2I + ISTORE 236 + ALOAD 0 + SIPUSH 232 + LALOAD + L2I + ISTORE 237 + ALOAD 0 + SIPUSH 233 + LALOAD + L2I + ISTORE 238 + ALOAD 0 + SIPUSH 234 + LALOAD + L2I + ISTORE 239 + ALOAD 0 + SIPUSH 235 + LALOAD + L2I + ISTORE 240 + ALOAD 0 + SIPUSH 236 + LALOAD + L2I + ISTORE 241 + ALOAD 0 + SIPUSH 237 + LALOAD + L2I + ISTORE 242 + ALOAD 0 + SIPUSH 238 + LALOAD + L2I + ISTORE 243 + ALOAD 0 + SIPUSH 239 + LALOAD + L2I + ISTORE 244 + ALOAD 0 + SIPUSH 240 + LALOAD + L2I + ISTORE 245 + ALOAD 0 + SIPUSH 241 + LALOAD + L2I + ISTORE 246 + ALOAD 0 + SIPUSH 242 + LALOAD + L2I + ISTORE 247 + ALOAD 0 + SIPUSH 243 + LALOAD + L2I + ISTORE 248 + ALOAD 0 + SIPUSH 244 + LALOAD + L2I + ISTORE 249 + ALOAD 0 + SIPUSH 245 + LALOAD + L2I + ISTORE 250 + ALOAD 0 + SIPUSH 246 + LALOAD + L2I + ISTORE 251 + ALOAD 0 + SIPUSH 247 + LALOAD + L2I + ISTORE 252 + ALOAD 0 + SIPUSH 248 + LALOAD + L2I + ISTORE 253 + ALOAD 0 + SIPUSH 249 + LALOAD + L2I + ISTORE 254 + ALOAD 0 + SIPUSH 250 + LALOAD + L2I + ISTORE 255 + ALOAD 0 + SIPUSH 251 + LALOAD + L2I + ISTORE 256 + ALOAD 0 + SIPUSH 252 + LALOAD + L2I + ISTORE 257 + ALOAD 0 + SIPUSH 253 + LALOAD + L2I + ISTORE 258 + ALOAD 0 + SIPUSH 254 + LALOAD + L2I + ISTORE 259 + ALOAD 0 + SIPUSH 255 + LALOAD + L2I + ISTORE 260 + ALOAD 0 + SIPUSH 256 + LALOAD + L2I + ISTORE 261 + ALOAD 0 + SIPUSH 257 + LALOAD + L2I + ISTORE 262 + ALOAD 0 + SIPUSH 258 + LALOAD + L2I + ISTORE 263 + ALOAD 0 + SIPUSH 259 + LALOAD + L2I + ISTORE 264 + ALOAD 0 + SIPUSH 260 + LALOAD + L2I + ISTORE 265 + ALOAD 0 + SIPUSH 261 + LALOAD + L2I + ISTORE 266 + ALOAD 0 + SIPUSH 262 + LALOAD + L2I + ISTORE 267 + ALOAD 0 + SIPUSH 263 + LALOAD + L2I + ISTORE 268 + ALOAD 0 + SIPUSH 264 + LALOAD + L2I + ISTORE 269 + ALOAD 0 + SIPUSH 265 + LALOAD + L2I + ISTORE 270 + ALOAD 0 + SIPUSH 266 + LALOAD + L2I + ISTORE 271 + ALOAD 0 + SIPUSH 267 + LALOAD + L2I + ISTORE 272 + ALOAD 0 + SIPUSH 268 + LALOAD + L2I + ISTORE 273 + ALOAD 0 + SIPUSH 269 + LALOAD + L2I + ISTORE 274 + ALOAD 0 + SIPUSH 270 + LALOAD + L2I + ISTORE 275 + ALOAD 0 + SIPUSH 271 + LALOAD + L2I + ISTORE 276 + ALOAD 0 + SIPUSH 272 + LALOAD + L2I + ISTORE 277 + ALOAD 0 + SIPUSH 273 + LALOAD + L2I + ISTORE 278 + ALOAD 0 + SIPUSH 274 + LALOAD + L2I + ISTORE 279 + ALOAD 0 + SIPUSH 275 + LALOAD + L2I + ISTORE 280 + ALOAD 0 + SIPUSH 276 + LALOAD + L2I + ISTORE 281 + ALOAD 0 + SIPUSH 277 + LALOAD + L2I + ISTORE 282 + ALOAD 0 + SIPUSH 278 + LALOAD + L2I + ISTORE 283 + ALOAD 0 + SIPUSH 279 + LALOAD + L2I + ISTORE 284 + ALOAD 0 + SIPUSH 280 + LALOAD + L2I + ISTORE 285 + ALOAD 0 + SIPUSH 281 + LALOAD + L2I + ISTORE 286 + ALOAD 0 + SIPUSH 282 + LALOAD + L2I + ISTORE 287 + ALOAD 0 + SIPUSH 283 + LALOAD + L2I + ISTORE 288 + ALOAD 0 + SIPUSH 284 + LALOAD + L2I + ISTORE 289 + ALOAD 0 + SIPUSH 285 + LALOAD + L2I + ISTORE 290 + ALOAD 0 + SIPUSH 286 + LALOAD + L2I + ISTORE 291 + ALOAD 0 + SIPUSH 287 + LALOAD + L2I + ISTORE 292 + ALOAD 0 + SIPUSH 288 + LALOAD + L2I + ISTORE 293 + ALOAD 0 + SIPUSH 289 + LALOAD + L2I + ISTORE 294 + ALOAD 0 + SIPUSH 290 + LALOAD + L2I + ISTORE 295 + ALOAD 0 + SIPUSH 291 + LALOAD + L2I + ISTORE 296 + ALOAD 0 + SIPUSH 292 + LALOAD + L2I + ISTORE 297 + ALOAD 0 + SIPUSH 293 + LALOAD + L2I + ISTORE 298 + ALOAD 0 + SIPUSH 294 + LALOAD + L2I + ISTORE 299 + ALOAD 0 + SIPUSH 295 + LALOAD + L2I + ISTORE 300 + ALOAD 0 + SIPUSH 296 + LALOAD + L2I + ISTORE 301 + ALOAD 0 + SIPUSH 297 + LALOAD + L2I + ISTORE 302 + ALOAD 0 + SIPUSH 298 + LALOAD + L2I + ISTORE 303 + ALOAD 0 + SIPUSH 299 + LALOAD + L2I + ISTORE 304 + ILOAD 3 + ILOAD 304 + IADD + IRETURN + + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J + ALOAD 2 + ALOAD 1 + ALOAD 0 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 ([JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + I2L + LSTORE 4 + ICONST_1 + NEWARRAY T_LONG + DUP + ICONST_0 + LLOAD 4 + LASTORE + ARETURN +} + +final class run/endive/$gen/CompiledMachineMachineCall { + + public ()V + ALOAD 0 + INVOKESPECIAL java/lang/Object. ()V + RETURN + + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ALOAD 0 + ALOAD 1 + ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 1: L1 default: L2 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L2 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.functions10.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.functions10.approved.txt index 776e13b08..0d0387cad 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.functions10.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.functions10.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -120,7 +153,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { IADD IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -129,12 +162,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -148,7 +181,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -157,12 +190,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -176,7 +209,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -185,12 +218,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -204,7 +237,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_3(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_3(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -213,12 +246,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_3 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -232,7 +265,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_3 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_4(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_4(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -241,12 +274,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_4 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -263,7 +296,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_4 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_5(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_5(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -272,12 +305,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_5 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -291,7 +324,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_5 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_6(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_6(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -300,12 +333,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_6 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -319,7 +352,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_6 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_7(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_7(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -328,12 +361,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_7 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -347,7 +380,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_7 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_8(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_8(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -356,12 +389,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_8 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -375,7 +408,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_8 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I IRETURN - public static call_9(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_9(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -384,12 +417,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_1 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.func_9 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -401,10 +434,11 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 @@ -419,34 +453,34 @@ final class run/endive/$gen/CompiledMachineMachineCall { 9: L9 default: L10 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L2 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_2 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_2 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L3 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_3 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_3 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L4 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_4 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_4 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L5 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_5 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_5 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L6 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_6 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_6 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L7 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_7 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_7 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L8 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_8 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_8 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L9 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_9 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_1.call_9 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L10 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBrTable.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBrTable.approved.txt index 65c1516f8..f891730a9 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBrTable.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBrTable.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -100,7 +133,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L4 ATHROW - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -109,12 +142,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -126,16 +159,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBranching.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBranching.approved.txt index 3ddbf9272..2d8a5003c 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBranching.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyBranching.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -102,7 +135,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ILOAD 3 IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -111,12 +144,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -128,16 +161,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyExceptions.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyExceptions.approved.txt index 77e775c09..9e25e57f3 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyExceptions.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyExceptions.approved.txt @@ -87,7 +87,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ICONST_4 IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -96,12 +96,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -120,7 +120,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ICONST_0 IRETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -129,12 +129,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -172,7 +172,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ICONST_1 IRETURN - public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -181,12 +181,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyFloat.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyFloat.approved.txt index b2426373d..0eb43f90b 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyFloat.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyFloat.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 3 @@ -76,7 +109,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { POP RETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -91,16 +124,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyGc.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyGc.approved.txt index 926ddb655..39b92e6bd 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyGc.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyGc.approved.txt @@ -1,6 +1,6 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { - public static func_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + public static func_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)Ljava/lang/Object; ILOAD 0 ILOAD 1 ISTORE 5 @@ -17,12 +17,13 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ILOAD 5 I2L LASTORE + ACONST_NULL ICONST_0 ALOAD 3 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structNew ([JILrun/endive/runtime/Instance;)I - IRETURN + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structNew ([J[Ljava/lang/Object;ILrun/endive/runtime/Instance;)Ljava/lang/Object; + ARETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -33,75 +34,163 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L2I ALOAD 1 ALOAD 0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - I2L - LSTORE 3 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)Ljava/lang/Object; + POP ICONST_1 NEWARRAY T_LONG + ARETURN + + public static callWithRefs_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + ALOAD 2 + ICONST_0 + LALOAD + L2I + ALOAD 2 + ICONST_1 + LALOAD + L2I + ALOAD 1 + ALOAD 0 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)Ljava/lang/Object; + ASTORE 4 + NEW run/endive/runtime/CallResult + DUP + ACONST_NULL + ICONST_1 + ANEWARRAY java/lang/Object DUP ICONST_0 - LLOAD 3 - LASTORE + ALOAD 4 + AASTORE + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V ARETURN - public static func_1(ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - ILOAD 0 + public static func_1(Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + ALOAD 0 ICONST_0 ICONST_0 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (IIILrun/endive/runtime/Instance;)J + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (Ljava/lang/Object;IILrun/endive/runtime/Instance;)J L2I IRETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J - ALOAD 2 + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J + ALOAD 3 + DUP + IFNONNULL L0 + POP + ACONST_NULL + GOTO L1 + L0 ICONST_0 - LALOAD - L2I + AALOAD + L1 ALOAD 1 ALOAD 0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN - public static func_2(ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I - ILOAD 0 + public static callWithRefs_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + ALOAD 3 + DUP + IFNONNULL L0 + POP + ACONST_NULL + GOTO L1 + L0 + ICONST_0 + AALOAD + L1 + ALOAD 1 + ALOAD 0 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + I2L + LSTORE 4 + NEW run/endive/runtime/CallResult + DUP + ICONST_1 + NEWARRAY T_LONG + DUP + ICONST_0 + LLOAD 4 + LASTORE + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + + public static func_2(Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + ALOAD 0 ICONST_0 ICONST_0 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (IIILrun/endive/runtime/Instance;)J + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (Ljava/lang/Object;IILrun/endive/runtime/Instance;)J L2I - ILOAD 0 + ALOAD 0 ICONST_0 ICONST_1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (IIILrun/endive/runtime/Instance;)J + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.structGet (Ljava/lang/Object;IILrun/endive/runtime/Instance;)J L2I IADD IRETURN - public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J - ALOAD 2 + public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J + ALOAD 3 + DUP + IFNONNULL L0 + POP + ACONST_NULL + GOTO L1 + L0 ICONST_0 - LALOAD - L2I + AALOAD + L1 ALOAD 1 ALOAD 0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 + ICONST_1 + NEWARRAY T_LONG + DUP + ICONST_0 + LLOAD 4 + LASTORE + ARETURN + + public static callWithRefs_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + ALOAD 3 + DUP + IFNONNULL L0 + POP + ACONST_NULL + GOTO L1 + L0 + ICONST_0 + AALOAD + L1 + ALOAD 1 + ALOAD 0 + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (Ljava/lang/Object;Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I + I2L + LSTORE 4 + NEW run/endive/runtime/CallResult + DUP ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V ARETURN } diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyHelloWasi.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyHelloWasi.approved.txt index 0bf4dd66e..309466640 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyHelloWasi.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyHelloWasi.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +23,41 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIIIIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 7 @@ -190,7 +223,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { POP RETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -205,24 +238,30 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 1: L1 default: L2 L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L0 POP POP + POP + POP + ALOAD 0 ILOAD 2 ALOAD 3 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callHostFunction (Lrun/endive/runtime/Instance;I[J)[J + ALOAD 4 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callHostFunctionWithRefs (Lrun/endive/runtime/Instance;I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + INVOKEVIRTUAL run/endive/runtime/CallResult.longs ()[J ARETURN L2 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32.approved.txt index 6a5ef2a4b..3272b0e33 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 3 @@ -100,7 +133,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { POP2 RETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -115,16 +148,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32Renamed.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32Renamed.approved.txt index 469719821..0d1860589 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32Renamed.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyI32Renamed.approved.txt @@ -2,6 +2,8 @@ public final class FOO implements run/endive/runtime/Machine { private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,8 @@ public final class FOO implements run/endive/runtime/Machine { public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 L0 ALOAD 0 GETFIELD FOO.instance : Lrun/endive/runtime/Instance; @@ -19,12 +23,41 @@ public final class FOO implements run/endive/runtime/Machine { INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC FOOMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC FOOMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC FOOShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + L0 + ALOAD 0 + GETFIELD FOO.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC FOOMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC FOOShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL FOO.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V INVOKESTATIC FOOShaded.checkInterruption ()V ALOAD 3 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyIterFact.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyIterFact.approved.txt index 29484bd45..d36a5eca3 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyIterFact.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyIterFact.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -104,7 +137,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ILOAD 3 IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -113,12 +146,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -130,16 +163,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyKitchenSink.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyKitchenSink.approved.txt index c2aa894fc..42fb76fd8 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyKitchenSink.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyKitchenSink.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -105,7 +138,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/runtime/OpcodeImpl.I32_EXTEND_8_S (I)I IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -114,12 +147,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -131,16 +164,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyMemory.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyMemory.approved.txt index 8b24e2d46..8d3b82ad4 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyMemory.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyMemory.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -140,7 +173,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L0 ATHROW - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -149,12 +182,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN @@ -172,19 +205,19 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L0 ATHROW - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (JLrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)J - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -196,20 +229,21 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 1: L1 default: L2 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L2 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyStart.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyStart.approved.txt index b2d2bcfcc..3b8006477 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyStart.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyStart.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +23,41 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 4 @@ -137,7 +170,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (ILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V RETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -152,24 +185,30 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 1: L1 default: L2 L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L0 POP POP + POP + POP + ALOAD 0 ILOAD 2 ALOAD 3 - INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callHostFunction (Lrun/endive/runtime/Instance;I[J)[J + ALOAD 4 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.callHostFunctionWithRefs (Lrun/endive/runtime/Instance;I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + INVOKEVIRTUAL run/endive/runtime/CallResult.longs ()[J ARETURN L2 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTailCall.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTailCall.approved.txt index a0379e79c..b29037c35 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTailCall.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTailCall.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,28 +14,70 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L2 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; - ASTORE 3 + ASTORE 4 L3 - ALOAD 3 + ALOAD 4 DUP INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ALOAD 4 INVOKEVIRTUAL run/endive/runtime/Instance.isTailCallPending ()Z IFEQ L1 POP - ALOAD 3 + ALOAD 4 INVOKEVIRTUAL run/endive/runtime/Instance.tailCallFuncId ()I ISTORE 1 - ALOAD 3 + ALOAD 4 INVOKEVIRTUAL run/endive/runtime/Instance.tailCallArgs ()[J ASTORE 2 + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.tailCallRefArgs ()[Ljava/lang/Object; + ASTORE 3 + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.clearTailCall ()V + GOTO L3 + L1 + ARETURN + L2 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L2 java/lang/StackOverflowError + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + ASTORE 4 + L3 + ALOAD 4 + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.isTailCallPending ()Z + IFEQ L1 + POP + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.tailCallFuncId ()I + ISTORE 1 + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.tailCallArgs ()[J + ASTORE 2 + ALOAD 4 + INVOKEVIRTUAL run/endive/runtime/Instance.tailCallRefArgs ()[Ljava/lang/Object; + ASTORE 3 + ALOAD 4 INVOKEVIRTUAL run/endive/runtime/Instance.clearTailCall ()V GOTO L3 L1 @@ -42,6 +86,18 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IIIIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 6 @@ -157,7 +213,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L1 IRETURN - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 2 ICONST_0 LALOAD @@ -174,12 +230,12 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (IIILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)I I2L - LSTORE 3 + LSTORE 4 ICONST_1 NEWARRAY T_LONG DUP ICONST_0 - LLOAD 3 + LLOAD 4 LASTORE ARETURN } @@ -191,16 +247,17 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 default: L1 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 ILOAD 2 diff --git a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTrap.approved.txt b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTrap.approved.txt index 9e1c20b63..f3ca0a33f 100644 --- a/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTrap.approved.txt +++ b/compiler/src/test/resources/run/endive/approvals/ApprovalTest.verifyTrap.approved.txt @@ -2,6 +2,8 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime private final Lrun/endive/runtime/Instance; instance + private final Lrun/endive/runtime/internal/CompilerInterpreterMachine; compilerInterpreterMachine + public (Lrun/endive/runtime/Instance;)V ALOAD 0 INVOKESPECIAL java/lang/Object. ()V @@ -12,6 +14,24 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime public call(I[J)[J TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError + ACONST_NULL + ASTORE 3 + L0 + ALOAD 0 + GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; + DUP + INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J + ARETURN + L1 + INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; + ATHROW + + public call(I[J[Ljava/lang/Object;)[J + TRYCATCHBLOCK L0 L1 L1 java/lang/StackOverflowError L0 ALOAD 0 GETFIELD run/endive/$gen/CompiledMachine.instance : Lrun/endive/runtime/Instance; @@ -19,12 +39,25 @@ public final class run/endive/$gen/CompiledMachine implements run/endive/runtime INVOKEVIRTUAL run/endive/runtime/Instance.memory ()Lrun/endive/runtime/Memory; ILOAD 1 ALOAD 2 - INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + ALOAD 3 + INVOKESTATIC run/endive/$gen/CompiledMachineMachineCall.call (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ARETURN L1 INVOKESTATIC run/endive/$gen/CompiledMachineShaded.throwCallStackExhausted (Ljava/lang/StackOverflowError;)Ljava/lang/RuntimeException; ATHROW + public callWithRefs(I[J[Ljava/lang/Object;)Lrun/endive/runtime/CallResult; + NEW run/endive/runtime/CallResult + DUP + ALOAD 0 + ILOAD 1 + ALOAD 2 + ALOAD 3 + INVOKEVIRTUAL run/endive/$gen/CompiledMachine.call (I[J[Ljava/lang/Object;)[J + ACONST_NULL + INVOKESPECIAL run/endive/runtime/CallResult. ([J[Ljava/lang/Object;)V + ARETURN + public static call_indirect_0(IILrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V INVOKESTATIC run/endive/$gen/CompiledMachineShaded.checkInterruption ()V ALOAD 3 @@ -83,7 +116,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { L0 ATHROW - public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_0(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -97,7 +130,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_0 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V RETURN - public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_1(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -111,7 +144,7 @@ final class run/endive/$gen/CompiledMachineFuncGroup_0 { INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_1 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V RETURN - public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + public static call_2(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ALOAD 1 ALOAD 0 INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.func_2 (Lrun/endive/runtime/Memory;Lrun/endive/runtime/Instance;)V @@ -126,10 +159,11 @@ final class run/endive/$gen/CompiledMachineMachineCall { INVOKESPECIAL java/lang/Object. ()V RETURN - public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J)[J + public static call(Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;I[J[Ljava/lang/Object;)[J ALOAD 0 ALOAD 1 ALOAD 3 + ALOAD 4 ILOAD 2 TABLESWITCH 0: L0 @@ -137,13 +171,13 @@ final class run/endive/$gen/CompiledMachineMachineCall { 2: L2 default: L3 L0 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_0 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L1 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_1 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L2 - INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_2 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J)[J + INVOKESTATIC run/endive/$gen/CompiledMachineFuncGroup_0.call_2 (Lrun/endive/runtime/Instance;Lrun/endive/runtime/Memory;[J[Ljava/lang/Object;)[J ARETURN L3 ILOAD 2 diff --git a/machine-tests/src/test/java/run/endive/testing/BrOnNullTest.java b/machine-tests/src/test/java/run/endive/testing/BrOnNullTest.java index e0961a5b4..a57ef649f 100644 --- a/machine-tests/src/test/java/run/endive/testing/BrOnNullTest.java +++ b/machine-tests/src/test/java/run/endive/testing/BrOnNullTest.java @@ -39,11 +39,14 @@ public void brOnNullRefinesType(Function mac var instance = machineInject.apply(Instance.builder(module)).build(); // Create a point(42, 7) and verify get_x_or_default returns 42 + // new_point takes (i32, i32) -> (ref $Point) var newPoint = instance.export("new_point"); - long[] pointRef = newPoint.apply(42, 7); + var pointResult = newPoint.applyWithRefs(new long[] {42, 7}, null); + // get_x_or_default takes (ref null $Point) -> i32 var getX = instance.export("get_x_or_default"); - assertEquals(42, getX.apply(pointRef[0])[0]); + var xResult = getX.applyWithRefs(new long[] {0}, new Object[] {pointResult.refResult(0)}); + assertEquals(42, (int) xResult.longResult(0)); } @ParameterizedTest diff --git a/machine-tests/src/test/java/run/endive/testing/GcArrayCopyFuncrefTest.java b/machine-tests/src/test/java/run/endive/testing/GcArrayCopyFuncrefTest.java new file mode 100644 index 000000000..5ac535df5 --- /dev/null +++ b/machine-tests/src/test/java/run/endive/testing/GcArrayCopyFuncrefTest.java @@ -0,0 +1,39 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmModule; + +public class GcArrayCopyFuncrefTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_array_copy_funcref.wat.wasm")); + + private static Stream machineImplementations() { + return Stream.of( + Arguments.of( + (Function) + (b) -> b.withMachineFactory(InterpreterMachine::new)), + Arguments.of( + (Function) + (b) -> b.withMachineFactory(MachineFactoryCompiler::compile))); + } + + @ParameterizedTest + @MethodSource("machineImplementations") + public void copyFuncrefArray(Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("copy_funcref_array").apply(); + assertEquals(0, result[0]); + } +} diff --git a/machine-tests/src/test/java/run/endive/testing/GcEdgeCasesTest.java b/machine-tests/src/test/java/run/endive/testing/GcEdgeCasesTest.java new file mode 100644 index 000000000..453778675 --- /dev/null +++ b/machine-tests/src/test/java/run/endive/testing/GcEdgeCasesTest.java @@ -0,0 +1,67 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmModule; + +public class GcEdgeCasesTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_edge_cases.wat.wasm")); + + private static Stream machineImplementations() { + return Stream.of( + Arguments.of( + (Function) + (b) -> b.withMachineFactory(InterpreterMachine::new)), + Arguments.of( + (Function) + (b) -> b.withMachineFactory(MachineFactoryCompiler::compile))); + } + + @ParameterizedTest + @MethodSource("machineImplementations") + public void structNewDefaultFuncrefIsNull( + Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("default_funcref_is_null").apply(); + assertEquals(1, result[0]); + } + + @ParameterizedTest + @MethodSource("machineImplementations") + public void externRoundTrip(Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + // extern_roundtrip takes no args: it creates a struct internally, + // converts to extern and back, then returns the x field (42). + var result = instance.export("extern_roundtrip").apply(); + assertEquals(42, (int) result[0]); + } + + @ParameterizedTest + @MethodSource("machineImplementations") + public void makeAndGetPoint(Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var makePoint = instance.export("make_point"); + var getX = instance.export("get_x"); + + // make_point(i32) -> (ref $Point) + var pointResult = makePoint.applyWithRefs(new long[] {99}, null); + assertNotNull(pointResult.refResult(0)); + + // get_x((ref $Point)) -> i32 + var xResult = getX.applyWithRefs(new long[] {0}, new Object[] {pointResult.refResult(0)}); + assertEquals(99, (int) xResult.longResult(0)); + } +} diff --git a/machine-tests/src/test/java/run/endive/testing/GcMultiValueTest.java b/machine-tests/src/test/java/run/endive/testing/GcMultiValueTest.java new file mode 100644 index 000000000..6c9dd8c09 --- /dev/null +++ b/machine-tests/src/test/java/run/endive/testing/GcMultiValueTest.java @@ -0,0 +1,110 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmModule; + +/** + * Tests for multi-value returns mixing GC refs and numerics. + */ +public class GcMultiValueTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_multivalue.wat.wasm")); + + private static Stream machineImplementations() { + return Stream.of( + Arguments.of( + "interpreter", + (Function) + (b) -> b.withMachineFactory(InterpreterMachine::new)), + Arguments.of( + "compiler", + (Function) + (b) -> b.withMachineFactory(MachineFactoryCompiler::compile))); + } + + // (result i32 (ref $Point)) -- numeric then ref + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void intThenRef( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var fn = instance.export("int_then_ref"); + var getX = instance.export("get_x"); + + var cr = fn.applyWithRefs(new long[] {42}, null); + assertEquals(42, cr.longResult(0), "numeric result"); + Object ref = cr.refResult(1); + assertNotNull(ref, "ref result should not be null"); + // Verify the ref is usable + var xResult = getX.applyWithRefs(new long[] {0}, new Object[] {ref}); + assertEquals(42, xResult.longResult(0), "get_x from returned ref"); + } + + // (result (ref $Point) i32) -- ref then numeric + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void refThenInt( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var fn = instance.export("ref_then_int"); + var getX = instance.export("get_x"); + + var cr = fn.applyWithRefs(new long[] {99}, null); + Object ref = cr.refResult(0); + assertNotNull(ref, "ref result should not be null"); + assertEquals(99, cr.longResult(1), "numeric result"); + // Verify the ref is usable + var xResult = getX.applyWithRefs(new long[] {0}, new Object[] {ref}); + assertEquals(99, xResult.longResult(0), "get_x from returned ref"); + } + + // (result (ref $Point) (ref $Point)) -- two refs + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void twoRefs(String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var fn = instance.export("two_refs"); + var getX = instance.export("get_x"); + + var cr = fn.applyWithRefs(new long[] {7}, null); + Object ref0 = cr.refResult(0); + Object ref1 = cr.refResult(1); + assertNotNull(ref0, "first ref should not be null"); + assertNotNull(ref1, "second ref should not be null"); + var x0 = getX.applyWithRefs(new long[] {0}, new Object[] {ref0}); + var x1 = getX.applyWithRefs(new long[] {0}, new Object[] {ref1}); + assertEquals(7, x0.longResult(0), "get_x from first ref"); + assertEquals(7, x1.longResult(0), "get_x from second ref"); + } + + // (result i32 i32 (ref $Point)) -- two numerics + ref + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void twoIntsRef( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var fn = instance.export("two_ints_ref"); + var getX = instance.export("get_x"); + + var cr = fn.applyWithRefs(new long[] {11}, null); + assertEquals(11, cr.longResult(0), "first numeric result"); + assertEquals(11, cr.longResult(1), "second numeric result"); + Object ref = cr.refResult(2); + assertNotNull(ref, "ref result should not be null"); + var xResult = getX.applyWithRefs(new long[] {0}, new Object[] {ref}); + assertEquals(11, xResult.longResult(0), "get_x from returned ref"); + } +} diff --git a/machine-tests/src/test/java/run/endive/testing/GcReviewFixesTest.java b/machine-tests/src/test/java/run/endive/testing/GcReviewFixesTest.java new file mode 100644 index 000000000..2cf30bbc8 --- /dev/null +++ b/machine-tests/src/test/java/run/endive/testing/GcReviewFixesTest.java @@ -0,0 +1,119 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmModule; + +/** + * Tests for GC ref review fixes: + * - Bug 2: null GC ref through block boundary (doControlTransfer) + * - Bug 7: null GC ref in exception (pushExceptionArgs) + * - Bug 8: table.fill integer overflow + */ +public class GcReviewFixesTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_review_fixes.wat.wasm")); + + private static Stream machineImplementations() { + return Stream.of( + Arguments.of( + "interpreter", + (Function) + (b) -> b.withMachineFactory(InterpreterMachine::new)), + Arguments.of( + "compiler", + (Function) + (b) -> b.withMachineFactory(MachineFactoryCompiler::compile))); + } + + // Bug 2: null GC ref must survive block boundary + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void nullRefThroughBlock( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("null_ref_through_block").apply(); + assertEquals(1, result[0], "null ref should be preserved through block"); + } + + // Bug 2: null GC ref through nested blocks + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void nullRefNestedBlocks( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("null_ref_nested_blocks").apply(); + assertEquals(1, result[0], "null ref should be preserved through nested blocks"); + } + + // Bug 2 regression: non-null GC ref through block + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void nonNullRefThroughBlock( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("nonnull_ref_through_block").apply(); + assertEquals(42, result[0], "non-null ref value should be preserved through block"); + } + + // Bug 7: null GC ref through exception catch + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void nullRefException( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("null_ref_exception").apply(); + assertEquals(1, result[0], "null ref should be preserved through exception"); + } + + // Bug 7 regression: non-null GC ref through exception + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void nonNullRefException( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("nonnull_ref_exception").apply(); + assertEquals(99, result[0], "non-null ref value should be preserved through exception"); + } + + // Bug 8: table.fill bounds (validates no integer overflow) + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void tableFillBounds( + String name, Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var result = instance.export("table_fill_bounds").apply(); + assertEquals(1, result[0], "table.fill should succeed for valid bounds"); + } + + // Bug 1: externref functions should throw from apply() (isObjectRef, not isGcReference) + @ParameterizedTest(name = "{0}") + @MethodSource("machineImplementations") + public void externrefApplyGuard( + String name, Function machineInject) { + // The gc_edge_cases module has make_point which takes/returns GC refs. + // After the fix, apply() should throw for functions with any ObjectRef params/returns. + var module = Parser.parse(CorpusResources.getResource("compiled/gc_edge_cases.wat.wasm")); + var instance = machineInject.apply(Instance.builder(module)).build(); + // make_point has (ref $Point) return - should throw from apply() + var makePoint = instance.export("make_point"); + try { + makePoint.apply(99); + // If we get here, the guard didn't fire (bug 1 not fixed) + throw new AssertionError("apply() should throw for GC ref returns"); + } catch (UnsupportedOperationException e) { + // expected + } + } +} diff --git a/machine-tests/src/test/java/run/endive/testing/GcStressTest.java b/machine-tests/src/test/java/run/endive/testing/GcStressTest.java new file mode 100644 index 000000000..0722c90bb --- /dev/null +++ b/machine-tests/src/test/java/run/endive/testing/GcStressTest.java @@ -0,0 +1,48 @@ +package run.endive.testing; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.function.Function; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import run.endive.compiler.MachineFactoryCompiler; +import run.endive.corpus.CorpusResources; +import run.endive.runtime.Instance; +import run.endive.runtime.InterpreterMachine; +import run.endive.wasm.Parser; +import run.endive.wasm.WasmModule; + +public class GcStressTest { + + private static final WasmModule MODULE = + Parser.parse(CorpusResources.getResource("compiled/gc_stress.wat.wasm")); + + private static Stream machineImplementations() { + return Stream.of( + Arguments.of( + (Function) + (b) -> b.withMachineFactory(InterpreterMachine::new)), + Arguments.of( + (Function) + (b) -> b.withMachineFactory(MachineFactoryCompiler::compile))); + } + + @ParameterizedTest + @MethodSource("machineImplementations") + public void gcCollectsUnreachableStructs( + Function machineInject) { + var instance = machineInject.apply(Instance.builder(MODULE)).build(); + var allocateChain = instance.export("allocate_chain"); + + // Allocate chains of 10,000 structs, 100 times. + // Each call creates a new chain; the previous chain becomes garbage. + // If Java GC doesn't collect unreachable Wasm GC refs, this OOMs. + for (int i = 0; i < 100; i++) { + var result = allocateChain.apply(10_000); + // Last struct in chain has val = n-1 + assertEquals(9999L, result[0]); + } + } +} diff --git a/runtime-tests/src/test/java/run/endive/testing/ArgsAdapter.java b/runtime-tests/src/test/java/run/endive/testing/ArgsAdapter.java index dae93f93d..b46820123 100644 --- a/runtime-tests/src/test/java/run/endive/testing/ArgsAdapter.java +++ b/runtime-tests/src/test/java/run/endive/testing/ArgsAdapter.java @@ -1,36 +1,58 @@ package run.endive.testing; -import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import run.endive.runtime.CallResult; +import run.endive.runtime.ExportFunction; public final class ArgsAdapter { - private final ArrayDeque stack; + private final List longs = new ArrayList<>(); + private final List refs = new ArrayList<>(); + private boolean hasRefs; - private ArgsAdapter() { - stack = new ArrayDeque<>(); - } + private ArgsAdapter() {} public static ArgsAdapter builder() { return new ArgsAdapter(); } - public long[] build() { - var result = new long[stack.size()]; - int i = stack.size() - 1; - while (!stack.isEmpty()) { - result[i--] = stack.pop(); - } - return result; + public ArgsAdapter add(long arg) { + longs.add(arg); + refs.add(null); + return this; } public ArgsAdapter add(long[] args) { for (var arg : args) { - stack.push(arg); + longs.add(arg); + refs.add(null); } return this; } - public ArgsAdapter add(long arg) { - stack.push(arg); + public ArgsAdapter addRef(Object ref) { + longs.add(0L); + refs.add(ref); + hasRefs = true; return this; } + + public long[] build() { + var result = new long[longs.size()]; + for (int i = 0; i < longs.size(); i++) { + result[i] = longs.get(i); + } + return result; + } + + public Object[] buildRefs() { + if (!hasRefs) { + return null; + } + return refs.toArray(); + } + + public CallResult applyWithRefs(ExportFunction func) { + return func.applyWithRefs(build(), buildRefs()); + } } diff --git a/runtime/src/main/java/run/endive/runtime/CallResult.java b/runtime/src/main/java/run/endive/runtime/CallResult.java new file mode 100644 index 000000000..c7ed2fa8c --- /dev/null +++ b/runtime/src/main/java/run/endive/runtime/CallResult.java @@ -0,0 +1,28 @@ +package run.endive.runtime; + +/** Holds the dual long[] + Object[] result of a Wasm function call that may return GC/externref values. */ +public final class CallResult { + private final long[] longs; + private final Object[] refs; + + public CallResult(long[] longs, Object[] refs) { + this.longs = longs; + this.refs = refs; + } + + public long[] longs() { + return longs; + } + + public Object[] refs() { + return refs; + } + + public long longResult(int i) { + return longs != null ? longs[i] : 0; + } + + public Object refResult(int i) { + return refs != null ? refs[i] : null; + } +} diff --git a/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java b/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java index 2d0a43688..d80daf14c 100644 --- a/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java +++ b/runtime/src/main/java/run/endive/runtime/ConstantEvaluators.java @@ -14,60 +14,89 @@ public final class ConstantEvaluators { private ConstantEvaluators() {} + public static final class ConstantResult { + private final long[] longs; + private final Object ref; + + ConstantResult(long[] longs, Object ref) { + this.longs = longs; + this.ref = ref; + } + + public long[] longs() { + return longs; + } + + public Object ref() { + return ref; + } + + public long longValue() { + return longs[0]; + } + } + public static long[] computeConstantValue(Instance instance, Instruction[] expr) { - return computeConstantValue(instance, Arrays.asList(expr)); + return computeConstant(instance, Arrays.asList(expr)).longs(); } public static long[] computeConstantValue(Instance instance, List expr) { - var stack = new ArrayDeque(); + return computeConstant(instance, expr).longs(); + } + + public static ConstantResult computeConstant(Instance instance, List expr) { + var stack = new ArrayDeque(); for (var instruction : expr) { switch (instruction.opcode()) { case I32_ADD: { - var x = (int) stack.pop()[0]; - var y = (int) stack.pop()[0]; - stack.push(new long[] {x + y}); + var x = (int) stack.pop().longValue(); + var y = (int) stack.pop().longValue(); + stack.push(longResult(x + y)); break; } case I32_SUB: { - var x = (int) stack.pop()[0]; - var y = (int) stack.pop()[0]; - stack.push(new long[] {y - x}); + var x = (int) stack.pop().longValue(); + var y = (int) stack.pop().longValue(); + stack.push(longResult(y - x)); break; } case I32_MUL: { - var x = (int) stack.pop()[0]; - var y = (int) stack.pop()[0]; + var x = (int) stack.pop().longValue(); + var y = (int) stack.pop().longValue(); int res = x * y; - stack.push(new long[] {res}); + stack.push(longResult(res)); break; } case I64_ADD: { - var x = stack.pop()[0]; - var y = stack.pop()[0]; - stack.push(new long[] {x + y}); + var x = stack.pop().longValue(); + var y = stack.pop().longValue(); + stack.push(longResult(x + y)); break; } case I64_SUB: { - var x = stack.pop()[0]; - var y = stack.pop()[0]; - stack.push(new long[] {y - x}); + var x = stack.pop().longValue(); + var y = stack.pop().longValue(); + stack.push(longResult(y - x)); break; } case I64_MUL: { - var x = stack.pop()[0]; - var y = stack.pop()[0]; - stack.push(new long[] {x * y}); + var x = stack.pop().longValue(); + var y = stack.pop().longValue(); + stack.push(longResult(x * y)); break; } case V128_CONST: { - stack.push(new long[] {instruction.operand(0), instruction.operand(1)}); + stack.push( + new ConstantResult( + new long[] {instruction.operand(0), instruction.operand(1)}, + null)); break; } case F32_CONST: @@ -76,12 +105,12 @@ public static long[] computeConstantValue(Instance instance, List e case I64_CONST: case REF_FUNC: { - stack.push(new long[] {instruction.operand(0)}); + stack.push(longResult(instruction.operand(0))); break; } case REF_NULL: { - stack.push(new long[] {Value.REF_NULL_VALUE}); + stack.push(new ConstantResult(new long[] {Value.REF_NULL_VALUE}, null)); break; } case GLOBAL_GET: @@ -92,16 +121,26 @@ public static long[] computeConstantValue(Instance instance, List e throw new InvalidException("unknown global"); } if (global.getType().equals(ValType.V128)) { - stack.push(new long[] {global.getValueLow(), global.getValueHigh()}); + stack.push( + new ConstantResult( + new long[] { + global.getValueLow(), global.getValueHigh() + }, + null)); + } else if (global.getType().isReference()) { + stack.push( + new ConstantResult( + new long[] {global.getValueLow()}, + global.getRefValue())); } else { - stack.push(new long[] {global.getValueLow()}); + stack.push(longResult(global.getValueLow())); } break; } case REF_I31: { - var val = (int) stack.pop()[0]; - stack.push(new long[] {Value.encodeI31(val)}); + var val = (int) stack.pop().longValue(); + stack.push(new ConstantResult(new long[] {0}, new WasmI31Ref(val))); break; } case STRUCT_NEW: @@ -115,12 +154,18 @@ public static long[] computeConstantValue(Instance instance, List e .structType(); var fieldCount = structType.fieldTypes().length; var fields = new long[fieldCount]; + var fieldRefs = new Object[fieldCount]; for (int i = fieldCount - 1; i >= 0; i--) { - fields[i] = stack.pop()[0]; + var entry = stack.pop(); + var ft = structType.fieldTypes()[i]; + if (ft.storageType().isObjectRef()) { + fieldRefs[i] = entry.ref(); + } else { + fields[i] = entry.longValue(); + } } - var struct = new WasmStruct(typeIdx, fields); - var refId = instance.registerGcRef(struct); - stack.push(new long[] {refId}); + var struct = new WasmStruct(typeIdx, fields, fieldRefs); + stack.push(new ConstantResult(new long[] {0}, struct)); break; } case STRUCT_NEW_DEFAULT: @@ -134,67 +179,92 @@ public static long[] computeConstantValue(Instance instance, List e .structType(); var fieldCount = structType.fieldTypes().length; var fields = new long[fieldCount]; + var fieldRefs = new Object[fieldCount]; + // Non-GC reference fields default to REF_NULL_VALUE for (int i = 0; i < fieldCount; i++) { var ft = structType.fieldTypes()[i]; if (ft.storageType().valType() != null - && ft.storageType().valType().isReference()) { + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { fields[i] = Value.REF_NULL_VALUE; } } - var struct = new WasmStruct(typeIdx, fields); - var refId = instance.registerGcRef(struct); - stack.push(new long[] {refId}); + var struct = new WasmStruct(typeIdx, fields, fieldRefs); + stack.push(new ConstantResult(new long[] {0}, struct)); break; } case ARRAY_NEW: { var typeIdx = (int) instruction.operand(0); - var len = (int) stack.pop()[0]; - var fillVal = stack.pop()[0]; + var len = (int) stack.pop().longValue(); + var fillEntry = stack.pop(); + var at = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .arrayType(); var elements = new long[len]; - Arrays.fill(elements, fillVal); - var array = new WasmArray(typeIdx, elements); - var refId = instance.registerGcRef(array); - stack.push(new long[] {refId}); + var elementRefs = new Object[len]; + if (at.fieldType().storageType().isObjectRef()) { + Arrays.fill(elementRefs, fillEntry.ref()); + } else { + Arrays.fill(elements, fillEntry.longValue()); + } + var array = new WasmArray(typeIdx, elements, elementRefs); + stack.push(new ConstantResult(new long[] {0}, array)); break; } case ARRAY_NEW_DEFAULT: { var typeIdx = (int) instruction.operand(0); - var len = (int) stack.pop()[0]; + var len = (int) stack.pop().longValue(); + var elements = new long[len]; + var elementRefs = new Object[len]; var at = instance.module() .typeSection() .getSubType(typeIdx) .compType() .arrayType(); - var elements = new long[len]; - if (at.fieldType().storageType().valType() != null - && at.fieldType().storageType().valType().isReference()) { + var ft = at.fieldType(); + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { Arrays.fill(elements, Value.REF_NULL_VALUE); } - var array = new WasmArray(typeIdx, elements); - var refId = instance.registerGcRef(array); - stack.push(new long[] {refId}); + var array = new WasmArray(typeIdx, elements, elementRefs); + stack.push(new ConstantResult(new long[] {0}, array)); break; } case ARRAY_NEW_FIXED: { var typeIdx = (int) instruction.operand(0); var len = (int) instruction.operand(1); + var at = + instance.module() + .typeSection() + .getSubType(typeIdx) + .compType() + .arrayType(); var elements = new long[len]; + var elementRefs = new Object[len]; + boolean isGcRef = at.fieldType().storageType().isObjectRef(); for (int i = len - 1; i >= 0; i--) { - elements[i] = stack.pop()[0]; + var entry = stack.pop(); + if (isGcRef) { + elementRefs[i] = entry.ref(); + } else { + elements[i] = entry.longValue(); + } } - var array = new WasmArray(typeIdx, elements); - var refId = instance.registerGcRef(array); - stack.push(new long[] {refId}); + var array = new WasmArray(typeIdx, elements, elementRefs); + stack.push(new ConstantResult(new long[] {0}, array)); break; } case ANY_CONVERT_EXTERN: case EXTERN_CONVERT_ANY: { - // Identity operations at runtime break; } case END: @@ -210,6 +280,10 @@ public static long[] computeConstantValue(Instance instance, List e return stack.pop(); } + private static ConstantResult longResult(long value) { + return new ConstantResult(new long[] {value}, null); + } + public static Instance computeConstantInstance(Instance instance, List expr) { for (Instruction instruction : expr) { if (instruction.opcode() == GLOBAL_GET) { diff --git a/runtime/src/main/java/run/endive/runtime/ExportFunction.java b/runtime/src/main/java/run/endive/runtime/ExportFunction.java index 82ccd2795..8cfec8561 100644 --- a/runtime/src/main/java/run/endive/runtime/ExportFunction.java +++ b/runtime/src/main/java/run/endive/runtime/ExportFunction.java @@ -8,4 +8,9 @@ @FunctionalInterface public interface ExportFunction { long[] apply(long... args) throws WasmEngineException; + + /** Invoke this exported function with separate numeric and Object ref arguments, returning a {@link CallResult}. */ + default CallResult applyWithRefs(long[] args, Object[] refArgs) throws WasmEngineException { + throw new UnsupportedOperationException("This function does not support applyWithRefs"); + } } diff --git a/runtime/src/main/java/run/endive/runtime/GlobalInstance.java b/runtime/src/main/java/run/endive/runtime/GlobalInstance.java index 48a93af9e..8dc045448 100644 --- a/runtime/src/main/java/run/endive/runtime/GlobalInstance.java +++ b/runtime/src/main/java/run/endive/runtime/GlobalInstance.java @@ -8,6 +8,7 @@ public class GlobalInstance { private long valueLow; private long valueHigh; + private Object refValue; private final ValType valType; private Instance instance; private final MutabilityType mutabilityType; @@ -88,4 +89,12 @@ public void setInstance(Instance instance) { public MutabilityType getMutabilityType() { return mutabilityType; } + + public Object getRefValue() { + return refValue; + } + + public void setRefValue(Object ref) { + this.refValue = ref; + } } diff --git a/runtime/src/main/java/run/endive/runtime/Instance.java b/runtime/src/main/java/run/endive/runtime/Instance.java index 2d4a70085..c18d2319d 100644 --- a/runtime/src/main/java/run/endive/runtime/Instance.java +++ b/runtime/src/main/java/run/endive/runtime/Instance.java @@ -2,6 +2,7 @@ import static java.util.Objects.requireNonNullElse; import static java.util.Objects.requireNonNullElseGet; +import static run.endive.runtime.ConstantEvaluators.computeConstant; import static run.endive.runtime.ConstantEvaluators.computeConstantInstance; import static run.endive.runtime.ConstantEvaluators.computeConstantValue; import static run.endive.wasm.types.ExternalType.FUNCTION; @@ -17,7 +18,6 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; -import run.endive.runtime.internal.GcRefStore; import run.endive.wasm.InvalidException; import run.endive.wasm.UninstantiableException; import run.endive.wasm.UnlinkableException; @@ -73,17 +73,24 @@ public class Instance { private final Exports fluentExports; private final Map exnRefs; - private final GcRefStore gcRefs; private TailCallPending tailCallPending; static final class TailCallPending { final int funcId; final long[] args; + final Object[] refArgs; TailCallPending(int funcId, long[] args) { this.funcId = funcId; this.args = args; + this.refArgs = null; + } + + TailCallPending(int funcId, long[] args, Object[] refArgs) { + this.funcId = funcId; + this.args = args; + this.refArgs = refArgs; } } @@ -129,16 +136,21 @@ static final class TailCallPending { this.fluentExports = new Exports(this); this.exnRefs = new HashMap<>(); - this.gcRefs = new GcRefStore(this); for (int i = 0; i < tables.length; i++) { - long rawValue = computeConstantValue(this, tables[i].initialize())[0]; - var initValue = OpcodeImpl.boxForTable(rawValue, this); + var result = computeConstant(this, tables[i].initialize()); + int initValue = (int) result.longValue(); if (tableFactory != null) { this.tables[i] = tableFactory.create(tables[i], initValue); } else { this.tables[i] = new TableInstance(tables[i], initValue); } + if (tables[i].elementType().isObjectRef() && result.ref() != null) { + var tbl = this.tables[i]; + for (int j = 0; j < tbl.size(); j++) { + tbl.setObjRef(j, result.ref(), this); + } + } } if (initialize) { @@ -151,7 +163,8 @@ public Instance initialize(boolean start) { // because segment offsets can reference local globals via global.get. for (var i = 0; i < globalInitializers.length; i++) { var g = globalInitializers[i]; - var values = computeConstantValue(this, g.initInstructions()); + var result = computeConstant(this, g.initInstructions()); + var values = result.longs(); if (globalFactory != null) { globals[i] = globalFactory.create( @@ -168,6 +181,9 @@ public Instance initialize(boolean start) { g.mutabilityType()); } globals[i].setInstance(this); + if (g.valueType().isReference()) { + globals[i].setRefValue(result.ref()); + } } for (var el : elements) { @@ -181,14 +197,20 @@ public Instance initialize(boolean start) { || (offset + initializers.size() - 1) >= table.size()) { throw new UninstantiableException("out of bounds table access"); } + boolean isObjRefTable = table.elementType().isObjectRef(); for (int i = 0; i < initializers.size(); i++) { final List init = initializers.get(i); int index = offset + i; - var value = computeConstantValue(this, init); var inst = computeConstantInstance(this, init); assert ae.type().isReference(); - table.setRef(index, OpcodeImpl.boxForTable(value[0], this), inst); + if (isObjRefTable) { + var result = computeConstant(this, init); + table.setObjRef(index, result.ref(), inst); + } else { + var value = computeConstantValue(this, init); + table.setRef(index, (int) value[0], inst); + } } } } @@ -233,9 +255,6 @@ public Instance initialize(boolean start) { } } - // Safe point: wasm stack is empty after init - gcSafePoint(); - return this; } @@ -268,11 +287,25 @@ private Export getExport(ExternalType type, String name) throws InvalidException public ExportFunction function(String name) { var export = getExport(FUNCTION, name); - return args -> { - try { + var funcType = instance.type(instance.functionType(export.index())); + boolean hasGcRefParams = funcType.hasObjectRefParams(); + boolean hasGcRefReturns = funcType.hasObjectRefReturns(); + return new ExportFunction() { + @Override + public long[] apply(long... args) { + if (hasGcRefParams || hasGcRefReturns) { + throw new UnsupportedOperationException( + "Function '" + + name + + "' uses GC reference types." + + " Use applyWithRefs()."); + } return instance.machine.call(export.index(), args); - } finally { - instance.gcSafePoint(); + } + + @Override + public CallResult applyWithRefs(long[] args, Object[] refArgs) { + return instance.machine.callWithRefs(export.index(), args, refArgs); } }; } @@ -429,20 +462,23 @@ public WasmException exn(int idx) { return exnRefs.get(idx); } + @Deprecated public long[] array(int idx) { - var gcRef = gcRefs.get(idx); - if (gcRef instanceof WasmArray) { - return ((WasmArray) gcRef).elements(); - } - return null; + throw new UnsupportedOperationException( + "GcRefStore has been removed. Use applyWithRefs() to get WasmArray objects" + + " directly."); } + @Deprecated public int registerGcRef(WasmGcRef ref) { - return gcRefs.put(ref); + throw new UnsupportedOperationException( + "GcRefStore has been removed. GC references are managed by Java GC."); } + @Deprecated public WasmGcRef gcRef(int idx) { - return gcRefs.get(idx); + throw new UnsupportedOperationException( + "GcRefStore has been removed. Use applyWithRefs() to get GC references directly."); } public boolean heapTypeMatch( @@ -467,15 +503,36 @@ public boolean heapTypeMatch( var funcTypeIdx = functionType((int) ref); return heapTypeSubOf(funcTypeIdx, targetHeapType); } - // ANY hierarchy: i31, struct, array, or internalized externref - if (Value.isI31(ref)) { - return heapTypeSubOf(ValType.TypeIdxCode.I31.code(), targetHeapType); + // Concrete function type source (sourceHeapType >= 0 and is a func type) + if (sourceHeapType >= 0 + && module.typeSection() != null + && module.typeSection().getSubType(sourceHeapType).compType().funcType() != null) { + var funcTypeIdx = functionType((int) ref); + return heapTypeSubOf(funcTypeIdx, targetHeapType); + } + // For non-GC ref values (int on JVM), the only remaining possibility is externref. + // In the new GC design, GC refs are Objects and should not reach this method. + // Return false for type mismatches. + return false; + } + + public boolean heapTypeMatchRef( + Object ref, boolean nullable, int targetHeapType, int sourceHeapType) { + if (ref == null) { + return nullable; + } + if (targetHeapType == ValType.TypeIdxCode.NONE.code() + || targetHeapType == ValType.TypeIdxCode.NOFUNC.code() + || targetHeapType == ValType.TypeIdxCode.NOEXTERN.code()) { + return false; + } + if (targetHeapType == ValType.TypeIdxCode.FUNC.code() + || targetHeapType == ValType.TypeIdxCode.EXTERN.code()) { + return true; } - var gc = gcRef((int) ref); - if (gc != null) { - return heapTypeSubOf(gc.typeIdx(), targetHeapType); + if (ref instanceof WasmGcRef) { + return heapTypeSubOf(((WasmGcRef) ref).typeIdx(), targetHeapType); } - // Internalized externref (via any.convert_extern) return targetHeapType == ValType.TypeIdxCode.ANY.code(); } @@ -486,11 +543,6 @@ private boolean heapTypeSubOf(int actual, int target) { return ValType.heapTypeSubtype(actual, target, module.typeSection()); } - /** Epoch-based GC safe point. Call when the wasm stack is guaranteed empty. */ - void gcSafePoint() { - gcRefs.safePoint(); - } - public Machine getMachine() { return machine; } @@ -507,10 +559,18 @@ public long[] tailCallArgs() { return tailCallPending.args; } + public Object[] tailCallRefArgs() { + return tailCallPending.refArgs; + } + public void setTailCall(int funcId, long[] args) { this.tailCallPending = new TailCallPending(funcId, args); } + public void setTailCall(int funcId, long[] args, Object[] refArgs) { + this.tailCallPending = new TailCallPending(funcId, args, refArgs); + } + public void clearTailCall() { this.tailCallPending = null; } diff --git a/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java b/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java index 29c7f7c5d..057ee831c 100644 --- a/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java +++ b/runtime/src/main/java/run/endive/runtime/InterpreterMachine.java @@ -62,7 +62,7 @@ protected void evalDefault( @Override public long[] call(int funcId, long[] args) throws WasmEngineException { - return call(stack, instance, callStack, funcId, args, null, true); + return call(stack, instance, callStack, funcId, args, null, null, true); } protected long[] call( @@ -71,6 +71,7 @@ protected long[] call( Deque callStack, int funcId, long[] args, + Object[] refArgs, FunctionType callType, boolean popResults) throws WasmEngineException { @@ -83,6 +84,22 @@ protected long[] call( verifyIndirectCall(type, callType, instance.module().typeSection()); } + // When called via call(int, long[]) with no refArgs and the function + // has externref params, populate refArgs from longs so the ref stack + // is set up correctly. Uses WasmExternRef (the proper externref type). + if (refArgs == null && type.hasObjectRefParams()) { + refArgs = new Object[args.length]; + int slot = 0; + for (int pi = 0; pi < type.params().size(); pi++) { + var param = type.params().get(pi); + if (param.isObjectRef()) { + long val = args[slot]; + refArgs[slot] = (val == REF_NULL_VALUE) ? null : new WasmExternRef(val); + } + slot += param.equals(ValType.V128) ? 2 : 1; + } + } + var func = instance.function(funcId); if (func != null) { var stackFrame = @@ -90,6 +107,7 @@ protected long[] call( instance, funcId, args, + refArgs, type.params(), func.localTypes(), func.instructions()); @@ -113,12 +131,30 @@ protected long[] call( var imprt = instance.imports().function(funcId); try { - var results = imprt.handle().apply(instance, args); - // a host function can return null or an array of ints - // which we will push onto the stack - if (results != null) { - for (var result : results) { - stack.push(result); + boolean hasObjectRefs = type.hasObjectRefParams() || type.hasObjectRefReturns(); + if (hasObjectRefs) { + var cr = imprt.handle().applyWithRefs(instance, args, refArgs); + int slot = 0; + for (int ri = 0; ri < type.returns().size(); ri++) { + var retType = type.returns().get(ri); + if (retType.isObjectRef()) { + stack.pushRef(cr.refResult(slot)); + slot++; + } else if (retType.equals(ValType.V128)) { + stack.push(cr.longResult(slot)); + stack.push(cr.longResult(slot + 1)); + slot += 2; + } else { + stack.push(cr.longResult(slot)); + slot++; + } + } + } else { + var results = imprt.handle().apply(instance, args); + if (results != null) { + for (var result : results) { + stack.push(result); + } } } } catch (WasmException e) { @@ -145,12 +181,69 @@ protected long[] call( var totalResults = sizeOf(type.returns()); var results = new long[totalResults]; - for (var i = totalResults - 1; i >= 0; i--) { - results[i] = stack.pop(); + int slot = totalResults; + for (int r = type.returns().size() - 1; r >= 0; r--) { + var retType = type.returns().get(r); + if (retType.isObjectRef()) { + slot--; + // Object refs are on the ref stack; callers wanting the actual + // Object should use callWithRefs(). For the long[] path, extract + // the long value for backward compat with externref host functions. + var ref = stack.popRef(); + // Object refs are on the ref stack; callers wanting the actual + // Object should use callWithRefs(). For the long[] path, return 0 + // (non-null indicator) or REF_NULL_VALUE. + results[slot] = (ref == null) ? Value.REF_NULL_VALUE : 0; + } else if (retType.equals(ValType.V128)) { + slot -= 2; + results[slot + 1] = stack.pop(); + results[slot] = stack.pop(); + } else { + slot--; + results[slot] = stack.pop(); + } } return results; } + @Override + public CallResult callWithRefs(int funcId, long[] args, Object[] refArgs) + throws WasmEngineException { + checkInterruption(); + var typeId = instance.functionType(funcId); + var type = instance.type(typeId); + + call(stack, instance, callStack, funcId, args, refArgs, null, false); + + if (type.returns().isEmpty() || stack.size() == 0) { + return new CallResult(null, null); + } + + var totalResults = sizeOf(type.returns()); + var longResults = new long[totalResults]; + Object[] refResults = null; + int slot = totalResults; + for (int r = type.returns().size() - 1; r >= 0; r--) { + var retType = type.returns().get(r); + if (retType.isObjectRef()) { + slot--; + var ref = stack.popRef(); + if (refResults == null) { + refResults = new Object[totalResults]; + } + refResults[slot] = ref; + } else if (retType.equals(ValType.V128)) { + slot -= 2; + longResults[slot + 1] = stack.pop(); + longResults[slot] = stack.pop(); + } else { + slot--; + longResults[slot] = stack.pop(); + } + } + return new CallResult(longResults, refResults); + } + protected Instance instance() { return instance; } @@ -170,16 +263,6 @@ protected void eval(MStack stack, Instance instance, Deque callStack return; } var instruction = frame.loadCurrentInstruction(); - // LOGGER.log( - // System.Logger.Level.DEBUG, - // "func=" - // + frame.funcId - // + "@" - // + frame.pc - // + ": " - // + instruction - // + " stack=" - // + stack); var opcode = instruction.opcode(); Operands operands = instruction::operand; instance.onExecution(instruction, stack); @@ -254,8 +337,10 @@ protected void eval(MStack stack, Instance instance, Deque callStack var tag = instance.tag(tagNumber); var type = instance.type(tag.tagType().typeIdx()); - var args = extractArgsForParams(stack, type.params()); - var exception = new WasmException(instance, tagNumber, args); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; + var exception = new WasmException(instance, tagNumber, args, refArgs); var exceptionIdx = instance.registerException(exception); frame = THROW_REF(instance, exceptionIdx, stack, frame, callStack); break; @@ -832,7 +917,7 @@ protected void eval(MStack stack, Instance instance, Deque callStack stack.push(operands.get(0)); break; case REF_NULL: - REF_NULL(stack); + REF_NULL(stack, operands); break; case REF_IS_NULL: REF_IS_NULL(stack); @@ -1064,13 +1149,13 @@ protected void eval(MStack stack, Instance instance, Deque callStack ARRAY_SET(stack, instance, operands); break; case ARRAY_LEN: - ARRAY_LEN(stack, instance); + ARRAY_LEN(stack); break; case ARRAY_FILL: ARRAY_FILL(stack, instance, operands); break; case ARRAY_COPY: - ARRAY_COPY(stack, instance); + ARRAY_COPY(stack, instance, operands); break; case ARRAY_INIT_DATA: ARRAY_INIT_DATA(stack, instance, operands); @@ -1093,9 +1178,10 @@ protected void eval(MStack stack, Instance instance, Deque callStack BR_ON_CAST_FAIL(stack, instance, frame, instruction, operands); break; case ANY_CONVERT_EXTERN: + ANY_CONVERT_EXTERN(stack); + break; case EXTERN_CONVERT_ANY: - // Identity operation at runtime: the value representation is the same - // for externref and anyref. No wrapping needed. + EXTERN_CONVERT_ANY(stack); break; default: { @@ -1774,8 +1860,16 @@ private static void F32_CONVERT_I64_S(MStack stack) { stack.push(Value.floatToLong(OpcodeImpl.F32_CONVERT_I64_S(tos))); } - private static void REF_NULL(MStack stack) { - stack.push(REF_NULL_VALUE); + private static void REF_NULL(MStack stack, Operands operands) { + var heapType = (int) operands.get(0); + if (heapType == ValType.TypeIdxCode.FUNC.code() + || heapType == ValType.TypeIdxCode.NOFUNC.code() + || heapType == ValType.TypeIdxCode.EXN.code()) { + stack.push(REF_NULL_VALUE); + } else { + // GC refs, externref, noexternref all use Object null + stack.pushRef(null); + } } private static void ELEM_DROP(Instance instance, Operands operands) { @@ -1784,16 +1878,33 @@ private static void ELEM_DROP(Instance instance, Operands operands) { } private static void REF_IS_NULL(MStack stack) { - var val = stack.pop(); - stack.push(((val == REF_NULL_VALUE) ? Value.TRUE : Value.FALSE)); + if (stack.topIsRef()) { + // GC ref position — check if the ref itself is null + Object refObj = stack.popRef(); + stack.push(refObj == null ? Value.TRUE : Value.FALSE); + } else { + // Non-GC ref (funcref) — check the long side + var val = stack.pop(); + stack.push(((val == REF_NULL_VALUE) ? Value.TRUE : Value.FALSE)); + } } private static void REF_AS_NON_NULL(MStack stack) { - var val = stack.pop(); - if (val == REF_NULL_VALUE) { - throw new TrapException("Trapped on ref_as_non_null on null reference"); + if (stack.topIsRef()) { + // GC ref position — check if the ref itself is null + Object refObj = stack.popRef(); + if (refObj == null) { + throw new TrapException("Trapped on ref_as_non_null on null reference"); + } + stack.pushRef(refObj); + } else { + // Non-GC ref (funcref) — check the long side + var val = stack.pop(); + if (val == REF_NULL_VALUE) { + throw new TrapException("Trapped on ref_as_non_null on null reference"); + } + stack.push(val); } - stack.push(val); } private static void DATA_DROP(Instance instance, Operands operands) { @@ -1809,12 +1920,19 @@ private static void F64_CONVERT_I64_S(MStack stack) { private static void TABLE_GROW(MStack stack, Instance instance, Operands operands) { var tableidx = (int) operands.get(0); var table = instance.table(tableidx); + var et = table.elementType(); + boolean isObjRefTable = et.isObjectRef(); var size = (int) stack.pop(); - var val = OpcodeImpl.boxForTable(stack.pop(), instance); - - var res = table.grow(size, val, instance); - stack.push(res); + if (isObjRefTable) { + var refVal = stack.popRef(); + var res = table.growWithRef(size, refVal, instance); + stack.push(res); + } else { + var val = (int) stack.pop(); + var res = table.grow(size, val, instance); + stack.push(res); + } } private static void TABLE_SIZE(MStack stack, Instance instance, Operands operands) { @@ -1826,12 +1944,25 @@ private static void TABLE_SIZE(MStack stack, Instance instance, Operands operand private static void TABLE_FILL(MStack stack, Instance instance, Operands operands) { var tableidx = (int) operands.get(0); + var table = instance.table(tableidx); + var et = table.elementType(); + boolean isObjRefTable = et.isObjectRef(); var size = (int) stack.pop(); - var val = OpcodeImpl.boxForTable(stack.pop(), instance); - var offset = (int) stack.pop(); - - OpcodeImpl.TABLE_FILL(instance, tableidx, size, val, offset); + if (isObjRefTable) { + var refVal = stack.popRef(); + var offset = (int) stack.pop(); + if ((long) offset + (long) size > table.size()) { + throw new TrapException("out of bounds table access"); + } + for (int i = 0; i < size; i++) { + table.setObjRef(offset + i, refVal, instance); + } + } else { + var val = (int) stack.pop(); + var offset = (int) stack.pop(); + OpcodeImpl.TABLE_FILL(instance, tableidx, size, val, offset); + } } private static void TABLE_COPY(MStack stack, Instance instance, Operands operands) { @@ -1975,8 +2106,16 @@ protected void CALL(Operands operands) { var type = instance.type(typeId); // given a list of param types, let's pop those params off the stack // and pass as args to the function call - var args = extractArgsForParams(stack, type.params()); - call(stack, instance, callStack, funcId, args, type, false); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + call( + stack, + instance, + callStack, + funcId, + (long[]) extracted[0], + (Object[]) extracted[1], + type, + false); } private void CALL_REF() { @@ -1988,8 +2127,16 @@ private void CALL_REF() { var type = instance.type(typeId); // given a list of param types, let's pop those params off the stack // and pass as args to the function call - var args = extractArgsForParams(stack, type.params()); - call(stack, instance, callStack, funcId, args, type, false); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + call( + stack, + instance, + callStack, + funcId, + (long[]) extracted[0], + (Object[]) extracted[1], + type, + false); } private static void F64_NEG(MStack stack) { @@ -2143,47 +2290,72 @@ private static void I32_LOAD(MStack stack, Instance instance, Operands operands) private static void TABLE_SET(MStack stack, Instance instance, Operands operands) { var idx = (int) operands.get(0); var table = instance.table(idx); - - var value = OpcodeImpl.boxForTable(stack.pop(), instance); - var i = (int) stack.pop(); - table.setRef(i, value, instance); + var et = table.elementType(); + boolean isObjRefTable = et.isObjectRef(); + if (isObjRefTable) { + var refVal = stack.popRef(); + var i = (int) stack.pop(); + table.setObjRef(i, refVal, instance); + } else { + var value = (int) stack.pop(); + var i = (int) stack.pop(); + table.setRef(i, value, instance); + } } private static void TABLE_GET(MStack stack, Instance instance, Operands operands) { var idx = (int) operands.get(0); var table = instance.table(idx); + var et = table.elementType(); + boolean isObjRefTable = et.isObjectRef(); var i = (int) stack.pop(); - var ref = OpcodeImpl.TABLE_GET(instance, idx, i); - stack.push(OpcodeImpl.unboxFromTable(ref, instance, table.elementType())); + if (isObjRefTable) { + stack.pushRef(table.objRef(i)); + } else { + var ref = OpcodeImpl.TABLE_GET(instance, idx, i); + stack.push(ref); + } } private static void GLOBAL_SET(MStack stack, Instance instance, Operands operands) { var id = (int) operands.get(0); - if (!instance.global(id).getType().equals(ValType.V128)) { - var val = stack.pop(); - instance.global(id).setValue(val); - } else { + var globalType = instance.global(id).getType(); + if (globalType.isObjectRef()) { + instance.global(id).setRefValue(stack.popRef()); + } else if (globalType.equals(ValType.V128)) { var high = stack.pop(); var low = stack.pop(); instance.global(id).setValueLow(low); instance.global(id).setValueHigh(high); + } else { + var val = stack.pop(); + instance.global(id).setValue(val); } } private static void GLOBAL_GET(MStack stack, Instance instance, Operands operands) { int idx = (int) operands.get(0); - - stack.push(instance.global(idx).getValueLow()); - if (instance.global(idx).getType().equals(ValType.V128)) { - stack.push(instance.global(idx).getValueHigh()); + var globalType = instance.global(idx).getType(); + if (globalType.isObjectRef()) { + stack.pushRef(instance.global(idx).getRefValue()); + } else { + stack.push(instance.global(idx).getValueLow()); + if (globalType.equals(ValType.V128)) { + stack.push(instance.global(idx).getValueHigh()); + } } } private static void DROP(MStack stack, Operands operands) { - if (operands.get(0) == ValType.ID.V128) { + var typeId = operands.get(0); + if (typeId == ValType.ID.V128) { + stack.pop(); + stack.pop(); + } else if (ValType.builder().fromId(typeId).isObjectRef()) { + stack.popRef(); + } else { stack.pop(); } - stack.pop(); } private static void SELECT(MStack stack, Operands operands) { @@ -2200,6 +2372,14 @@ private static void SELECT(MStack stack, Operands operands) { stack.push(a2); stack.push(a1); } + } else if (ValType.builder().fromId(operands.get(0)).isObjectRef()) { + var b = stack.popRef(); + var a = stack.popRef(); + if (pred == 0) { + stack.pushRef(b); + } else { + stack.pushRef(a); + } } else { var b = stack.pop(); var a = stack.pop(); @@ -2227,6 +2407,14 @@ private static void SELECT_T(MStack stack, Operands operands) { stack.push(a2); stack.push(a1); } + } else if (ValType.builder().fromId(typeId).isObjectRef()) { + var b = stack.popRef(); + var a = stack.popRef(); + if (pred == 0) { + stack.pushRef(b); + } else { + stack.pushRef(a); + } } else { var b = stack.pop(); var a = stack.pop(); @@ -2241,7 +2429,10 @@ private static void SELECT_T(MStack stack, Operands operands) { private static void LOCAL_GET(MStack stack, Operands operands, StackFrame currentStackFrame) { var idx = (int) operands.get(0); var i = currentStackFrame.localIndexOf(idx); - if (currentStackFrame.localType(idx).equals(ValType.V128)) { + var localType = currentStackFrame.localType(idx); + if (localType.isObjectRef()) { + stack.pushRef(currentStackFrame.localRef(i)); + } else if (localType.equals(ValType.V128)) { stack.push(currentStackFrame.local(i)); stack.push(currentStackFrame.local(i + 1)); } else { @@ -2252,7 +2443,10 @@ private static void LOCAL_GET(MStack stack, Operands operands, StackFrame curren private static void LOCAL_SET(MStack stack, Operands operands, StackFrame currentStackFrame) { var idx = (int) operands.get(0); var i = currentStackFrame.localIndexOf(idx); - if (currentStackFrame.localType(idx).equals(ValType.V128)) { + var localType = currentStackFrame.localType(idx); + if (localType.isObjectRef()) { + currentStackFrame.setLocalRef(i, stack.popRef()); + } else if (localType.equals(ValType.V128)) { currentStackFrame.setLocal(i, stack.pop()); currentStackFrame.setLocal(i + 1, stack.pop()); } else { @@ -2264,7 +2458,10 @@ private static void LOCAL_TEE(MStack stack, Operands operands, StackFrame curren // here we peek instead of pop, leaving it on the stack var idx = (int) operands.get(0); var i = currentStackFrame.localIndexOf(idx); - if (currentStackFrame.localType(idx).equals(ValType.V128)) { + var localType = currentStackFrame.localType(idx); + if (localType.isObjectRef()) { + currentStackFrame.setLocalRef(i, stack.peekRef()); + } else if (localType.equals(ValType.V128)) { var tmp = stack.pop(); currentStackFrame.setLocal(i, tmp); currentStackFrame.setLocal(i + 1, stack.peek()); @@ -2643,13 +2840,15 @@ private static StackFrame RETURN_CALL( var typeId = instance.functionType(funcId); var type = instance.type(typeId); var func = instance.function(funcId); - var args = extractArgsForParams(stack, type.params()); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; // optimizing when the tail call happens in the same function if (currentStackFrame.funcId() == funcId) { var ctrlFrame = currentStackFrame.popCtrlTillCall(); StackFrame.doControlTransfer(ctrlFrame, stack); - currentStackFrame.reset(args); + currentStackFrame.reset(args, refArgs); currentStackFrame.pushCtrl(ctrlFrame); return currentStackFrame; } else { @@ -2666,6 +2865,7 @@ private static StackFrame RETURN_CALL( instance, funcId, args, + refArgs, type.params(), func.localTypes(), func.instructions()); @@ -2682,12 +2882,15 @@ private static StackFrame RETURN_CALL( var imprt = instance.imports().function(funcId); try { - var results = imprt.handle().apply(instance, args); - // a host function can return null or an array of ints - // which we will push onto the stack - if (results != null) { - for (var result : results) { - stack.push(result); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + var cr = imprt.handle().applyWithRefs(instance, args, refArgs); + pushCallResult(cr, type, stack); + } else { + var results = imprt.handle().apply(instance, args); + if (results != null) { + for (var result : results) { + stack.push(result); + } } } } catch (WasmException e) { @@ -2728,13 +2931,15 @@ private static StackFrame RETURN_CALL_INDIRECT( + refMachine.getName()); } - var args = extractArgsForParams(stack, type.params()); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; // optimizing when the tail call happens in the same function if (currentStackFrame.funcId() == funcId) { var ctrlFrame = currentStackFrame.popCtrlTillCall(); StackFrame.doControlTransfer(ctrlFrame, stack); - currentStackFrame.reset(args); + currentStackFrame.reset(args, refArgs); currentStackFrame.pushCtrl(ctrlFrame); return currentStackFrame; } else { @@ -2752,6 +2957,7 @@ private static StackFrame RETURN_CALL_INDIRECT( instance, funcId, args, + refArgs, type.params(), func.localTypes(), func.instructions()); @@ -2768,12 +2974,15 @@ private static StackFrame RETURN_CALL_INDIRECT( var imprt = instance.imports().function(funcId); try { - var results = imprt.handle().apply(instance, args); - // a host function can return null or an array of ints - // which we will push onto the stack - if (results != null) { - for (var result : results) { - stack.push(result); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + var cr = imprt.handle().applyWithRefs(instance, args, refArgs); + pushCallResult(cr, type, stack); + } else { + var results = imprt.handle().apply(instance, args); + if (results != null) { + for (var result : results) { + stack.push(result); + } } } } catch (WasmException e) { @@ -2801,13 +3010,15 @@ private static StackFrame RETURN_CALL_REF( var func = instance.function(funcId); // given a list of param types, let's pop those params off the stack // and pass as args to the function call - var args = extractArgsForParams(stack, type.params()); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; // optimizing when the tail call happens in the same function if (currentStackFrame.funcId() == funcId) { var ctrlFrame = currentStackFrame.popCtrlTillCall(); StackFrame.doControlTransfer(ctrlFrame, stack); - currentStackFrame.reset(args); + currentStackFrame.reset(args, refArgs); currentStackFrame.pushCtrl(ctrlFrame); return currentStackFrame; } else { @@ -2818,6 +3029,7 @@ private static StackFrame RETURN_CALL_REF( instance, funcId, args, + refArgs, type.params(), func.localTypes(), func.instructions()); @@ -2845,15 +3057,22 @@ private void CALL_INDIRECT( // given a list of param types, let's pop those params off the stack // and pass as args to the function call - var args = extractArgsForParams(stack, type.params()); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; if (useCurrentInstanceInterpreter(instance, refInstance, funcId)) { - call(stack, instance, callStack, funcId, args, null, false); + call(stack, instance, callStack, funcId, args, refArgs, null, false); } else { checkInterruption(); - var results = refInstance.getMachine().call(funcId, args); - if (results != null) { - for (var result : results) { - stack.push(result); + if (type.hasObjectRefParams() || type.hasObjectRefReturns()) { + var cr = refInstance.getMachine().callWithRefs(funcId, args, refArgs); + pushCallResult(cr, type, stack); + } else { + var results = refInstance.getMachine().call(funcId, args); + if (results != null) { + for (var result : results) { + stack.push(result); + } } } } @@ -2937,17 +3156,13 @@ protected static StackFrame THROW_REF( case CATCH: if (currentCatch.tag() == exception.tagIdx() || compatibleImport) { found = true; - for (var arg : exception.args()) { - stack.push(arg); - } + pushExceptionArgs(exception, stack); } break; case CATCH_REF: if (currentCatch.tag() == exception.tagIdx() || compatibleImport) { found = true; - for (var arg : exception.args()) { - stack.push(arg); - } + pushExceptionArgs(exception, stack); stack.push(exceptionIdx); } break; @@ -3060,22 +3275,45 @@ private static void BR_IF(StackFrame frame, MStack stack, AnnotatedInstruction i private static void BR_ON_NULL( StackFrame frame, MStack stack, AnnotatedInstruction instruction) { - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { - BR(frame, stack, instruction); + if (stack.topIsRef()) { + // GC ref position — check if the ref itself is null + Object refObj = stack.popRef(); + if (refObj == null) { + // null GC ref — branch + BR(frame, stack, instruction); + } else { + // non-null GC ref — keep it and fall through + stack.pushRef(refObj); + } } else { - stack.push(ref); + // Non-GC ref (funcref/externref) — check the long side + var val = stack.pop(); + if (val == REF_NULL_VALUE) { + BR(frame, stack, instruction); + } else { + stack.push(val); + } } } private static void BR_ON_NON_NULL( StackFrame frame, MStack stack, AnnotatedInstruction instruction) { - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { - // do nothing + if (stack.topIsRef()) { + // GC ref position — check if the ref itself is null + Object refObj = stack.popRef(); + if (refObj != null) { + // non-null GC ref — push it back and branch + stack.pushRef(refObj); + BR(frame, stack, instruction); + } + // null GC ref — drop it and fall through } else { - stack.push(ref); - BR(frame, stack, instruction); + // Non-GC ref (funcref/externref) — check the long side + var val = stack.pop(); + if (val != REF_NULL_VALUE) { + stack.push(val); + BR(frame, stack, instruction); + } } } @@ -3090,6 +3328,45 @@ protected static long[] extractArgsForParams(MStack stack, List params) return args; } + /** + * Extract both long args and Object ref args from the stack for a call. + * Ref-typed params are popped from the ref stack; others from the long stack. + * Returns a 2-element array: [0] = long[] args, [1] = Object[] refArgs (or null if no refs). + */ + protected static Object[] extractArgsAndRefsForParams( + MStack stack, List params, Instance instance) { + if (params == null || params.isEmpty()) { + return new Object[] {Value.EMPTY_VALUES, null}; + } + var size = sizeOf(params); + var args = new long[size]; + boolean hasRefs = false; + for (var p : params) { + if (p.isObjectRef()) { + hasRefs = true; + break; + } + } + Object[] refArgs = hasRefs ? new Object[size] : null; + // Pop in reverse order (last param on top of stack) + int idx = size; + for (int i = params.size() - 1; i >= 0; i--) { + var p = params.get(i); + if (p.equals(ValType.V128)) { + idx -= 2; + args[idx + 1] = stack.pop(); + args[idx] = stack.pop(); + } else if (p.isObjectRef()) { + idx -= 1; + refArgs[idx] = stack.popRef(); + } else { + idx -= 1; + args[idx] = stack.pop(); + } + } + return new Object[] {args, refArgs}; + } + private static boolean functionTypeMatch( FunctionType actual, FunctionType expected, TypeSection ts) { if (actual.params().size() != expected.params().size() @@ -3150,131 +3427,200 @@ private static void checkInterruption() { // ===== GC opcode implementations ===== private static void REF_EQ(MStack stack) { - var b = stack.pop(); - var a = stack.pop(); - stack.push(a == b ? Value.TRUE : Value.FALSE); + var b = stack.popRef(); + var a = stack.popRef(); + boolean eq; + if (a == b) { + eq = true; + } else if (a == null || b == null) { + // one is null, the other is not (both-null caught by a == b) + eq = false; + } else if (a instanceof WasmI31Ref && b instanceof WasmI31Ref) { + eq = a.equals(b); + } else { + eq = false; // identity comparison for structs/arrays + } + stack.push(eq ? Value.TRUE : Value.FALSE); } private static void REF_I31(MStack stack) { var val = (int) stack.pop(); - stack.push(Value.encodeI31(val)); + stack.pushRef(new WasmI31Ref(val)); } private static void I31_GET_S(MStack stack) { - var ref = stack.pop(); - if (ref == REF_NULL_VALUE) { + var ref = stack.popRef(); + if (ref == null) { throw new TrapException("null i31 reference"); } - stack.push(Value.decodeI31S(ref)); + var i31 = (WasmI31Ref) ref; + int val = i31.value(); + // Sign-extend from 31 bits + if ((val & 0x40000000) != 0) { + val |= 0x80000000; + } + stack.push(val); } private static void I31_GET_U(MStack stack) { - var ref = stack.pop(); - if (ref == REF_NULL_VALUE) { + var ref = stack.popRef(); + if (ref == null) { throw new TrapException("null i31 reference"); } - stack.push(Value.decodeI31U(ref)); + var i31 = (WasmI31Ref) ref; + stack.push(i31.value() & 0x7FFFFFFFL); } private static void STRUCT_NEW(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var fields = new long[st.fieldTypes().length]; + var fieldRefs = new Object[st.fieldTypes().length]; // Pop fields in reverse order (last field on top) for (int i = fields.length - 1; i >= 0; i--) { - fields[i] = stack.pop(); + var ft = st.fieldTypes()[i]; + if (ft.storageType().isObjectRef()) { + fieldRefs[i] = stack.popRef(); + } else { + fields[i] = stack.pop(); + } } - var struct = new WasmStruct(typeIdx, fields); - stack.push(instance.registerGcRef(struct)); + var struct = new WasmStruct(typeIdx, fields, fieldRefs); + stack.pushRef(struct); } private static void STRUCT_NEW_DEFAULT(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var fields = new long[st.fieldTypes().length]; - // Default values: 0 for numeric, REF_NULL_VALUE for references + // Default values: 0 for numeric, null for GC refs (already zero-initialized), + // REF_NULL_VALUE for non-GC references (funcref, externref) for (int i = 0; i < fields.length; i++) { var ft = st.fieldTypes()[i]; - if (ft.storageType().valType() != null && ft.storageType().valType().isReference()) { + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { fields[i] = REF_NULL_VALUE; } - // numeric types default to 0 (already zero-initialized) } var struct = new WasmStruct(typeIdx, fields); - stack.push(instance.registerGcRef(struct)); + stack.pushRef(struct); } private static void STRUCT_GET( MStack stack, Instance instance, Operands operands, OpCode opcode) { var typeIdx = (int) operands.get(0); var fieldIdx = (int) operands.get(1); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var structObj = stack.popRef(); + if (structObj == null) { throw new TrapException("null structure reference"); } - var struct = (WasmStruct) instance.gcRef(ref); - var val = struct.field(fieldIdx); + var struct = (WasmStruct) structObj; var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; - if (ft.storageType().packedType() != null) { - if (opcode == OpCode.STRUCT_GET_S) { - val = ft.storageType().packedType().signExtend(val); - } else { - val = val & ft.storageType().packedType().mask(); + if (ft.storageType().isObjectRef()) { + stack.pushRef(struct.fieldRef(fieldIdx)); + } else { + var val = struct.field(fieldIdx); + if (ft.storageType().packedType() != null) { + if (opcode == OpCode.STRUCT_GET_S) { + val = ft.storageType().packedType().signExtend(val); + } else { + val = val & ft.storageType().packedType().mask(); + } } + stack.push(val); } - stack.push(val); } private static void STRUCT_SET(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); var fieldIdx = (int) operands.get(1); - var val = stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { - throw new TrapException("null structure reference"); - } - var struct = (WasmStruct) instance.gcRef(ref); var st = instance.module().typeSection().getSubType(typeIdx).compType().structType(); var ft = st.fieldTypes()[fieldIdx]; - if (ft.storageType().packedType() != null) { - val = val & ft.storageType().packedType().mask(); + boolean isRef = ft.storageType().isObjectRef(); + Object refVal = null; + long val = 0; + if (isRef) { + refVal = stack.popRef(); + } else { + val = stack.pop(); + } + var structObj = stack.popRef(); + if (structObj == null) { + throw new TrapException("null structure reference"); + } + var struct = (WasmStruct) structObj; + if (isRef) { + struct.setFieldRef(fieldIdx, refVal); + } else { + if (ft.storageType().packedType() != null) { + val = val & ft.storageType().packedType().mask(); + } + struct.setField(fieldIdx, val); } - struct.setField(fieldIdx, val); } private static void ARRAY_NEW(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); var len = (int) stack.pop(); - var initVal = stack.pop(); - var elems = new long[len]; - java.util.Arrays.fill(elems, initVal); - var arr = new WasmArray(typeIdx, elems); - stack.push(instance.registerGcRef(arr)); + if (isRef) { + var initRef = stack.popRef(); + var elems = new long[len]; + var elemRefs = new Object[len]; + java.util.Arrays.fill(elemRefs, initRef); + var arr = new WasmArray(typeIdx, elems, elemRefs); + stack.pushRef(arr); + } else { + var initVal = stack.pop(); + var elems = new long[len]; + java.util.Arrays.fill(elems, initVal); + var arr = new WasmArray(typeIdx, elems); + stack.pushRef(arr); + } } private static void ARRAY_NEW_DEFAULT(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); var len = (int) stack.pop(); - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); var elems = new long[len]; - if (at.fieldType().storageType().valType() != null - && at.fieldType().storageType().valType().isReference()) { - java.util.Arrays.fill(elems, REF_NULL_VALUE); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + var ft = at.fieldType(); + if (ft.storageType().valType() != null + && ft.storageType().valType().isReference() + && !ft.storageType().isObjectRef()) { + java.util.Arrays.fill(elems, Value.REF_NULL_VALUE); } var arr = new WasmArray(typeIdx, elems); - stack.push(instance.registerGcRef(arr)); + stack.pushRef(arr); } private static void ARRAY_NEW_FIXED(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); var len = (int) operands.get(1); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); var elems = new long[len]; - for (int i = len - 1; i >= 0; i--) { - elems[i] = stack.pop(); + if (isRef) { + var elemRefs = new Object[len]; + for (int i = len - 1; i >= 0; i--) { + elemRefs[i] = stack.popRef(); + } + var arr = new WasmArray(typeIdx, elems, elemRefs); + stack.pushRef(arr); + } else { + for (int i = len - 1; i >= 0; i--) { + elems[i] = stack.pop(); + } + var arr = new WasmArray(typeIdx, elems); + stack.pushRef(arr); } - var arr = new WasmArray(typeIdx, elems); - stack.push(instance.registerGcRef(arr)); } private static void ARRAY_NEW_DATA(MStack stack, Instance instance, Operands operands) { @@ -3294,7 +3640,7 @@ private static void ARRAY_NEW_DATA(MStack stack, Instance instance, Operands ope elems[i] = readFromData(data, byteOff, elemSize); } var arr = new WasmArray(typeIdx, elems); - stack.push(instance.registerGcRef(arr)); + stack.pushRef(arr); } private static void ARRAY_NEW_ELEM(MStack stack, Instance instance, Operands operands) { @@ -3306,112 +3652,162 @@ private static void ARRAY_NEW_ELEM(MStack stack, Instance instance, Operands ope if (element == null || offset + len > element.elementCount()) { throw new TrapException("out of bounds table access"); } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); var elems = new long[len]; + var elemRefs = new Object[len]; for (int i = 0; i < len; i++) { var init = element.initializers().get(offset + i); - elems[i] = ConstantEvaluators.computeConstantValue(instance, init)[0]; + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + elemRefs[i] = result.ref(); + } else { + elems[i] = result.longValue(); + } } - var arr = new WasmArray(typeIdx, elems); - stack.push(instance.registerGcRef(arr)); + var arr = new WasmArray(typeIdx, elems, elemRefs); + stack.pushRef(arr); } private static void ARRAY_GET( MStack stack, Instance instance, Operands operands, OpCode opcode) { var typeIdx = (int) operands.get(0); var idx = (int) stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } - var val = arr.get(idx); var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().packedType() != null) { - if (opcode == OpCode.ARRAY_GET_S) { - val = at.fieldType().storageType().packedType().signExtend(val); - } else { - val = val & at.fieldType().storageType().packedType().mask(); + if (at.fieldType().storageType().isObjectRef()) { + stack.pushRef(arr.getRef(idx)); + } else { + var val = arr.get(idx); + if (at.fieldType().storageType().packedType() != null) { + if (opcode == OpCode.ARRAY_GET_S) { + val = at.fieldType().storageType().packedType().signExtend(val); + } else { + val = val & at.fieldType().storageType().packedType().mask(); + } } + stack.push(val); } - stack.push(val); } private static void ARRAY_SET(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); - var val = stack.pop(); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); + Object refVal = null; + long val = 0; + if (isRef) { + refVal = stack.popRef(); + } else { + val = stack.pop(); + } var idx = (int) stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; if (idx < 0 || idx >= arr.length()) { throw new TrapException("out of bounds array access"); } - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().packedType() != null) { - val = val & at.fieldType().storageType().packedType().mask(); + if (isRef) { + arr.setRef(idx, refVal); + } else { + if (at.fieldType().storageType().packedType() != null) { + val = val & at.fieldType().storageType().packedType().mask(); + } + arr.set(idx, val); } - arr.set(idx, val); } - private static void ARRAY_LEN(MStack stack, Instance instance) { - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + private static void ARRAY_LEN(MStack stack) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; stack.push(arr.length()); } private static void ARRAY_FILL(MStack stack, Instance instance, Operands operands) { var typeIdx = (int) operands.get(0); + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = + at.fieldType().storageType().valType() != null + && at.fieldType().storageType().isObjectRef(); var len = (int) stack.pop(); - var val = stack.pop(); + Object refVal = null; + long val = 0; + if (isRef) { + refVal = stack.popRef(); + } else { + val = stack.pop(); + } var offset = (int) stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; if (offset + len > arr.length()) { throw new TrapException("out of bounds array access"); } - var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); - if (at.fieldType().storageType().packedType() != null) { - val = val & at.fieldType().storageType().packedType().mask(); - } - for (int i = 0; i < len; i++) { - arr.set(offset + i, val); + if (isRef) { + for (int i = 0; i < len; i++) { + arr.setRef(offset + i, refVal); + } + } else { + if (at.fieldType().storageType().packedType() != null) { + val = val & at.fieldType().storageType().packedType().mask(); + } + for (int i = 0; i < len; i++) { + arr.set(offset + i, val); + } } } - private static void ARRAY_COPY(MStack stack, Instance instance) { - // operands 0 and 1 are dst/src type indices (used for validation, not needed at runtime) + private static void ARRAY_COPY(MStack stack, Instance instance, Operands operands) { + var dstTypeIdx = (int) operands.get(0); var len = (int) stack.pop(); var srcOffset = (int) stack.pop(); - var srcRef = (int) stack.pop(); + var srcObj = stack.popRef(); var dstOffset = (int) stack.pop(); - var dstRef = (int) stack.pop(); - if (dstRef == REF_NULL_VALUE || srcRef == REF_NULL_VALUE) { + var dstObj = stack.popRef(); + if (dstObj == null || srcObj == null) { throw new TrapException("null array reference"); } - var dst = (WasmArray) instance.gcRef(dstRef); - var src = (WasmArray) instance.gcRef(srcRef); + var dst = (WasmArray) dstObj; + var src = (WasmArray) srcObj; if (dstOffset + len > dst.length() || srcOffset + len > src.length()) { throw new TrapException("out of bounds array access"); } + var dstAt = instance.module().typeSection().getSubType(dstTypeIdx).compType().arrayType(); + boolean isRef = dstAt.fieldType().storageType().isObjectRef(); // Handle overlapping copies if (dstOffset <= srcOffset) { for (int i = 0; i < len; i++) { - dst.set(dstOffset + i, src.get(srcOffset + i)); + if (isRef) { + dst.setRef(dstOffset + i, src.getRef(srcOffset + i)); + } else { + dst.set(dstOffset + i, src.get(srcOffset + i)); + } } } else { for (int i = len - 1; i >= 0; i--) { - dst.set(dstOffset + i, src.get(srcOffset + i)); + if (isRef) { + dst.setRef(dstOffset + i, src.getRef(srcOffset + i)); + } else { + dst.set(dstOffset + i, src.get(srcOffset + i)); + } } } } @@ -3422,11 +3818,11 @@ private static void ARRAY_INIT_DATA(MStack stack, Instance instance, Operands op var len = (int) stack.pop(); var srcOffset = (int) stack.pop(); var dstOffset = (int) stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); var elemSize = at.fieldType().storageType().byteSize(); var data = instance.dataSegmentData(dataIdx); @@ -3443,16 +3839,16 @@ private static void ARRAY_INIT_DATA(MStack stack, Instance instance, Operands op } private static void ARRAY_INIT_ELEM(MStack stack, Instance instance, Operands operands) { - // operand 0 is the type index (used for validation, not needed at runtime) + var typeIdx = (int) operands.get(0); var elemIdx = (int) operands.get(1); var len = (int) stack.pop(); var srcOffset = (int) stack.pop(); var dstOffset = (int) stack.pop(); - var ref = (int) stack.pop(); - if (ref == REF_NULL_VALUE) { + var arrObj = stack.popRef(); + if (arrObj == null) { throw new TrapException("null array reference"); } - var arr = (WasmArray) instance.gcRef(ref); + var arr = (WasmArray) arrObj; var element = instance.element(elemIdx); if (dstOffset + len > arr.length()) { throw new TrapException("out of bounds array access"); @@ -3465,34 +3861,109 @@ private static void ARRAY_INIT_ELEM(MStack stack, Instance instance, Operands op if (len == 0) { return; } + var at = instance.module().typeSection().getSubType(typeIdx).compType().arrayType(); + boolean isRef = at.fieldType().storageType().isObjectRef(); for (int i = 0; i < len; i++) { var init = element.initializers().get(srcOffset + i); - arr.set(dstOffset + i, ConstantEvaluators.computeConstantValue(instance, init)[0]); + var result = ConstantEvaluators.computeConstant(instance, init); + if (isRef) { + arr.setRef(dstOffset + i, result.ref()); + } else { + arr.set(dstOffset + i, result.longValue()); + } + } + } + + private static void pushCallResult(CallResult cr, FunctionType type, MStack stack) { + if (cr == null) { + return; + } + int slot = 0; + for (int i = 0; i < type.returns().size(); i++) { + var retType = type.returns().get(i); + if (retType.isObjectRef()) { + stack.pushRef(cr.refResult(slot)); + slot++; + } else if (retType.equals(ValType.V128)) { + stack.push(cr.longResult(slot)); + stack.push(cr.longResult(slot + 1)); + slot += 2; + } else { + stack.push(cr.longResult(slot)); + slot++; + } + } + } + + private static void pushExceptionArgs(WasmException exception, MStack stack) { + var refArgs = exception.refArgs(); + var tag = exception.instance().tag(exception.tagIdx()); + var tagType = exception.instance().type(tag.tagType().typeIdx()); + var params = tagType.params(); + int slot = 0; + for (int i = 0; i < params.size(); i++) { + var p = params.get(i); + if (p.isObjectRef()) { + // This position is a ref (even if the value is null) + stack.pushRef(refArgs != null && slot < refArgs.length ? refArgs[slot] : null); + slot++; + } else if (p.equals(ValType.V128)) { + stack.push(exception.args()[slot]); + stack.push(exception.args()[slot + 1]); + slot += 2; + } else { + stack.push(exception.args()[slot]); + slot++; + } } } + private static boolean isSourceGcRef(int sourceHeapType) { + return sourceHeapType != ValType.TypeIdxCode.FUNC.code() + && sourceHeapType != ValType.TypeIdxCode.NOFUNC.code() + && sourceHeapType != ValType.TypeIdxCode.EXTERN.code() + && sourceHeapType != ValType.TypeIdxCode.NOEXTERN.code() + && sourceHeapType != ValType.TypeIdxCode.EXN.code(); + } + private static void REF_TEST( MStack stack, Instance instance, Operands operands, OpCode opcode) { var heapType = (int) operands.get(0); var sourceHeapType = (int) operands.get(1); - var ref = stack.pop(); boolean nullable = (opcode == OpCode.REF_TEST_NULL); - stack.push( - instance.heapTypeMatch(ref, nullable, heapType, sourceHeapType) - ? Value.TRUE - : Value.FALSE); + if (isSourceGcRef(sourceHeapType)) { + var ref = stack.popRef(); + stack.push( + instance.heapTypeMatchRef(ref, nullable, heapType, sourceHeapType) + ? Value.TRUE + : Value.FALSE); + } else { + var val = stack.pop(); + stack.push( + instance.heapTypeMatch(val, nullable, heapType, sourceHeapType) + ? Value.TRUE + : Value.FALSE); + } } private static void CAST_TEST( MStack stack, Instance instance, Operands operands, OpCode opcode) { var heapType = (int) operands.get(0); var sourceHeapType = (int) operands.get(1); - var ref = stack.pop(); boolean nullable = (opcode == OpCode.CAST_TEST_NULL); - if (!instance.heapTypeMatch(ref, nullable, heapType, sourceHeapType)) { - throw new TrapException("cast failure"); + if (isSourceGcRef(sourceHeapType)) { + var ref = stack.popRef(); + if (!instance.heapTypeMatchRef(ref, nullable, heapType, sourceHeapType)) { + throw new TrapException("cast failure"); + } + stack.pushRef(ref); + } else { + var val = stack.pop(); + if (!instance.heapTypeMatch(val, nullable, heapType, sourceHeapType)) { + throw new TrapException("cast failure"); + } + stack.push(val); } - stack.push(ref); } private static void BR_ON_CAST( @@ -3505,13 +3976,24 @@ private static void BR_ON_CAST( var ht2 = (int) operands.get(3); var sourceHeapType = (int) operands.get(4); boolean null2 = (flags & 2) != 0; - var ref = stack.pop(); - if (instance.heapTypeMatch(ref, null2, ht2, sourceHeapType)) { - stack.push(ref); - ctrlJump(frame, stack, (int) operands.get(1)); - frame.jumpTo(instruction.labelTrue()); + if (isSourceGcRef(sourceHeapType)) { + var ref = stack.popRef(); + if (instance.heapTypeMatchRef(ref, null2, ht2, sourceHeapType)) { + stack.pushRef(ref); + ctrlJump(frame, stack, (int) operands.get(1)); + frame.jumpTo(instruction.labelTrue()); + } else { + stack.pushRef(ref); + } } else { - stack.push(ref); + var val = stack.pop(); + if (instance.heapTypeMatch(val, null2, ht2, sourceHeapType)) { + stack.push(val); + ctrlJump(frame, stack, (int) operands.get(1)); + frame.jumpTo(instruction.labelTrue()); + } else { + stack.push(val); + } } } @@ -3525,16 +4007,39 @@ private static void BR_ON_CAST_FAIL( var ht2 = (int) operands.get(3); var sourceHeapType = (int) operands.get(4); boolean null2 = (flags & 2) != 0; - var ref = stack.pop(); - if (!instance.heapTypeMatch(ref, null2, ht2, sourceHeapType)) { - stack.push(ref); - ctrlJump(frame, stack, (int) operands.get(1)); - frame.jumpTo(instruction.labelTrue()); + if (isSourceGcRef(sourceHeapType)) { + var ref = stack.popRef(); + if (!instance.heapTypeMatchRef(ref, null2, ht2, sourceHeapType)) { + stack.pushRef(ref); + ctrlJump(frame, stack, (int) operands.get(1)); + frame.jumpTo(instruction.labelTrue()); + } else { + stack.pushRef(ref); + } } else { - stack.push(ref); + var val = stack.pop(); + if (!instance.heapTypeMatch(val, null2, ht2, sourceHeapType)) { + stack.push(val); + ctrlJump(frame, stack, (int) operands.get(1)); + frame.jumpTo(instruction.labelTrue()); + } else { + stack.push(val); + } } } + private static void ANY_CONVERT_EXTERN(MStack stack) { + // Both externref and anyref are Object on the stack — identity + var ref = stack.popRef(); + stack.pushRef(ref); + } + + private static void EXTERN_CONVERT_ANY(MStack stack) { + // Both anyref and externref are Object on the stack — identity + var ref = stack.popRef(); + stack.pushRef(ref); + } + private static long readFromData(byte[] data, int offset, int size) { long val = 0; for (int i = 0; i < size; i++) { diff --git a/runtime/src/main/java/run/endive/runtime/MStack.java b/runtime/src/main/java/run/endive/runtime/MStack.java index 40d002556..b3f3babe9 100644 --- a/runtime/src/main/java/run/endive/runtime/MStack.java +++ b/runtime/src/main/java/run/endive/runtime/MStack.java @@ -1,10 +1,17 @@ package run.endive.runtime; +import static run.endive.wasm.types.Value.REF_NULL_VALUE; + public class MStack { public static final int MIN_CAPACITY = 8; + // Sentinel to distinguish "null GC ref" from "no ref at this position" in refs[]. + // Package-private: used by MStack and StackFrame. + static final Object NULL_REF = new Object(); + private int count; private long[] elements; + private Object[] refs; public MStack() { this.elements = new long[MIN_CAPACITY]; @@ -15,8 +22,13 @@ private void increaseCapacity() { final long[] array = new long[newCapacity]; System.arraycopy(elements, 0, array, 0, elements.length); - elements = array; + + if (refs != null) { + final Object[] refArray = new Object[newCapacity]; + System.arraycopy(refs, 0, refArray, 0, refs.length); + refs = refArray; + } } // internal use only! @@ -24,7 +36,14 @@ public long[] array() { return elements; } + public Object[] refArray() { + return refs; + } + public void push(long v) { + if (refs != null) { + refs[count] = null; + } elements[count] = v; count++; @@ -33,16 +52,66 @@ public void push(long v) { } } + public void pushRef(Object ref) { + if (refs == null) { + refs = new Object[elements.length]; + } + elements[count] = (ref == null) ? REF_NULL_VALUE : 0; + refs[count] = (ref == null) ? NULL_REF : ref; + count++; + + if (count == elements.length) { + increaseCapacity(); + } + } + public long pop() { count--; + if (refs != null) { + refs[count] = null; + } return elements[count]; } + public Object popRef() { + count--; + if (refs == null) { + return null; + } + Object ref = refs[count]; + refs[count] = null; + return (ref == NULL_REF) ? null : ref; + } + public long peek() { return elements[count - 1]; } + public Object peekRef() { + if (refs == null) { + return null; + } + Object ref = refs[count - 1]; + return (ref == NULL_REF) ? null : ref; + } + + /** + * Returns true if the top-of-stack position holds a ref (including null GC refs). + * Unlike peekRef(), this distinguishes "null GC ref" from "no ref at this position". + */ + public boolean topIsRef() { + return refs != null && refs[count - 1] != null; + } + public int size() { return count; } + + public void clearRefsTo(int newSize) { + if (refs != null) { + for (int i = count - 1; i >= newSize; i--) { + refs[i] = null; + } + } + } } diff --git a/runtime/src/main/java/run/endive/runtime/Machine.java b/runtime/src/main/java/run/endive/runtime/Machine.java index d981d58ae..b844c7061 100644 --- a/runtime/src/main/java/run/endive/runtime/Machine.java +++ b/runtime/src/main/java/run/endive/runtime/Machine.java @@ -6,4 +6,14 @@ public interface Machine { long[] call(int funcId, long[] args) throws WasmEngineException; + + default long[] call(int funcId, long[] args, Object[] refArgs) throws WasmEngineException { + return call(funcId, args); + } + + /** Call function {@code funcId} with separate numeric and Object ref arguments, returning a {@link CallResult}. */ + default CallResult callWithRefs(int funcId, long[] args, Object[] refArgs) + throws WasmEngineException { + return new CallResult(call(funcId, args, refArgs), null); + } } diff --git a/runtime/src/main/java/run/endive/runtime/OpcodeImpl.java b/runtime/src/main/java/run/endive/runtime/OpcodeImpl.java index f0edaae9f..c7643607c 100644 --- a/runtime/src/main/java/run/endive/runtime/OpcodeImpl.java +++ b/runtime/src/main/java/run/endive/runtime/OpcodeImpl.java @@ -8,7 +8,6 @@ import run.endive.wasm.types.OpCode; import run.endive.wasm.types.PassiveElement; import run.endive.wasm.types.ValType; -import run.endive.wasm.types.Value; /** * Note: Some opcodes are easy or trivial to implement as compiler intrinsics (local.get, i32.add, etc). @@ -820,17 +819,25 @@ public static void TABLE_COPY( throw new WasmRuntimeException("out of bounds table access"); } + boolean isObjRefTable = dest.elementType().isObjectRef(); + for (int i = size - 1; i >= 0; i--) { if (d <= s) { - var val = src.ref(s); var inst = src.instance(s); - dest.setRef(d, (int) val, inst); + if (isObjRefTable) { + dest.setObjRef(d, src.objRef(s), inst); + } else { + dest.setRef(d, src.ref(s), inst); + } s++; d++; } else { - var val = src.ref(s + i); var inst = src.instance(s + i); - dest.setRef(d + i, (int) val, inst); + if (isObjRefTable) { + dest.setObjRef(d + i, src.objRef(s + i), inst); + } else { + dest.setRef(d + i, src.ref(s + i), inst); + } } } } @@ -861,18 +868,22 @@ public static void TABLE_INIT( } int end = (int) endL; + boolean isObjRefTable = table.elementType().isObjectRef(); for (int i = offset; i < end; i++) { var elem = instance.element(elementidx); - var val = - boxForTable( - computeConstantValue(instance, elem.initializers().get(elemidx++))[0], - instance); - if (table.elementType().equals(ValType.FuncRef)) { - if (val > instance.functionCount()) { - throw new WasmRuntimeException("out of bounds table access"); - } - table.setRef(i, val, instance); + if (isObjRefTable) { + var result = + ConstantEvaluators.computeConstant( + instance, elem.initializers().get(elemidx++)); + table.setObjRef(i, result.ref(), instance); } else { + var val = + (int) computeConstantValue(instance, elem.initializers().get(elemidx++))[0]; + if (table.elementType().equals(ValType.FuncRef)) { + if (val > instance.functionCount()) { + throw new WasmRuntimeException("out of bounds table access"); + } + } table.setRef(i, val, instance); } } @@ -883,29 +894,15 @@ public static void TABLE_INIT( * i31 values (tagged longs) are boxed as WasmI31Ref GC refs so the tag is preserved. * For non-GC values (funcref, externref), this is a no-op cast to int. */ + @SuppressWarnings("InlineMeSuggester") + @Deprecated public static int boxForTable(long stackValue, Instance instance) { - if (Value.isI31(stackValue)) { - var i31Ref = new WasmI31Ref(Value.decodeI31U(stackValue)); - return instance.registerGcRef(i31Ref); - } return (int) stackValue; } - /** - * Converts an int from table storage to a stack long value, unboxing i31 refs. - * Only performs GC ref lookup for GC-typed tables (anyref, eqref, etc.) to avoid - * overhead for funcref tables in non-GC modules. - */ + @SuppressWarnings("InlineMeSuggester") + @Deprecated public static long unboxFromTable(int tableValue, Instance instance, ValType elementType) { - if (tableValue != Value.REF_NULL_VALUE - && tableValue >= 0 - && !elementType.equals(ValType.FuncRef) - && !elementType.equals(ValType.ExternRef)) { - var gcRef = instance.gcRef(tableValue); - if (gcRef instanceof WasmI31Ref) { - return Value.encodeI31(((WasmI31Ref) gcRef).value()); - } - } return tableValue; } diff --git a/runtime/src/main/java/run/endive/runtime/StackFrame.java b/runtime/src/main/java/run/endive/runtime/StackFrame.java index eb684f7a9..889efe324 100644 --- a/runtime/src/main/java/run/endive/runtime/StackFrame.java +++ b/runtime/src/main/java/run/endive/runtime/StackFrame.java @@ -27,6 +27,7 @@ public class StackFrame { private final int funcId; private int pc; private final long[] locals; + private final Object[] localRefs; private final ValType[] localTypes; private final int[] localIdx; private final Instance instance; @@ -38,6 +39,7 @@ public StackFrame(Instance instance, int funcId, long[] args) { instance, funcId, args, + null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); @@ -47,6 +49,7 @@ public StackFrame(Instance instance, int funcId, long[] args) { Instance instance, int funcId, long[] args, + Object[] refArgs, List argsTypes, List localTypes, List code) { @@ -54,6 +57,10 @@ public StackFrame(Instance instance, int funcId, long[] args) { this.instance = instance; this.funcId = funcId; this.locals = Arrays.copyOf(args, sizeOf(argsTypes) + sizeOf(localTypes)); + this.localRefs = new Object[this.locals.length]; + if (refArgs != null) { + System.arraycopy(refArgs, 0, this.localRefs, 0, refArgs.length); + } int localsSize = argsTypes.size() + localTypes.size(); this.localTypes = new ValType[localsSize]; for (int i = 0; i < argsTypes.size(); i++) { @@ -70,7 +77,11 @@ public StackFrame(Instance instance, int funcId, long[] args) { ValType type = localTypes.get(i); var idx = j + sizeOf(argsTypes); if (!type.equals(ValType.V128)) { - locals[idx] = Value.zero(type); + if (type.isReference()) { + locals[idx] = Value.REF_NULL_VALUE; + } else { + locals[idx] = Value.zero(type); + } j += 1; } else { locals[idx] = Value.zero(ValType.I64); @@ -95,6 +106,18 @@ void reset(long[] args) { for (int i = 0; i < locals.length; i++) { setLocal(i, args[i]); } + Arrays.fill(localRefs, null); + pc = 0; + } + + void reset(long[] args, Object[] refArgs) { + for (int i = 0; i < locals.length; i++) { + setLocal(i, args[i]); + } + Arrays.fill(localRefs, null); + if (refArgs != null) { + System.arraycopy(refArgs, 0, localRefs, 0, refArgs.length); + } pc = 0; } @@ -118,6 +141,15 @@ long local(int i) { return locals[i]; } + void setLocalRef(int i, Object ref) { + this.localRefs[i] = ref; + this.locals[i] = (ref == null) ? Value.REF_NULL_VALUE : 0; + } + + Object localRef(int i) { + return localRefs[i]; + } + @Override public String toString() { var nameSec = instance.module().nameSection(); @@ -200,19 +232,32 @@ void jumpTo(int newPc) { static void doControlTransfer(CtrlFrame ctrlFrame, MStack stack) { var endResults = ctrlFrame.startValues + ctrlFrame.endValues; // unwind stack long[] returns = new long[endResults]; + Object[] returnRefs = new Object[endResults]; + Object[] stackRefArray = stack.refArray(); for (int i = 0; i < returns.length; i++) { if (stack.size() > 0) { + if (stackRefArray != null) { + returnRefs[i] = stackRefArray[stack.size() - 1]; + } returns[i] = stack.pop(); } } + stack.clearRefsTo(ctrlFrame.height); while (stack.size() > ctrlFrame.height) { stack.pop(); } for (int i = 0; i < returns.length; i++) { - long value = returns[returns.length - 1 - i]; - stack.push(value); + int idx = returns.length - 1 - i; + long value = returns[idx]; + Object ref = returnRefs[idx]; + if (ref != null) { + // Was a ref (real Object or NULL_REF sentinel for null GC ref) + stack.pushRef(ref == MStack.NULL_REF ? null : ref); + } else { + stack.push(value); + } } } } diff --git a/runtime/src/main/java/run/endive/runtime/TableInstance.java b/runtime/src/main/java/run/endive/runtime/TableInstance.java index 36eb8cf36..5632b0201 100644 --- a/runtime/src/main/java/run/endive/runtime/TableInstance.java +++ b/runtime/src/main/java/run/endive/runtime/TableInstance.java @@ -14,12 +14,20 @@ public class TableInstance { private final Table table; private Instance[] instances; private int[] refs; + private Object[] objRefs; public TableInstance(Table table, int initialValue) { this.table = table; this.instances = new Instance[(int) table.limits().min()]; refs = new int[(int) table.limits().min()]; Arrays.fill(refs, initialValue); + if (isGcTable()) { + objRefs = new Object[(int) table.limits().min()]; + } + } + + private boolean isGcTable() { + return table.elementType().isObjectRef(); } public int size() { @@ -46,6 +54,29 @@ public int grow(int size, int value, Instance instance) { Arrays.fill(newInstances, oldSize, targetSize, instance); refs = newRefs; instances = newInstances; + if (objRefs != null) { + objRefs = Arrays.copyOf(objRefs, targetSize); + } + table.limits().grow(size); + return oldSize; + } + + public int growWithRef(int size, Object refValue, Instance instance) { + var oldSize = refs.length; + var targetSize = oldSize + size; + if (size < 0 || targetSize > limits().max()) { + return -1; + } + var newRefs = Arrays.copyOf(refs, targetSize); + Arrays.fill(newRefs, oldSize, targetSize, REF_NULL_VALUE); + var newInstances = Arrays.copyOf(instances, targetSize); + Arrays.fill(newInstances, oldSize, targetSize, instance); + refs = newRefs; + instances = newInstances; + if (objRefs != null) { + objRefs = Arrays.copyOf(objRefs, targetSize); + Arrays.fill(objRefs, oldSize, targetSize, refValue); + } table.limits().grow(size); return oldSize; } @@ -65,6 +96,13 @@ public int requiredRef(int index) { return ref; } + public Object objRef(int index) { + if (index < 0 || index >= this.refs.length) { + throw new WasmEngineException("out of bounds table access"); + } + return (objRefs != null) ? objRefs[index] : null; + } + public void setRef(int index, int value, Instance instance) { if (index < 0 || index >= this.refs.length || index >= this.instances.length) { throw new UninstantiableException("out of bounds table access"); @@ -73,6 +111,17 @@ public void setRef(int index, int value, Instance instance) { this.instances[index] = instance; } + public void setObjRef(int index, Object ref, Instance instance) { + if (index < 0 || index >= this.refs.length || index >= this.instances.length) { + throw new UninstantiableException("out of bounds table access"); + } + if (objRefs != null) { + objRefs[index] = ref; + } + this.refs[index] = (ref == null) ? REF_NULL_VALUE : 0; + this.instances[index] = instance; + } + public Instance instance(int index) { return instances[index]; } @@ -81,5 +130,8 @@ public void reset() { for (int i = 0; i < refs.length; i++) { this.refs[i] = REF_NULL_VALUE; } + if (objRefs != null) { + Arrays.fill(objRefs, null); + } } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmArray.java b/runtime/src/main/java/run/endive/runtime/WasmArray.java index a46fb3d47..b28554bac 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmArray.java +++ b/runtime/src/main/java/run/endive/runtime/WasmArray.java @@ -2,16 +2,23 @@ /** * Runtime representation of a WasmGC array instance. - * Elements are stored as raw long values (same encoding as stack values). + * Numeric elements are stored in {@code elements} (long[]). + * Reference-typed elements are stored in {@code elementRefs} (Object[]). * Packed types (i8, i16) are stored as full long slots for simplicity. */ public final class WasmArray implements WasmGcRef { private final int typeIdx; private final long[] elements; + private Object[] elementRefs; public WasmArray(int typeIdx, long[] elements) { + this(typeIdx, elements, null); + } + + public WasmArray(int typeIdx, long[] elements, Object[] elementRefs) { this.typeIdx = typeIdx; this.elements = elements; + this.elementRefs = elementRefs; } @Override @@ -27,6 +34,17 @@ public void set(int idx, long value) { elements[idx] = value; } + public Object getRef(int idx) { + return elementRefs != null ? elementRefs[idx] : null; + } + + public void setRef(int idx, Object ref) { + if (elementRefs == null) { + elementRefs = new Object[elements.length]; + } + elementRefs[idx] = ref; + } + public int length() { return elements.length; } @@ -34,4 +52,8 @@ public int length() { public long[] elements() { return elements; } + + public Object[] elementRefs() { + return elementRefs; + } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmException.java b/runtime/src/main/java/run/endive/runtime/WasmException.java index 3ba19c2ad..f3d2b0d92 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmException.java +++ b/runtime/src/main/java/run/endive/runtime/WasmException.java @@ -3,12 +3,18 @@ public class WasmException extends RuntimeException { private final int tagIdx; private final long[] args; + private final Object[] refArgs; private final Instance instance; public WasmException(Instance instance, int tagIdx, long[] args) { + this(instance, tagIdx, args, null); + } + + public WasmException(Instance instance, int tagIdx, long[] args, Object[] refArgs) { this.instance = instance; this.tagIdx = tagIdx; this.args = args.clone(); + this.refArgs = (refArgs != null) ? refArgs.clone() : null; this.setStackTrace(new StackTraceElement[0]); } @@ -23,4 +29,8 @@ public int tagIdx() { public long[] args() { return args; } + + public Object[] refArgs() { + return refArgs; + } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmExternRef.java b/runtime/src/main/java/run/endive/runtime/WasmExternRef.java index 92e34c192..20e3842f2 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmExternRef.java +++ b/runtime/src/main/java/run/endive/runtime/WasmExternRef.java @@ -1,17 +1,25 @@ package run.endive.runtime; /** - * Wrapper for externref values converted to anyref via any.convert_extern. - * Prevents GC ref ID collisions between extern values and native GC objects. + * Wrapper for externref values. + * Can wrap either a long (for host-provided externref values) or an Object + * (for GC values externalized via extern.convert_any). */ public final class WasmExternRef implements WasmGcRef { private static final int ANY_HEAP_TYPE = -18; // ValType.TypeIdxCode.ANY.code() - private final long value; + private final long longValue; + private final Object objectValue; public WasmExternRef(long value) { - this.value = value; + this.longValue = value; + this.objectValue = null; + } + + public WasmExternRef(Object value) { + this.longValue = 0; + this.objectValue = value; } @Override @@ -20,6 +28,14 @@ public int typeIdx() { } public long value() { - return value; + return longValue; + } + + public Object objectValue() { + return objectValue; + } + + public boolean isObjectRef() { + return objectValue != null; } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmFunctionHandle.java b/runtime/src/main/java/run/endive/runtime/WasmFunctionHandle.java index 627cdc08f..93e3060f9 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmFunctionHandle.java +++ b/runtime/src/main/java/run/endive/runtime/WasmFunctionHandle.java @@ -6,4 +6,16 @@ @FunctionalInterface public interface WasmFunctionHandle { long[] apply(Instance instance, long... args); + + /** + * Call this host function with separate long and Object ref arguments. + * Override this method for host functions that need to receive/return + * externref or GC reference values as Objects. + * + *

The default delegates to {@link #apply(Instance, long...)} which discards + * Object refs; override to handle them. + */ + default CallResult applyWithRefs(Instance instance, long[] args, Object[] refArgs) { + return new CallResult(apply(instance, args), null); + } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmI31Ref.java b/runtime/src/main/java/run/endive/runtime/WasmI31Ref.java index 8a03239e3..4df863b1f 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmI31Ref.java +++ b/runtime/src/main/java/run/endive/runtime/WasmI31Ref.java @@ -1,10 +1,9 @@ package run.endive.runtime; /** - * Boxed representation of an i31ref value for storage in int-typed containers (tables, globals). - * On the stack, i31 values use an efficient tagged-long encoding (see {@link - * run.endive.wasm.types.Value#encodeI31}). This class is only used when i31 values need - * to pass through int-typed storage where the tag would be lost. + * Representation of an i31ref value. + * On the stack, stored as Object in the refs array. + * Two i31ref values with the same integer value are equal per the Wasm spec (ref.eq). */ public final class WasmI31Ref implements WasmGcRef { @@ -24,4 +23,20 @@ public int typeIdx() { public int value() { return value; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof WasmI31Ref)) { + return false; + } + return value == ((WasmI31Ref) o).value; + } + + @Override + public int hashCode() { + return value; + } } diff --git a/runtime/src/main/java/run/endive/runtime/WasmStruct.java b/runtime/src/main/java/run/endive/runtime/WasmStruct.java index 35e3eb049..3ea42c6a0 100644 --- a/runtime/src/main/java/run/endive/runtime/WasmStruct.java +++ b/runtime/src/main/java/run/endive/runtime/WasmStruct.java @@ -2,15 +2,23 @@ /** * Runtime representation of a WasmGC struct instance. - * Fields are stored as raw long values (same encoding as stack values). + * Numeric fields are stored in {@code fields} (long[]). + * Reference-typed fields are stored in {@code fieldRefs} (Object[]). + * Both arrays are indexed by field index; only one is "active" per field. */ public final class WasmStruct implements WasmGcRef { private final int typeIdx; private final long[] fields; + private Object[] fieldRefs; public WasmStruct(int typeIdx, long[] fields) { + this(typeIdx, fields, null); + } + + public WasmStruct(int typeIdx, long[] fields, Object[] fieldRefs) { this.typeIdx = typeIdx; this.fields = fields; + this.fieldRefs = fieldRefs; } @Override @@ -26,6 +34,17 @@ public void setField(int idx, long value) { fields[idx] = value; } + public Object fieldRef(int idx) { + return fieldRefs != null ? fieldRefs[idx] : null; + } + + public void setFieldRef(int idx, Object ref) { + if (fieldRefs == null) { + fieldRefs = new Object[fields.length]; + } + fieldRefs[idx] = ref; + } + public int fieldCount() { return fields.length; } diff --git a/runtime/src/main/java/run/endive/runtime/internal/CompilerInterpreterMachine.java b/runtime/src/main/java/run/endive/runtime/internal/CompilerInterpreterMachine.java index b725f75a0..ef3649513 100644 --- a/runtime/src/main/java/run/endive/runtime/internal/CompilerInterpreterMachine.java +++ b/runtime/src/main/java/run/endive/runtime/internal/CompilerInterpreterMachine.java @@ -11,6 +11,7 @@ import run.endive.runtime.WasmException; import run.endive.wasm.WasmEngineException; import run.endive.wasm.types.FunctionType; +import run.endive.wasm.types.ValType; /** * This class is used by compiler generated classes. It MUST remain backwards compatible @@ -44,6 +45,7 @@ protected long[] call( Deque callStack, int funcId, long[] args, + Object[] refArgs, FunctionType callType, boolean popResults) throws WasmEngineException { @@ -51,7 +53,7 @@ protected long[] call( usedInterpretedFunctions.add(funcId); System.err.println("Endive: calling interpreted function " + funcId); } - return super.call(stack, instance, callStack, funcId, args, callType, popResults); + return super.call(stack, instance, callStack, funcId, args, refArgs, callType, popResults); } @Override @@ -71,15 +73,35 @@ protected void CALL(Operands operands) { var stack = stack(); var typeId = instance.functionType(funcId); var type = instance.type(typeId); - var args = extractArgsForParams(stack, type.params()); + var extracted = extractArgsAndRefsForParams(stack, type.params(), instance); + var args = (long[]) extracted[0]; + var refArgs = (Object[]) extracted[1]; + boolean hasObjectRefs = type.hasObjectRefReturns(); try { - var results = instance.getMachine().call(funcId, args); - // a host function can return null or an array of ints - // which we will push onto the stack - if (results != null) { - for (var result : results) { - stack.push(result); + if (hasObjectRefs) { + var cr = instance.getMachine().callWithRefs(funcId, args, refArgs); + int slot = 0; + for (int i = 0; i < type.returns().size(); i++) { + var retType = type.returns().get(i); + if (retType.isObjectRef()) { + stack.pushRef(cr.refResult(slot)); + slot++; + } else if (retType.equals(ValType.V128)) { + stack.push(cr.longResult(slot)); + stack.push(cr.longResult(slot + 1)); + slot += 2; + } else { + stack.push(cr.longResult(slot)); + slot++; + } + } + } else { + var results = instance.getMachine().call(funcId, args, refArgs); + if (results != null) { + for (var result : results) { + stack.push(result); + } } } } catch (WasmException e) { diff --git a/runtime/src/main/java/run/endive/runtime/internal/GcRefStore.java b/runtime/src/main/java/run/endive/runtime/internal/GcRefStore.java deleted file mode 100644 index bfefbc214..000000000 --- a/runtime/src/main/java/run/endive/runtime/internal/GcRefStore.java +++ /dev/null @@ -1,125 +0,0 @@ -package run.endive.runtime.internal; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import run.endive.runtime.Instance; -import run.endive.runtime.WasmArray; -import run.endive.runtime.WasmGcRef; -import run.endive.runtime.WasmStruct; -import run.endive.wasm.types.Value; - -/** - * Store for GC-managed references keyed by auto-assigned integers. - * - *

Uses epoch-based deferred collection: refs are never swept during wasm - * execution. Collection only happens at safe points — between - * top-level calls — when the wasm operand stack and all call frames are - * empty. At that point the only roots are globals and tables. - */ -public class GcRefStore { - - /** - * GC ref IDs start at this offset to avoid collisions with externref - * values that get internalized via any.convert_extern. Since internalized - * externrefs and GC refs both live in the ANY hierarchy, they share the - * same integer representation space. - */ - public static final int ID_OFFSET = 0x10000; - - private static final int SWEEP_INTERVAL = 4096; - - private final Instance instance; - private final Map map = new HashMap<>(); - private int nextId = ID_OFFSET; - private int allocsSinceLastSweep; - private boolean sweepRequested; - - public GcRefStore(Instance instance) { - this.instance = instance; - } - - /** Inserts a value with an automatically assigned key. */ - public int put(WasmGcRef value) { - int id = nextId++; - map.put(id, value); - allocsSinceLastSweep++; - if (allocsSinceLastSweep >= SWEEP_INTERVAL) { - sweepRequested = true; - } - return id; - } - - /** Retrieves a value by key, or null if missing. */ - public WasmGcRef get(int key) { - return map.get(key); - } - - /** Called at safe points (between top-level calls). */ - public void safePoint() { - if (sweepRequested) { - sweep(); - sweepRequested = false; - allocsSinceLastSweep = 0; - } - } - - /** Checks whether a raw reference value is a GC ref ID. */ - public static boolean isGcRefId(long val) { - return val >= ID_OFFSET && val != Value.REF_NULL_VALUE && !Value.isI31(val); - } - - private void sweep() { - Set reachable = new HashSet<>(); - - // 1. Scan globals - int globalCount = instance.globalCount(); - for (int i = 0; i < globalCount; i++) { - var g = instance.global(i); - if (g != null) { - markIfGcRef(g.getValueLow(), reachable); - } - } - - // 2. Scan tables - int tableCount = instance.tableCount(); - for (int i = 0; i < tableCount; i++) { - var table = instance.table(i); - if (table != null) { - for (int j = 0; j < table.size(); j++) { - markIfGcRef(table.ref(j), reachable); - } - } - } - - // 3. Remove unreachable entries - map.keySet().removeIf(id -> !reachable.contains(id)); - } - - private void markIfGcRef(long val, Set reachable) { - if (!isGcRefId(val)) { - return; - } - int id = (int) val; - if (!reachable.add(id)) { - return; // already visited — prevents infinite loops in cyclic structures - } - WasmGcRef ref = map.get(id); - if (ref == null) { - return; - } - // Recursively trace nested refs - if (ref instanceof WasmStruct) { - var s = (WasmStruct) ref; - for (int i = 0; i < s.fieldCount(); i++) { - markIfGcRef(s.field(i), reachable); - } - } else if (ref instanceof WasmArray) { - var a = (WasmArray) ref; - for (int i = 0; i < a.length(); i++) { - markIfGcRef(a.get(i), reachable); - } - } - } -} diff --git a/runtime/src/test/java/run/endive/runtime/WasmModuleTest.java b/runtime/src/test/java/run/endive/runtime/WasmModuleTest.java index ab4b912ce..d229e7d09 100644 --- a/runtime/src/test/java/run/endive/runtime/WasmModuleTest.java +++ b/runtime/src/test/java/run/endive/runtime/WasmModuleTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static run.endive.wasm.types.Value.REF_NULL_VALUE; @@ -761,7 +762,33 @@ public void shouldSupportTableFactoryOverride() { @Test public void testExternrefHandling() { var testObject = new Object(); - var sideTable = new HashMap(); + + WasmFunctionHandle getHostObject = + new WasmFunctionHandle() { + @Override + public long[] apply(Instance inst, long... args) { + throw new UnsupportedOperationException("Use applyWithRefs"); + } + + @Override + public CallResult applyWithRefs(Instance inst, long[] args, Object[] refArgs) { + return new CallResult(null, new Object[] {testObject}); + } + }; + + WasmFunctionHandle isNull = + new WasmFunctionHandle() { + @Override + public long[] apply(Instance inst, long... args) { + throw new UnsupportedOperationException("Use applyWithRefs"); + } + + @Override + public CallResult applyWithRefs(Instance inst, long[] args, Object[] refArgs) { + Object ref = (refArgs != null && refArgs.length > 0) ? refArgs[0] : null; + return new CallResult(new long[] {ref == null ? 1 : 0}, null); + } + }; var imports = ImportValues.builder() @@ -770,45 +797,44 @@ public void testExternrefHandling() { "env", "get_host_object", FunctionType.of(List.of(), List.of(ValType.ExternRef)), - (inst, args) -> { - sideTable.put(123L, testObject); - return new long[] {123L}; - })) + getHostObject)) .addFunction( new HostFunction( "env", "is_null", FunctionType.of( List.of(ValType.ExternRef), List.of(ValType.I32)), - (inst, args) -> { - long key = args[0]; - return (sideTable.get(key) == null) - ? new long[] {1} - : new long[] {0}; - })) + isNull)) .build(); var instance = Instance.builder(loadModule("compiled/externref-example.wat.wasm")) .withImportValues(imports) .build(); - var roundTrip = instance.exports().function("process_externref").apply(123L)[0]; - assertEquals(123L, roundTrip); - - // object has not been created yet - var isNull1 = instance.exports().function("is_null").apply(123L)[0]; - assertEquals(1L, isNull1); - - // now we create the test object - var ref = instance.exports().function("get_host_object").apply()[0]; - assertEquals(123L, ref); - - var isNull2 = instance.exports().function("is_null").apply(123L)[0]; - assertEquals(0L, isNull2); - - // verify against a reference that doesn't exist - var isNull3 = instance.exports().function("is_null").apply(1L)[0]; - assertEquals(1L, isNull3); + // Round-trip: pass an externref Object, get it back + var roundTrip = + instance.exports() + .function("process_externref") + .applyWithRefs(new long[0], new Object[] {testObject}); + assertSame(testObject, roundTrip.refResult(0)); + + // is_null with a non-null ref + var isNull1 = + instance.exports() + .function("is_null") + .applyWithRefs(new long[0], new Object[] {testObject}); + assertEquals(0, isNull1.longResult(0)); + + // is_null with null + var isNull2 = + instance.exports() + .function("is_null") + .applyWithRefs(new long[0], new Object[] {null}); + assertEquals(1, isNull2.longResult(0)); + + // get_host_object returns the testObject + var ref = instance.exports().function("get_host_object").applyWithRefs(new long[0], null); + assertSame(testObject, ref.refResult(0)); } @Test diff --git a/runtime/src/test/java/run/endive/runtime/internal/GcRefStoreTest.java b/runtime/src/test/java/run/endive/runtime/internal/GcRefStoreTest.java deleted file mode 100644 index e0d2a4173..000000000 --- a/runtime/src/test/java/run/endive/runtime/internal/GcRefStoreTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package run.endive.runtime.internal; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.ByteArrayInputStream; -import org.junit.jupiter.api.Test; -import run.endive.runtime.Instance; -import run.endive.runtime.WasmGcRef; -import run.endive.wasm.Parser; - -public class GcRefStoreTest { - - // Minimal valid wasm module: magic number + version 1 - private static final byte[] EMPTY_WASM = {0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}; - - private static WasmGcRef ref(int typeIdx) { - return () -> typeIdx; - } - - private static GcRefStore newStore() { - var module = Parser.parse(new ByteArrayInputStream(EMPTY_WASM)); - var instance = Instance.builder(module).build(); - return new GcRefStore(instance); - } - - @Test - public void autoAssignKeysFromOffset() { - var store = newStore(); - int k0 = store.put(ref(0)); - int k1 = store.put(ref(1)); - int k2 = store.put(ref(2)); - assertEquals(GcRefStore.ID_OFFSET, k0); - assertEquals(GcRefStore.ID_OFFSET + 1, k1); - assertEquals(GcRefStore.ID_OFFSET + 2, k2); - assertEquals(0, store.get(k0).typeIdx()); - assertEquals(1, store.get(k1).typeIdx()); - assertEquals(2, store.get(k2).typeIdx()); - } - - @Test - public void getMissingKeyReturnsNull() { - var store = newStore(); - assertNull(store.get(0)); - assertNull(store.get(999)); - } - - @Test - public void isGcRefIdClassifiesCorrectly() { - assertTrue(GcRefStore.isGcRefId(GcRefStore.ID_OFFSET)); - assertTrue(GcRefStore.isGcRefId(GcRefStore.ID_OFFSET + 100)); - assertFalse(GcRefStore.isGcRefId(0)); - assertFalse(GcRefStore.isGcRefId(GcRefStore.ID_OFFSET - 1)); - } -} diff --git a/test-gen-lib/src/main/java/run/endive/testgen/JavaTestGen.java b/test-gen-lib/src/main/java/run/endive/testgen/JavaTestGen.java index 79f22924c..4d4b11366 100644 --- a/test-gen-lib/src/main/java/run/endive/testgen/JavaTestGen.java +++ b/test-gen-lib/src/main/java/run/endive/testgen/JavaTestGen.java @@ -77,6 +77,7 @@ public CompilationUnit generate(String name, Wast wast, String wasmClasspath) { cu.addImport("org.junit.jupiter.api.Assertions.assertNotNull", true, false); cu.addImport("org.junit.jupiter.api.Assertions.assertThrows", true, false); cu.addImport("org.junit.jupiter.api.Assertions.assertTrue", true, false); + cu.addImport("org.junit.jupiter.api.Assertions.assertNull", true, false); cu.addImport("org.junit.jupiter.api.Assertions.assertDoesNotThrow", true, false); // testing imports @@ -87,6 +88,7 @@ public CompilationUnit generate(String name, Wast wast, String wasmClasspath) { cu.addImport("run.endive.wasm.WasmEngineException"); cu.addImport("run.endive.runtime.WasmException"); cu.addImport("run.endive.runtime.ExportFunction"); + cu.addImport("run.endive.runtime.CallResult"); cu.addImport("run.endive.runtime.Instance"); // base imports @@ -371,41 +373,99 @@ private Optional generateFieldExport( } } + /** + * Check if a command involves Object reference types in its args or expected values. + * If so, we must use applyWithRefs(long[], Object[]) instead of apply(long...). + */ + private static boolean needsRefCall(Command cmd) { + if (cmd.action() != null && cmd.action().args() != null) { + for (var arg : cmd.action().args()) { + if (arg.type().isObjectRef()) { + return true; + } + } + } + if (cmd.expected() != null) { + for (var expected : cmd.expected()) { + if (expected.type().isObjectRef()) { + return true; + } + } + } + return false; + } + private List generateAssert(String varName, Command cmd, String moduleName) { assert (cmd.type() == CommandType.ASSERT_RETURN || cmd.type() == CommandType.ASSERT_TRAP || cmd.type() == CommandType.ASSERT_EXCEPTION || cmd.type() == CommandType.ASSERT_EXHAUSTION); - var args = - (cmd.action().args() != null) - ? Arrays.stream(cmd.action().args()) - .map(WasmValue::toArgsValue) - .collect(Collectors.toList()) - : List.of(); - - var adaptedArgs = - (args == null || args.size() == 0) - ? "" - : args.stream().collect(Collectors.joining(").add(", ".add(", ")")); + boolean useRef = needsRefCall(cmd); - // Function or Global - var invocationMethod = - (cmd.action().type() == ActionType.INVOKE) - ? ".apply(ArgsAdapter.builder()" + adaptedArgs + ".build()" + ")" - : ".getValue()"; + String invocationMethod; + String refCallExpr = null; + if (cmd.action().type() == ActionType.INVOKE) { + if (useRef) { + // Build ArgsAdapter chain for ref-using functions + var argsBuilder = new StringBuilder("ArgsAdapter.builder()"); + if (cmd.action().args() != null) { + for (var arg : cmd.action().args()) { + if (arg.type().isObjectRef()) { + argsBuilder.append(".addRef(").append(arg.toRefArgsValue()).append(")"); + } else { + argsBuilder.append(".add(").append(arg.toArgsValue()).append(")"); + } + } + } + refCallExpr = argsBuilder + ".applyWithRefs(" + varName + ")"; + invocationMethod = null; // not used for ref path + } else { + var args = + (cmd.action().args() != null) + ? Arrays.stream(cmd.action().args()) + .map(WasmValue::toArgsValue) + .collect(Collectors.toList()) + : List.of(); + var adaptedArgs = + (args == null || args.size() == 0) + ? "" + : args.stream().collect(Collectors.joining(").add(", ".add(", ")")); + invocationMethod = ".apply(ArgsAdapter.builder()" + adaptedArgs + ".build()" + ")"; + } + } else { + invocationMethod = ".getValue()"; + } if (cmd.type() == CommandType.ASSERT_TRAP || cmd.type() == CommandType.ASSERT_EXHAUSTION || cmd.type() == CommandType.ASSERT_EXCEPTION) { + String trapExpr; + if (cmd.action().type() == ActionType.INVOKE) { + // Always use applyWithRefs for trap tests to avoid + // UnsupportedOperationException from apply() on GC ref functions + if (refCallExpr != null) { + trapExpr = refCallExpr; + } else { + // Build ArgsAdapter chain even for non-ref args + var argsBuilder = new StringBuilder("ArgsAdapter.builder()"); + if (cmd.action().args() != null) { + for (var arg : cmd.action().args()) { + argsBuilder.append(".add(").append(arg.toArgsValue()).append(")"); + } + } + trapExpr = argsBuilder + ".applyWithRefs(" + varName + ")"; + } + } else { + trapExpr = varName + invocationMethod; + } var assertDecl = new NameExpr( "var exception =" + " assertThrows(" + getExceptionType(cmd.type()) + ".class, () -> " - + varName - + invocationMethod + + trapExpr + ")"); if (cmd.text() != null) { return List.of(assertDecl, exceptionMessageMatch(cmd.text())); @@ -416,42 +476,58 @@ private List generateAssert(String varName, Command cmd, String modu assert (cmd.expected() != null); List exprs = new ArrayList<>(); - var resVarName = (cmd.action().type() == ActionType.INVOKE) ? "results" : "result"; - exprs.add(new NameExpr("var " + resVarName + " = " + varName + invocationMethod)); - - for (int i = 0; i < cmd.expected().length; i++) { - var expected = cmd.expected()[i]; - var resultVar = - (cmd.action().type() == ActionType.INVOKE) - ? expected.toResultValue(resVarName + "[" + i + "]") - : expected.toResultValue(resVarName); - - if (expected.type().equals(WasmValueType.V128)) { - exprs.add(new NameExpr("var expected = " + resultVar)); - switch (expected.laneType()) { - case I8: - exprs.add( - new NameExpr( - "assertArrayEquals(expected," + " vecTo8(results))")); - break; - case I16: - exprs.add( - new NameExpr("assertArrayEquals(expected, vecTo16(results))")); - break; - case I32: - exprs.add( - new NameExpr( - "assertArrayEquals(expected," + " vecTo32(results))")); - break; - case F32: - exprs.add( - new NameExpr( - "assertArrayEquals(expected," + " vecToF32(results))")); - break; - } - } else { - exprs.add(expected.toAssertion(resultVar, moduleName)); + if (useRef && cmd.action().type() == ActionType.INVOKE) { + // Ref path: result is CallResult + exprs.add(new NameExpr("var cr = " + refCallExpr)); + + for (int i = 0; i < cmd.expected().length; i++) { + var expected = cmd.expected()[i]; + var resultVar = expected.toRefResultValue("cr", i); + exprs.add(expected.toRefAssertion(resultVar, moduleName)); + } + } else { + var resVarName = (cmd.action().type() == ActionType.INVOKE) ? "results" : "result"; + exprs.add(new NameExpr("var " + resVarName + " = " + varName + invocationMethod)); + + for (int i = 0; i < cmd.expected().length; i++) { + var expected = cmd.expected()[i]; + var resultVar = + (cmd.action().type() == ActionType.INVOKE) + ? expected.toResultValue(resVarName + "[" + i + "]") + : expected.toResultValue(resVarName); + + if (expected.type().equals(WasmValueType.V128)) { + exprs.add(new NameExpr("var expected = " + resultVar)); + switch (expected.laneType()) { + case I8: + exprs.add( + new NameExpr( + "assertArrayEquals(expected," + + " vecTo8(results))")); + break; + case I16: + exprs.add( + new NameExpr( + "assertArrayEquals(expected," + + " vecTo16(results))")); + break; + case I32: + exprs.add( + new NameExpr( + "assertArrayEquals(expected," + + " vecTo32(results))")); + break; + case F32: + exprs.add( + new NameExpr( + "assertArrayEquals(expected," + + " vecToF32(results))")); + break; + } + } else { + exprs.add(expected.toAssertion(resultVar, moduleName)); + } } } @@ -464,23 +540,34 @@ private List generateAssert(String varName, Command cmd, String modu private List generateInvoke(String varName, Command cmd) { assert cmd.type() == CommandType.ACTION; - String invocationMethod; + String invokeExpr; if (cmd.action().type() == ActionType.INVOKE) { - var args = - Arrays.stream(cmd.action().args()) - .map(WasmValue::toArgsValue) - .collect(Collectors.joining(", ")); - invocationMethod = ".apply(" + args + ")"; + boolean useRef = needsRefCall(cmd); + if (useRef) { + var argsBuilder = new StringBuilder("ArgsAdapter.builder()"); + if (cmd.action().args() != null) { + for (var arg : cmd.action().args()) { + if (arg.type().isObjectRef()) { + argsBuilder.append(".addRef(").append(arg.toRefArgsValue()).append(")"); + } else { + argsBuilder.append(".add(").append(arg.toArgsValue()).append(")"); + } + } + } + invokeExpr = argsBuilder + ".applyWithRefs(" + varName + ")"; + } else { + var args = + Arrays.stream(cmd.action().args()) + .map(WasmValue::toArgsValue) + .collect(Collectors.joining(", ")); + invokeExpr = varName + ".apply(" + args + ")"; + } } else { throw new IllegalArgumentException("Unhandled action type " + cmd.action().type()); } var assertDecl = - new NameExpr( - "var exception = assertDoesNotThrow(() -> " - + varName - + invocationMethod - + ")"); + new NameExpr("var exception = assertDoesNotThrow(() -> " + invokeExpr + ")"); return List.of(assertDecl); } diff --git a/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValue.java b/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValue.java index cc6af11b5..59f61598e 100644 --- a/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValue.java +++ b/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValue.java @@ -288,6 +288,69 @@ public String intLaneValue(String v) { return Integer.toUnsignedString((int) (0xFFFFFFFF & longValue)) + "L"; } + /** + * Generate assertion for CallResult from applyWithRefs. + * + * Object ref types use cr.refResult(i) and compare with Java null/not-null. + * Numeric types use cr.longResult(i) and compare with expected values. + */ + public NameExpr toRefAssertion(String resultVar, String moduleName) { + if (value == null) { + // Type-only assertion (no specific value) + switch (type) { + case FUNC_REF: + return new NameExpr("assertNotNull(" + resultVar + ")"); + case EXTERN_REF: + return new NameExpr("assertNotNull(" + resultVar + ")"); + case REF_NULL: + case NULL_REF: + // These are GC null types -> Java null from popRef() + return new NameExpr("assertNull(" + resultVar + ")"); + case NULL_FUNC_REF: + case NULL_EXTERN_REF: + return new NameExpr("assertNull(" + resultVar + ")"); + case STRUCT_REF: + case ANY_REF: + case I31_REF: + case ARRAY_REF: + case EQ_REF: + return new NameExpr("assertNotNull(" + resultVar + ")"); + default: + throw new IllegalArgumentException( + "cannot generate GC assertion for WasmValue: " + this); + } + } + + // Value-based assertion + switch (type) { + case STRUCT_REF: + case ANY_REF: + case NULL_REF: + case ARRAY_REF: + case EQ_REF: + case I31_REF: + case REF_NULL: + // GC ref types: null -> Java null, non-null -> assertNotNull + if (value[0].equals("null")) { + return new NameExpr("assertNull(" + resultVar + ")"); + } + return new NameExpr("assertNotNull(" + resultVar + ")"); + case EXTERN_REF: + case EXN_REF: + case FUNC_REF: + case NULL_FUNC_REF: + case NULL_EXTERN_REF: + if (value[0].equals("null")) { + return new NameExpr("assertNull(" + resultVar + ")"); + } + return new NameExpr("assertNotNull(" + resultVar + ")"); + default: + // Numeric types in GC path + var expectedVar = toExpectedValue(); + return new NameExpr("assertEquals(" + expectedVar + ", " + resultVar + ")"); + } + } + public String toArgsValue() { switch (type) { case I32: @@ -392,4 +455,70 @@ public String toArgsValue() { throw new IllegalArgumentException("Type not recognized " + type); } } + + /** + * Generate the Object expression for use with ArgsAdapter.addRef(). + * Only called for types where isObjectRef() is true. + * + *

In the wast spec test format, non-null values for anyref/externref/etc. + * represent host objects (ref.host N or ref.extern N) which are encoded + * as WasmExternRef on the JVM side. + */ + public String toRefArgsValue() { + if (value[0].equals("null")) { + return "null"; + } + switch (type) { + case EXTERN_REF: + case NULL_EXTERN_REF: + case ANY_REF: + case EQ_REF: + case STRUCT_REF: + case ARRAY_REF: + case I31_REF: + case NULL_REF: + case REF_NULL: + // Non-null host references in wast tests are encoded as + // WasmExternRef wrapping the numeric value + return "new run.endive.runtime.WasmExternRef(Long.parseLong(\"" + value[0] + "\"))"; + case FUNC_REF: + case NULL_FUNC_REF: + return "null"; + default: + throw new IllegalArgumentException("Type not recognized for ref args: " + type); + } + } + + /** + * Generate result extraction expression for CallResult. + * For numeric types, uses cr.longResult(i). + * For ref types, uses cr.refResult(i). + */ + public String toRefResultValue(String crVar, int index) { + switch (type) { + case I64: + return crVar + ".longResult(" + index + ")"; + case I32: + return "(int) " + crVar + ".longResult(" + index + ")"; + case F32: + return "Float.intBitsToFloat((int) " + crVar + ".longResult(" + index + ")), 0.0"; + case F64: + return "Double.longBitsToDouble(" + crVar + ".longResult(" + index + ")), 0.0"; + case EXTERN_REF: + case EXN_REF: + case FUNC_REF: + case NULL_FUNC_REF: + case NULL_EXTERN_REF: + case STRUCT_REF: + case ANY_REF: + case NULL_REF: + case ARRAY_REF: + case EQ_REF: + case I31_REF: + case REF_NULL: + return crVar + ".refResult(" + index + ")"; + default: + throw new IllegalArgumentException("Type not recognized " + type); + } + } } diff --git a/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValueType.java b/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValueType.java index 379fe2cac..d58597f1f 100644 --- a/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValueType.java +++ b/test-gen-lib/src/main/java/run/endive/testgen/wast/WasmValueType.java @@ -49,4 +49,21 @@ public enum WasmValueType { public String value() { return value; } + + public boolean isObjectRef() { + switch (this) { + case STRUCT_REF: + case ANY_REF: + case NULL_REF: + case ARRAY_REF: + case EQ_REF: + case I31_REF: + case REF_NULL: + case EXTERN_REF: + case NULL_EXTERN_REF: + return true; + default: + return false; + } + } } diff --git a/wasm-corpus/src/main/resources/compiled/gc_array_copy_funcref.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_array_copy_funcref.wat.wasm new file mode 100644 index 000000000..d464eb1d9 Binary files /dev/null and b/wasm-corpus/src/main/resources/compiled/gc_array_copy_funcref.wat.wasm differ diff --git a/wasm-corpus/src/main/resources/compiled/gc_edge_cases.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_edge_cases.wat.wasm new file mode 100644 index 000000000..e28945598 Binary files /dev/null and b/wasm-corpus/src/main/resources/compiled/gc_edge_cases.wat.wasm differ diff --git a/wasm-corpus/src/main/resources/compiled/gc_multivalue.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_multivalue.wat.wasm new file mode 100644 index 000000000..8573c9bb3 Binary files /dev/null and b/wasm-corpus/src/main/resources/compiled/gc_multivalue.wat.wasm differ diff --git a/wasm-corpus/src/main/resources/compiled/gc_review_fixes.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_review_fixes.wat.wasm new file mode 100644 index 000000000..7b3bcf4bd Binary files /dev/null and b/wasm-corpus/src/main/resources/compiled/gc_review_fixes.wat.wasm differ diff --git a/wasm-corpus/src/main/resources/compiled/gc_stress.wat.wasm b/wasm-corpus/src/main/resources/compiled/gc_stress.wat.wasm new file mode 100644 index 000000000..d581c0c39 Binary files /dev/null and b/wasm-corpus/src/main/resources/compiled/gc_stress.wat.wasm differ diff --git a/wasm-corpus/src/main/resources/wat/gc_array_copy_funcref.wat b/wasm-corpus/src/main/resources/wat/gc_array_copy_funcref.wat new file mode 100644 index 000000000..dfe9377f5 --- /dev/null +++ b/wasm-corpus/src/main/resources/wat/gc_array_copy_funcref.wat @@ -0,0 +1,22 @@ +(module + (type $ft (func)) + (type $arr (array (mut funcref))) + + (func $dummy (type $ft)) + + (func (export "copy_funcref_array") (result i32) + ;; Create src array with 2 funcrefs + (local $src (ref $arr)) + (local $dst (ref $arr)) + (local.set $src (array.new $arr (ref.func $dummy) (i32.const 2))) + (local.set $dst (array.new_default $arr (i32.const 2))) + ;; Copy src[0..2] -> dst[0..2] + (array.copy $arr $arr (local.get $dst) (i32.const 0) (local.get $src) (i32.const 0) (i32.const 2)) + ;; Check dst[0] is not null (funcref was copied) + (local.get $dst) + (i32.const 0) + (array.get $arr) + (ref.is_null) + ;; Should be 0 (not null) + ) +) diff --git a/wasm-corpus/src/main/resources/wat/gc_multivalue.wat b/wasm-corpus/src/main/resources/wat/gc_multivalue.wat new file mode 100644 index 000000000..f14ff7a47 --- /dev/null +++ b/wasm-corpus/src/main/resources/wat/gc_multivalue.wat @@ -0,0 +1,44 @@ +(module + (type $Point (struct (field $x i32) (field $y i32))) + + ;; (result i32 (ref $Point)) -- numeric then ref + (func (export "int_then_ref") (param i32) (result i32 (ref $Point)) + local.get 0 + local.get 0 + local.get 0 + struct.new $Point + ) + + ;; (result (ref $Point) i32) -- ref then numeric + (func (export "ref_then_int") (param i32) (result (ref $Point) i32) + local.get 0 + local.get 0 + struct.new $Point + local.get 0 + ) + + ;; (result (ref $Point) (ref $Point)) -- two refs + (func (export "two_refs") (param i32) (result (ref $Point) (ref $Point)) + local.get 0 + local.get 0 + struct.new $Point + local.get 0 + local.get 0 + struct.new $Point + ) + + ;; (result i32 i32 (ref $Point)) -- two numerics + ref + (func (export "two_ints_ref") (param i32) (result i32 i32 (ref $Point)) + local.get 0 + local.get 0 + local.get 0 + local.get 0 + struct.new $Point + ) + + ;; Helper: get x from Point + (func (export "get_x") (param (ref $Point)) (result i32) + local.get 0 + struct.get $Point $x + ) +) diff --git a/wasm-corpus/src/main/resources/wat/gc_review_fixes.wat b/wasm-corpus/src/main/resources/wat/gc_review_fixes.wat new file mode 100644 index 000000000..21f17e02d --- /dev/null +++ b/wasm-corpus/src/main/resources/wat/gc_review_fixes.wat @@ -0,0 +1,73 @@ +(module + ;; Bug 2: null GC ref survives control transfer (block boundary) + (type $s (struct (field i32))) + + (func (export "null_ref_through_block") (result i32) + (local $r (ref null $s)) + (local.set $r + (block (result (ref null $s)) + ref.null $s + ) + ) + (ref.is_null (local.get $r)) + ) + + ;; Bug 2 variant: null GC ref survives nested blocks + (func (export "null_ref_nested_blocks") (result i32) + (local $r (ref null $s)) + (local.set $r + (block (result (ref null $s)) + (block (result (ref null $s)) + ref.null $s + ) + ) + ) + (ref.is_null (local.get $r)) + ) + + ;; Bug 2: non-null GC ref through block boundary (regression check) + (func (export "nonnull_ref_through_block") (result i32) + (local $r (ref null $s)) + (local.set $r + (block (result (ref null $s)) + (struct.new $s (i32.const 42)) + ) + ) + (struct.get $s 0 (local.get $r)) + ) + + ;; Bug 7: null GC ref through exception catch + (tag $e (param (ref null $s))) + + (func (export "null_ref_exception") (result i32) + (local $r (ref null $s)) + (block $b (result (ref null $s)) + (try_table (catch $e $b) + (throw $e (ref.null $s)) + ) + (unreachable) + ) + (local.set $r) + (ref.is_null (local.get $r)) + ) + + ;; Bug 7: non-null GC ref through exception (regression check) + (func (export "nonnull_ref_exception") (result i32) + (block $b (result (ref null $s)) + (try_table (catch $e $b) + (throw $e (struct.new $s (i32.const 99))) + ) + (unreachable) + ) + (struct.get $s 0) + ) + + ;; Bug 8: table.fill with valid bounds + (table $t 10 (ref null $s)) + + (func (export "table_fill_bounds") (result i32) + ;; Fill first 3 entries, should succeed + (table.fill $t (i32.const 0) (ref.null $s) (i32.const 3)) + (i32.const 1) ;; success + ) +) diff --git a/wasm/src/main/java/run/endive/wasm/types/FunctionType.java b/wasm/src/main/java/run/endive/wasm/types/FunctionType.java index d20296792..d1192f9c1 100644 --- a/wasm/src/main/java/run/endive/wasm/types/FunctionType.java +++ b/wasm/src/main/java/run/endive/wasm/types/FunctionType.java @@ -20,6 +20,40 @@ public List returns() { return returns; } + private int cachedObjRefFlags = + -1; // -1 = not computed, 0 = none, 1 = params, 2 = returns, 3 = both + + public boolean hasObjectRefParams() { + if (cachedObjRefFlags < 0) { + computeObjRefFlags(); + } + return (cachedObjRefFlags & 1) != 0; + } + + public boolean hasObjectRefReturns() { + if (cachedObjRefFlags < 0) { + computeObjRefFlags(); + } + return (cachedObjRefFlags & 2) != 0; + } + + private void computeObjRefFlags() { + int flags = 0; + for (var p : params) { + if (p.isObjectRef()) { + flags |= 1; + break; + } + } + for (var r : returns) { + if (r.isObjectRef()) { + flags |= 2; + break; + } + } + cachedObjRefFlags = flags; + } + public boolean paramsMatch(FunctionType other) { return params.equals(other.params); } diff --git a/wasm/src/main/java/run/endive/wasm/types/StorageType.java b/wasm/src/main/java/run/endive/wasm/types/StorageType.java index c869c1aa7..e06a3a10f 100644 --- a/wasm/src/main/java/run/endive/wasm/types/StorageType.java +++ b/wasm/src/main/java/run/endive/wasm/types/StorageType.java @@ -27,6 +27,18 @@ public PackedType packedType() { return packedType; } + public boolean isReference() { + return valType != null && valType.isReference(); + } + + public boolean isGcReference() { + return valType != null && valType.isGcReference(); + } + + public boolean isObjectRef() { + return valType != null && valType.isObjectRef(); + } + public int byteSize() { if (packedType != null) { switch (packedType) { diff --git a/wasm/src/main/java/run/endive/wasm/types/ValType.java b/wasm/src/main/java/run/endive/wasm/types/ValType.java index 189de8df6..ced274da4 100644 --- a/wasm/src/main/java/run/endive/wasm/types/ValType.java +++ b/wasm/src/main/java/run/endive/wasm/types/ValType.java @@ -43,6 +43,8 @@ public final class ValType { // This is useful when validating import function values. private int resolvedFunctionTypeHash; private final int resolvedFunctionTypeId; + private boolean gcReference; + private boolean objectRef; private ValType(int opcode) { this(opcode, NULL_TYPEIDX, -1); @@ -101,6 +103,11 @@ private ValType( this.resolvedFunctionTypeHash = resolvedFunctionTypeHash; this.id = createId(opcode, typeIdx); + + // Pre-compute gcReference and objectRef for well-known types + // (these don't need a TypeSection) + this.gcReference = computeIsGcReference(null); + this.objectRef = this.gcReference || computeIsExternRef(); } public ValType resolve(TypeSection typeSection) { @@ -112,9 +119,45 @@ public ValType resolve(TypeSection typeSection) { throw new InvalidException("unknown type: " + resolvedFunctionTypeId); } } + this.gcReference = computeIsGcReference(typeSection); + this.objectRef = gcReference || computeIsExternRef(); return this; } + private boolean computeIsGcReference(TypeSection ts) { + int op = opcode(); + switch (op) { + case ID.AnyRef: + case ID.EqRef: + case ID.i31: + case ID.StructRef: + case ID.ArrayRef: + case ID.NoneRef: + return true; + case ID.Ref: + case ID.RefNull: + int ht = typeIdx(); + if (ht == TypeIdxCode.FUNC.code() + || ht == TypeIdxCode.NOFUNC.code() + || ht == TypeIdxCode.EXTERN.code() + || ht == TypeIdxCode.NOEXTERN.code() + || ht == TypeIdxCode.EXN.code()) { + return false; + } + if (ht >= 0 && ts != null) { + return isConcreteInAnyHierarchy(ht, ts); + } + return ht == TypeIdxCode.ANY.code() + || ht == TypeIdxCode.EQ.code() + || ht == TypeIdxCode.I31.code() + || ht == TypeIdxCode.STRUCT.code() + || ht == TypeIdxCode.ARRAY.code() + || ht == TypeIdxCode.NONE.code(); + default: + return false; + } + } + private static long createId(int opcode, int typeIdx) { return ((long) typeIdx) << TYPEIDX_SHIFT | (opcode & OPCODE_MASK); } @@ -234,6 +277,27 @@ public boolean isReference() { return isReference(this.opcode()); } + public boolean isGcReference() { + return gcReference; + } + + /** + * Returns true if this type is an Object reference on the JVM: GC refs AND externref, + * but NOT funcref (which stays as int for call_indirect dispatch). + */ + public boolean isObjectRef() { + return objectRef; + } + + private boolean computeIsExternRef() { + int op = opcode(); + if (op != ID.Ref && op != ID.RefNull) { + return false; + } + int ht = typeIdx(); + return ht == TypeIdxCode.EXTERN.code() || ht == TypeIdxCode.NOEXTERN.code(); + } + // https://webassembly.github.io/gc/core/binary/types.html#heap-types public static boolean isAbsHeapType(int opcode) { return (opcode == ID.NoFuncRef @@ -672,6 +736,67 @@ public boolean isReference() { return ValType.isReference(opcode); } + public boolean isGcReference() { + return isGcReference(null); + } + + public boolean isGcReference(TypeSection ts) { + if (!isReference()) { + return false; + } + switch (opcode) { + case ID.AnyRef: + case ID.EqRef: + case ID.i31: + case ID.StructRef: + case ID.ArrayRef: + case ID.NoneRef: + return true; + case ID.Ref: + case ID.RefNull: + if (typeIdx == TypeIdxCode.FUNC.code() + || typeIdx == TypeIdxCode.NOFUNC.code() + || typeIdx == TypeIdxCode.EXTERN.code() + || typeIdx == TypeIdxCode.NOEXTERN.code() + || typeIdx == TypeIdxCode.EXN.code()) { + return false; + } + if (typeIdx >= 0 && ts != null) { + return isConcreteInAnyHierarchy(typeIdx, ts); + } + return typeIdx == TypeIdxCode.ANY.code() + || typeIdx == TypeIdxCode.EQ.code() + || typeIdx == TypeIdxCode.I31.code() + || typeIdx == TypeIdxCode.STRUCT.code() + || typeIdx == TypeIdxCode.ARRAY.code() + || typeIdx == TypeIdxCode.NONE.code(); + default: + return false; + } + } + + public boolean isObjectRef() { + return isObjectRef(null); + } + + public boolean isObjectRef(TypeSection ts) { + return isGcReference(ts) || isExternRef(); + } + + private boolean isExternRef() { + if (!isReference()) { + return false; + } + if (opcode == ID.ExternRef || opcode == ID.NoExternRef) { + return true; + } + if (opcode == ID.Ref || opcode == ID.RefNull) { + return typeIdx == TypeIdxCode.EXTERN.code() + || typeIdx == TypeIdxCode.NOEXTERN.code(); + } + return false; + } + @Deprecated(since = "use .build.resolve(typeSection) instead") public ValType build(Function context) { if (!isValidOpcode(opcode)) {