diff --git a/wasm/CHANGELOG.md b/wasm/CHANGELOG.md index 1a38a9544fb3..4e42f9159b4e 100644 --- a/wasm/CHANGELOG.md +++ b/wasm/CHANGELOG.md @@ -6,6 +6,7 @@ This changelog summarizes major changes to the WebAssembly engine implemented in * Implemented the [exception handling](https://github.com/WebAssembly/exception-handling) proposal. This feature can be enabled with the experimental option `wasm.Exceptions=true`. * Implemented the [typed function references](https://github.com/WebAssembly/function-references) proposal. This feature can be enabled with the experimental option `wasm.TypedFunctionReferences=true`. +* Implemented the [GC proposal](https://github.com/WebAssembly/gc) proposal. This feature can be enabled with the experimental option `wasm.GC=true`. ## Version 25.0.0 diff --git a/wasm/README.md b/wasm/README.md index 3f4235b6594b..1caaef807024 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -77,14 +77,17 @@ The `floyd` function is defined as separate and can be later exported. ```java try (Context context = Context.newBuilder("wasm").option("wasm.Builtins", "wasi_snapshot_preview1").build()) { // Evaluate the WebAssembly module - Source source = Source.newBuilder("wasm", new File("path/to/floyd.wasm")).name("example").build(); - context.eval(source); - - // Initialize the module and execute the floyd function - Value exampleModule = context.getBindings("wasm").getMember("example"); - exampleModule.getMember("_initialize").executeVoid(); - Value floydFunction = exampleModule.getMember("floyd"); - floydFunction.execute(); + Source source = Source.newBuilder("wasm", new File("path/to/floyd.wasm")).build(); + Value exampleModule = context.eval(source); + + // Initialize the module + Value exampleInstance = exampleModule.newInstance(); + Value exampleExports = exampleInstance.getMember("exports"); + exampleExports.getMember("_initialize").executeVoid(); + + // Execute the floyd function + Value floyd = exampleExports.getMember("floyd"); + floyd.execute(); } ``` diff --git a/wasm/mx.wasm/suite.py b/wasm/mx.wasm/suite.py index e96fa7491ced..6d10ce88bff6 100644 --- a/wasm/mx.wasm/suite.py +++ b/wasm/mx.wasm/suite.py @@ -305,6 +305,10 @@ "org.graalvm.collections", "static jdk.incubator.vector", # Vector API ], + "exports" : [ + # Export WasmStruct supertype to Truffle Static Object class generator + "org.graalvm.wasm.struct", + ], }, "subDir" : "src", "dependencies" : [ diff --git a/wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java b/wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java similarity index 98% rename from wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java rename to wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java index ce47b8d659df..1c64dc0e5a35 100644 --- a/wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java +++ b/wasm/src/org.graalvm.wasm.jdk25/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java @@ -39,7 +39,7 @@ * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; import com.oracle.truffle.api.CompilerDirectives; import jdk.incubator.vector.ByteVector; @@ -58,7 +58,7 @@ import java.util.function.Function; -import static org.graalvm.wasm.api.Vector128.BYTES; +import static org.graalvm.wasm.vector.Vector128.BYTES; /** * This is a JDK25-specific implementation of the GraalWasm SIMD proposal. It uses the {@link Vector @@ -72,7 +72,7 @@ final class Vector128OpsVectorAPI implements Vector128Ops { private static final Vector128Ops fallbackOps = Vector128OpsFallback.create(); static Vector128Ops create() { - return new Vector128OpsVectorAPI(); + return new org.graalvm.wasm.vector.Vector128OpsVectorAPI(); } private abstract static class Shape { @@ -350,21 +350,21 @@ public ByteVector unary(ByteVector xVec, int vectorOpcode) { case Bytecode.VECTOR_F32X4_NEG -> unop(x, F32X4, VectorOperators.NEG); case Bytecode.VECTOR_F32X4_SQRT -> unop(x, F32X4, VectorOperators.SQRT); case Bytecode.VECTOR_F32X4_CEIL -> ceil(x, F32X4, I32X4, VectorOperators.REINTERPRET_F2I, VectorOperators.REINTERPRET_I2F, - Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F32X4_FLOOR -> floor(x, F32X4, I32X4, VectorOperators.REINTERPRET_F2I, VectorOperators.REINTERPRET_I2F, - Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F32X4_TRUNC -> trunc(x, F32X4, I32X4, VectorOperators.REINTERPRET_F2I, VectorOperators.REINTERPRET_I2F, - Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentFloats, FLOAT_SIGNIFICAND_WIDTH, I32X4.broadcast(FLOAT_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F32X4_NEAREST -> nearest(x, F32X4, 1 << (FLOAT_SIGNIFICAND_WIDTH - 1)); case Bytecode.VECTOR_F64X2_ABS -> unop(x, F64X2, VectorOperators.ABS); case Bytecode.VECTOR_F64X2_NEG -> unop(x, F64X2, VectorOperators.NEG); case Bytecode.VECTOR_F64X2_SQRT -> unop(x, F64X2, VectorOperators.SQRT); case Bytecode.VECTOR_F64X2_CEIL -> ceil(x, F64X2, I64X2, VectorOperators.REINTERPRET_D2L, VectorOperators.REINTERPRET_L2D, - Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F64X2_FLOOR -> floor(x, F64X2, I64X2, VectorOperators.REINTERPRET_D2L, VectorOperators.REINTERPRET_L2D, - Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F64X2_TRUNC -> trunc(x, F64X2, I64X2, VectorOperators.REINTERPRET_D2L, VectorOperators.REINTERPRET_L2D, - Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); + org.graalvm.wasm.vector.Vector128OpsVectorAPI::getExponentDoubles, DOUBLE_SIGNIFICAND_WIDTH, I64X2.broadcast(DOUBLE_SIGNIF_BIT_MASK)); case Bytecode.VECTOR_F64X2_NEAREST -> nearest(x, F64X2, 1L << (DOUBLE_SIGNIFICAND_WIDTH - 1)); case Bytecode.VECTOR_I32X4_TRUNC_SAT_F32X4_S, Bytecode.VECTOR_I32X4_RELAXED_TRUNC_F32X4_S -> fromArray(fallbackOps.unary(x.toArray(), vectorOpcode)); // GR-51421 case Bytecode.VECTOR_I32X4_TRUNC_SAT_F32X4_U, Bytecode.VECTOR_I32X4_RELAXED_TRUNC_F32X4_U -> fromArray(fallbackOps.unary(x.toArray(), vectorOpcode)); // GR-51421 diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java index d9a437626389..f6ded6330f41 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/WasmJsApiSuite.java @@ -108,7 +108,7 @@ public class WasmJsApiSuite { private static WasmFunctionInstance createWasmFunctionInstance(WasmContext context, int[] paramTypes, int[] resultTypes, RootNode functionRootNode) { WasmModule module = WasmModule.createBuiltin("dummyModule"); - module.allocateFunctionType(paramTypes, resultTypes, context.getContextOptions().supportMultiValue()); + module.allocateFunctionType(paramTypes, resultTypes, context.getContextOptions().supportMultiValue(), context.language()); WasmFunction func = module.declareFunction(0); func.setTarget(functionRootNode.getCallTarget()); WasmInstance moduleInstance = context.contextStore().readInstance(module); @@ -857,11 +857,13 @@ public void testExportCountsLimit() throws IOException { context.readModule(binaryWithMixedExports, limits); final int noLimit = Integer.MAX_VALUE; - limits = new ModuleLimits(noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, 6, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit); + limits = new ModuleLimits(noLimit, noLimit, noLimit, noLimit, noLimit, 6, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, + noLimit, noLimit, noLimit); context.readModule(binaryWithMixedExports, limits); try { - limits = new ModuleLimits(noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, 5, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit); + limits = new ModuleLimits(noLimit, noLimit, noLimit, noLimit, noLimit, 5, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, noLimit, + noLimit, noLimit, noLimit); context.readModule(binaryWithMixedExports, limits); Assert.fail("Should have failed - export count exceeds the limit"); } catch (WasmException ex) { diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/bytecode/BytecodeSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/bytecode/BytecodeSuite.java index 2eca736fdadd..12bac627ef00 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/bytecode/BytecodeSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/bytecode/BytecodeSuite.java @@ -792,11 +792,6 @@ public void testInvalidElemHeaderElemType() { testAssertion(b -> b.addElemHeader(SegmentMode.ACTIVE, 0, WasmType.I32_TYPE, 0, null, 1), "invalid elem type in elem header"); } - @Test - public void testElemNull() { - test(RuntimeBytecodeGen::addElemNull, new byte[]{0x10}); - } - @Test public void testElemMinFunctionIndex() { test(b -> b.addElemFunctionIndex(0), new byte[]{0x00}); @@ -804,12 +799,12 @@ public void testElemMinFunctionIndex() { @Test public void testElemMaxInlineFunctionIndex() { - test(b -> b.addElemFunctionIndex(15), new byte[]{0x0F}); + test(b -> b.addElemFunctionIndex(31), new byte[]{0x1F}); } @Test public void testElemMinU8FunctionIndex() { - test(b -> b.addElemFunctionIndex(16), new byte[]{0x20, 0x10}); + test(b -> b.addElemFunctionIndex(32), new byte[]{0x20, 0x20}); } @Test @@ -832,11 +827,6 @@ public void testElemMinI32FunctionIndex() { test(b -> b.addElemFunctionIndex(65536), new byte[]{0x60, 0x00, 0x00, 0x01, 0x00}); } - @Test - public void testElemGlobalIndex() { - test(b -> b.addElemGlobalIndex(256), new byte[]{(byte) 0xC0, 0x00, 0x01}); - } - @Test public void testCodeEntryMin() { test(b -> b.addCodeEntry(0, 0, 0, 0, 0), new byte[]{0x04, 0x00}); diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ReferenceTypesValidationSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ReferenceTypesValidationSuite.java index 31dcf9980adc..7b7f4a0e73e8 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ReferenceTypesValidationSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ReferenceTypesValidationSuite.java @@ -46,7 +46,7 @@ import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Value; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.test.AbstractBinarySuite; import org.junit.Assert; import org.junit.Test; @@ -308,7 +308,7 @@ public void testTableInitInvalidElementExpression() throws IOException { context.eval(source); Assert.fail("Should have thrown"); } catch (PolyglotException e) { - Assert.assertTrue("Expected type mismatch error", e.getMessage().contains("Invalid constant expression for table elem expression:")); + Assert.assertTrue("Expected type mismatch error", e.getMessage().contains("Expected result types [funcref], but got [i32]")); } }); } @@ -874,7 +874,7 @@ public void testGlobalWithNull() throws IOException { // (func (export "main") (type 0) // global.get 0 // ) - final byte[] binary = newBuilder().addGlobal(GlobalModifier.CONSTANT, WasmType.EXTERNREF_TYPE, "D0 6F 0B").addType(EMPTY_INTS, new int[]{WasmType.EXTERNREF_TYPE}).addFunction(0, + final byte[] binary = newBuilder().addGlobal(Mutability.CONSTANT, WasmType.EXTERNREF_TYPE, "D0 6F 0B").addType(EMPTY_INTS, new int[]{WasmType.EXTERNREF_TYPE}).addFunction(0, EMPTY_INTS, "23 00 0B").addFunctionExport(0, "main").build(); runRuntimeTest(binary, instance -> { Value main = instance.getMember("main"); @@ -890,7 +890,7 @@ public void testGlobalWithNullException() throws IOException { // (func (export "main") (type 0) // global.get 0 // ) - final byte[] binary = newBuilder().addGlobal(GlobalModifier.CONSTANT, WasmType.EXNREF_TYPE, "D0 69 0B").addType(EMPTY_INTS, new int[]{WasmType.EXNREF_TYPE}).addFunction(0, + final byte[] binary = newBuilder().addGlobal(Mutability.CONSTANT, WasmType.EXNREF_TYPE, "D0 69 0B").addType(EMPTY_INTS, new int[]{WasmType.EXNREF_TYPE}).addFunction(0, EMPTY_INTS, "23 00 0B").addFunctionExport(0, "main").build(); runRuntimeTest(binary, options -> options.option("wasm.Exceptions", "true"), instance -> { Value main = instance.getMember("main"); @@ -914,7 +914,7 @@ public void testGlobalWithFunction() throws IOException { // i32.const 0 // call_indirect 0 (type 0) // ) - final byte[] binary = newBuilder().addGlobal(GlobalModifier.CONSTANT, WasmType.FUNCREF_TYPE, "D2 00 0B").addTable(1, 1, WasmType.FUNCREF_TYPE).addType(EMPTY_INTS, + final byte[] binary = newBuilder().addGlobal(Mutability.CONSTANT, WasmType.FUNCREF_TYPE, "D2 00 0B").addTable(1, 1, WasmType.FUNCREF_TYPE).addType(EMPTY_INTS, new int[]{WasmType.I32_TYPE}).addFunction(0, EMPTY_INTS, "41 01 0B").addFunction(0, EMPTY_INTS, "41 00 23 00 26 00 41 00 11 00 00 0B").addFunctionExport( 1, "main").build(); runRuntimeTest(binary, instance -> { diff --git a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ValidationSuite.java b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ValidationSuite.java index 33de86503d32..ab583d714518 100644 --- a/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ValidationSuite.java +++ b/wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/validation/ValidationSuite.java @@ -220,12 +220,12 @@ public static Collection data() { // The type `C.types[x]` must be defined in the context. stringCase( "Function - invalid type index", - "Function type variable 1 out of range. (max 0)", + "Type variable 1 out of range. (max 0)", "(type (func (result i32))) (func (export \"f\") (type 1))", Failure.Type.INVALID), stringCase( "Function - invalid type index", - "Function type variable 1073741823 out of range. (max 0)", + "Type variable 1073741823 out of range. (max 0)", "(type (func (result i32))) (func (export \"f\") (type 1073741823))", Failure.Type.INVALID), @@ -936,7 +936,7 @@ public static Collection data() { // Indirect call with missing type binaryCase("Call_indirect - missing type", - "Function type variable 1 out of range. (max 0)", + "Type variable 1 out of range. (max 0)", // (module // (type (func)) // (table 1 funcref) @@ -968,7 +968,7 @@ public static Collection data() { null), binaryCase("Invalid instruction", - "Unknown opcode: 0x06", + "Legacy exception handling is not supported (opcode: 0x06)", // (module // (func diff --git a/wasm/src/org.graalvm.wasm.test/src/test/issue/GR-54851-typeindex-oob.result b/wasm/src/org.graalvm.wasm.test/src/test/issue/GR-54851-typeindex-oob.result index 78eb6a9b92a0..9c165bd04a24 100644 --- a/wasm/src/org.graalvm.wasm.test/src/test/issue/GR-54851-typeindex-oob.result +++ b/wasm/src/org.graalvm.wasm.test/src/test/issue/GR-54851-typeindex-oob.result @@ -1 +1 @@ -validation Function type variable 8128 out of range. (max 3) +validation Type variable 8128 out of range. (max 3) diff --git a/wasm/src/org.graalvm.wasm/src/META-INF/native-image/org.graalvm.wasm/wasm-language/native-image.properties b/wasm/src/org.graalvm.wasm/src/META-INF/native-image/org.graalvm.wasm/wasm-language/native-image.properties index 05fa961d3d5f..ecf98477f6bd 100644 --- a/wasm/src/org.graalvm.wasm/src/META-INF/native-image/org.graalvm.wasm/wasm-language/native-image.properties +++ b/wasm/src/org.graalvm.wasm/src/META-INF/native-image/org.graalvm.wasm/wasm-language/native-image.properties @@ -1,7 +1,7 @@ # This file contains native-image arguments needed to build graal-wasm Args = --initialize-at-build-time=org.graalvm.wasm \ - -H:MaxRuntimeCompileMethods=2700 \ + -H:MaxRuntimeCompileMethods=2850 \ -H:+UnlockExperimentalVMOptions \ -H:+VectorAPISupport \ --add-modules=jdk.incubator.vector diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java index 7790fc7ee553..15da6f363ddd 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java @@ -49,7 +49,13 @@ import static org.graalvm.wasm.Assert.assertUnsignedIntLessOrEqual; import static org.graalvm.wasm.Assert.assertUnsignedLongLessOrEqual; import static org.graalvm.wasm.Assert.fail; +import static org.graalvm.wasm.WasmType.ANYREF_TYPE; +import static org.graalvm.wasm.WasmType.ANY_HEAPTYPE; +import static org.graalvm.wasm.WasmType.ARRAYREF_TYPE; +import static org.graalvm.wasm.WasmType.ARRAY_HEAPTYPE; import static org.graalvm.wasm.WasmType.BOT; +import static org.graalvm.wasm.WasmType.EQREF_TYPE; +import static org.graalvm.wasm.WasmType.EQ_HEAPTYPE; import static org.graalvm.wasm.WasmType.EXNREF_TYPE; import static org.graalvm.wasm.WasmType.EXN_HEAPTYPE; import static org.graalvm.wasm.WasmType.EXTERNREF_TYPE; @@ -58,16 +64,29 @@ import static org.graalvm.wasm.WasmType.F64_TYPE; import static org.graalvm.wasm.WasmType.FUNCREF_TYPE; import static org.graalvm.wasm.WasmType.FUNC_HEAPTYPE; +import static org.graalvm.wasm.WasmType.I16_TYPE; +import static org.graalvm.wasm.WasmType.I31REF_TYPE; +import static org.graalvm.wasm.WasmType.I31_HEAPTYPE; import static org.graalvm.wasm.WasmType.I32_TYPE; import static org.graalvm.wasm.WasmType.I64_TYPE; +import static org.graalvm.wasm.WasmType.I8_TYPE; +import static org.graalvm.wasm.WasmType.NOEXN_HEAPTYPE; +import static org.graalvm.wasm.WasmType.NOEXTERN_HEAPTYPE; +import static org.graalvm.wasm.WasmType.NOFUNC_HEAPTYPE; +import static org.graalvm.wasm.WasmType.NONE_HEAPTYPE; +import static org.graalvm.wasm.WasmType.NULLEXNREF_TYPE; +import static org.graalvm.wasm.WasmType.NULLEXTERNREF_TYPE; +import static org.graalvm.wasm.WasmType.NULLFUNCREF_TYPE; +import static org.graalvm.wasm.WasmType.NULLREF_TYPE; import static org.graalvm.wasm.WasmType.REF_NULL_TYPE_HEADER; import static org.graalvm.wasm.WasmType.REF_TYPE_HEADER; +import static org.graalvm.wasm.WasmType.STRUCTREF_TYPE; +import static org.graalvm.wasm.WasmType.STRUCT_HEAPTYPE; import static org.graalvm.wasm.WasmType.V128_TYPE; import static org.graalvm.wasm.WasmType.VOID_BLOCK_TYPE; +import static org.graalvm.wasm.constants.Bytecode.REF_CAST; +import static org.graalvm.wasm.constants.Bytecode.REF_TEST; import static org.graalvm.wasm.constants.Bytecode.vectorOpcodeToBytecode; -import static org.graalvm.wasm.constants.BytecodeBitEncoding.ELEM_ITEM_REF_FUNC_ENTRY_PREFIX; -import static org.graalvm.wasm.constants.BytecodeBitEncoding.ELEM_ITEM_GLOBAL_GET_ENTRY_PREFIX; -import static org.graalvm.wasm.constants.BytecodeBitEncoding.ELEM_ITEM_REF_NULL_ENTRY_PREFIX; import static org.graalvm.wasm.constants.Sizes.MAX_MEMORY_64_DECLARATION_SIZE; import static org.graalvm.wasm.constants.Sizes.MAX_MEMORY_DECLARATION_SIZE; import static org.graalvm.wasm.constants.Sizes.MAX_TABLE_DECLARATION_SIZE; @@ -83,14 +102,14 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.Pair; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Shape; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Shape; import org.graalvm.wasm.collection.IntArrayList; import org.graalvm.wasm.constants.Bytecode; import org.graalvm.wasm.constants.BytecodeBitEncoding; import org.graalvm.wasm.constants.ExceptionHandlerType; import org.graalvm.wasm.constants.ExportIdentifier; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.constants.ImportIdentifier; import org.graalvm.wasm.constants.Instructions; import org.graalvm.wasm.constants.LimitsPrefix; @@ -103,6 +122,7 @@ import org.graalvm.wasm.parser.bytecode.BytecodeGen; import org.graalvm.wasm.parser.bytecode.BytecodeParser; import org.graalvm.wasm.parser.bytecode.RuntimeBytecodeGen; +import org.graalvm.wasm.parser.bytecode.RuntimeBytecodeGen.BranchOp.BrOnCast; import org.graalvm.wasm.parser.ir.CallNode; import org.graalvm.wasm.parser.ir.CodeEntry; import org.graalvm.wasm.parser.validation.ExceptionHandler; @@ -137,6 +157,7 @@ public class BinaryParser extends BinaryStreamParser { private final boolean simd; private final boolean exceptions; private final boolean typedFunctionReferences; + private final boolean gc; @TruffleBoundary public BinaryParser(WasmModule module, WasmContext context, byte[] data) { @@ -154,6 +175,7 @@ public BinaryParser(WasmModule module, WasmContext context, byte[] data) { this.simd = context.getContextOptions().supportSIMD(); this.exceptions = context.getContextOptions().supportExceptions(); this.typedFunctionReferences = context.getContextOptions().supportTypedFunctionReferences(); + this.gc = context.getContextOptions().supportGC(); } @TruffleBoundary @@ -408,18 +430,57 @@ private void readTagNames() { } private void readTypeSection() { - final int typeCount = readLength(); - module.limits().checkTypeCount(typeCount); - for (int typeIndex = 0; typeIndex != typeCount; typeIndex++) { + final int recursiveTypeGroupCount = readLength(); + int recursiveTypeGroupIndex = 0; + int typeIndex = 0; + while (recursiveTypeGroupIndex < recursiveTypeGroupCount) { + int recursiveTypeGroupStart = typeIndex; assertTrue(!isEOF(), Failure.LENGTH_OUT_OF_BOUNDS); - final byte type = read1(); - if (type == 0x60) { - readFunctionType(); + int subTypeCount; + if (peek1() == 0x4e) { + offset++; + subTypeCount = readLength(); } else { - // According to the official tests this should be an integer presentation too long - // error - fail(Failure.INTEGER_REPRESENTATION_TOO_LONG, "Only function types are supported in the type section"); + subTypeCount = 1; } + module.declareRecursiveTypeGroup(subTypeCount); + for (int subTypeIndex = 0; subTypeIndex < subTypeCount; subTypeIndex++) { + module.limits().checkTypeCount(typeIndex + 1); + module.registerFinalType(typeIndex, peek1() != 0x50); + if (peek1() == 0x4f || peek1() == 0x50) { + offset++; + int superTypeCount = readLength(); + if (superTypeCount == 1) { + int superTypeIndex = readTypeIndex(); + module.registerSuperType(typeIndex, superTypeIndex); + module.limits().checkSubtypeDepth(module.superTypeDepth(typeIndex)); + } else if (superTypeCount > 1) { + fail(Failure.SUB_TYPE, "Only one super type admissible in sub type definitions"); + } + } + switch (read1()) { + case 0x5e -> readArrayType(typeIndex); + case 0x5f -> readStructType(typeIndex); + case 0x60 -> readFunctionType(typeIndex); + // According to the official tests this should be an integer presentation too + // long error + default -> fail(Failure.INTEGER_REPRESENTATION_TOO_LONG, "Only function types are supported in the type section"); + } + typeIndex++; + } + module.finishRecursiveTypeGroup(recursiveTypeGroupStart, wasmContext.language()); + for (int subTypeIndex = typeIndex - subTypeCount; subTypeIndex < typeIndex; subTypeIndex++) { + if (module.hasSuperType(subTypeIndex)) { + int superTypeIndex = module.superType(subTypeIndex); + if (module.isFinalType(superTypeIndex)) { + Assert.fail(Failure.SUB_TYPE, "Declared supertype %d of subtype %d is final", superTypeIndex, subTypeIndex); + } + if (!module.closedTypeAt(subTypeIndex).expand().isSubtypeOf(module.closedTypeAt(superTypeIndex))) { + Assert.fail(Failure.SUB_TYPE_DOES_NOT_MATCH_SUPER_TYPE, "Subtype %d does not match supertype %d", subTypeIndex, superTypeIndex); + } + } + } + recursiveTypeGroupIndex++; } } @@ -436,7 +497,7 @@ private void readImportSection() { byte importType = readImportType(); switch (importType) { case ImportIdentifier.FUNCTION: { - int typeIndex = readTypeIndex(); + int typeIndex = readFunctionTypeIndex(); module.symbolTable().importFunction(moduleName, memberName, typeIndex); break; } @@ -470,7 +531,7 @@ private void readImportSection() { fail(Failure.MALFORMED_IMPORT_KIND, "Invalid import type identifier: 0x%02x", importType); } final byte attribute = readTagAttribute(); - final int typeIndex = readTypeIndex(); + final int typeIndex = readFunctionTypeIndex(); final int tagIndex = module.symbolTable().tagCount(); module.symbolTable().importTag(moduleName, memberName, tagIndex, attribute, typeIndex); break; @@ -487,7 +548,7 @@ private void readFunctionSection() { module.limits().checkFunctionCount(functionCount); for (int functionIndex = 0; functionIndex != functionCount; functionIndex++) { assertTrue(!isEOF(), Failure.LENGTH_OUT_OF_BOUNDS); - int functionTypeIndex = readTypeIndex(); + int functionTypeIndex = readFunctionTypeIndex(); module.symbolTable().declareFunction(functionTypeIndex); } } @@ -506,10 +567,10 @@ private void readTableSection(int endOffset) { offset += 2; elemType = readRefType(); readTableLimits(multiResult); - Pair initExpression = readConstantExpression(elemType, endOffset); - initValue = initExpression.getLeft(); + ConstantExpression initExpression = readConstantExpression(elemType, endOffset); + initValue = initExpression.constantValue(); // Drop the initializer bytecode if we can eval the initializer during parsing - initBytecode = initValue == null ? initExpression.getRight() : null; + initBytecode = initValue == null ? initExpression.bytecode() : null; } else { elemType = readRefType(); readTableLimits(multiResult); @@ -616,8 +677,17 @@ private static int[] encapsulateResultType(int type) { case F32_TYPE -> WasmType.F32_TYPE_ARRAY; case F64_TYPE -> WasmType.F64_TYPE_ARRAY; case V128_TYPE -> WasmType.V128_TYPE_ARRAY; + case NULLEXNREF_TYPE -> WasmType.NULLEXNREF_TYPE_ARRAY; + case NULLFUNCREF_TYPE -> WasmType.NULLFUNCREF_TYPE_ARRAY; + case NULLEXTERNREF_TYPE -> WasmType.NULLEXTERNREF_TYPE_ARRAY; + case NULLREF_TYPE -> WasmType.NULLREF_TYPE_ARRAY; case FUNCREF_TYPE -> WasmType.FUNCREF_TYPE_ARRAY; case EXTERNREF_TYPE -> WasmType.EXTERNREF_TYPE_ARRAY; + case ANYREF_TYPE -> WasmType.ANYREF_TYPE_ARRAY; + case EQREF_TYPE -> WasmType.EQREF_TYPE_ARRAY; + case I31REF_TYPE -> WasmType.I31REF_TYPE_ARRAY; + case STRUCTREF_TYPE -> WasmType.STRUCTREF_TYPE_ARRAY; + case ARRAYREF_TYPE -> WasmType.ARRAYREF_TYPE_ARRAY; case EXNREF_TYPE -> WasmType.EXNREF_TYPE_ARRAY; default -> new int[]{type}; }; @@ -814,7 +884,7 @@ private CodeEntry readFunction(int functionIndex, int[] locals, int sourceCodeEn break; } case Instructions.CALL_INDIRECT: { - final int expectedFunctionTypeIndex = readTypeIndex(); + final int expectedFunctionTypeIndex = readFunctionTypeIndex(); final int tableIndex = readTableIndex(); // Pop the function index to call state.popChecked(I32_TYPE); @@ -926,6 +996,14 @@ private CodeEntry readFunction(int functionIndex, int[] locals, int sourceCodeEn state.setUnreachable(); break; } + case Instructions.TRY: + case Instructions.RETHROW: + case Instructions.CATCH: + case Instructions.DELEGATE: + case Instructions.CATCH_ALL: { + checkLegacyExceptionHandlingSupport(opcode); + break; + } case Instructions.LOCAL_GET: { final int localIndex = readLocalIndex(); assertUnsignedIntLess(localIndex, locals.length, Failure.UNKNOWN_LOCAL); @@ -975,7 +1053,7 @@ private CodeEntry readFunction(int functionIndex, int[] locals, int sourceCodeEn case Instructions.GLOBAL_SET: { final int index = readGlobalIndex(); // Assert that the global is mutable. - assertByteEqual(module.symbolTable().globalMutability(index), GlobalModifier.MUTABLE, + assertByteEqual(module.symbolTable().globalMutability(index), Mutability.MUTABLE, "Immutable globals cannot be set: " + index, Failure.IMMUTABLE_GLOBAL_WRITE); state.popChecked(module.symbolTable().globalValueType(index)); state.addUnsignedInstruction(Bytecode.GLOBAL_SET_U8, index); @@ -1135,7 +1213,7 @@ private CodeEntry readFunction(int functionIndex, int[] locals, int sourceCodeEn } case Instructions.CALL_REF: { checkTypedFunctionReferencesSupport(opcode); - final int expectedFunctionTypeIndex = readTypeIndex(); + final int expectedFunctionTypeIndex = readFunctionTypeIndex(); final int functionReferenceType = WasmType.withNullable(true, expectedFunctionTypeIndex); state.popChecked(functionReferenceType); // Pop parameters @@ -1558,8 +1636,7 @@ private void readNumericInstructions(ParserState state, int opcode) { } case Instructions.DATA_DROP: { checkBulkMemoryAndRefTypesSupport(miscOpcode); - final int dataIndex = readUnsignedInt32(); - module.checkDataSegmentIndex(dataIndex); + final int dataIndex = readDataSegmentIndex(); state.addMiscFlag(); state.addInstruction(Bytecode.DATA_DROP, dataIndex); break; @@ -1646,8 +1723,7 @@ private void readNumericInstructions(ParserState state, int opcode) { } case Instructions.ELEM_DROP: { checkBulkMemoryAndRefTypesSupport(miscOpcode); - final int elementIndex = readUnsignedInt32(); - module.checkElemIndex(elementIndex); + final int elementIndex = readElemSegmentIndex(); state.addMiscFlag(); state.addInstruction(Bytecode.ELEM_DROP, elementIndex); break; @@ -1735,6 +1811,256 @@ private void readNumericInstructions(ParserState state, int opcode) { state.push(functionReferenceType); state.addInstruction(Bytecode.REF_FUNC, functionIndex); break; + case Instructions.REF_EQ: + checkGCSupport(opcode); + state.popChecked(EQREF_TYPE); + state.popChecked(EQREF_TYPE); + state.push(I32_TYPE); + state.addMiscFlag(); + state.addInstruction(Bytecode.REF_EQ); + break; + case Instructions.AGGREGATE: + checkGCSupport(opcode); + int aggregateOpcode = readUnsignedInt32(); + state.addAggregateFlag(); + switch (aggregateOpcode) { + case Instructions.STRUCT_NEW: { + int structTypeIdx = readStructTypeIndex(); + for (int fieldIdx = module.structTypeFieldCount(structTypeIdx) - 1; fieldIdx >= 0; fieldIdx--) { + state.popChecked(WasmType.unpack(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))); + } + state.push(WasmType.withNullable(false, structTypeIdx)); + state.addInstruction(Bytecode.STRUCT_NEW, structTypeIdx); + break; + } + case Instructions.STRUCT_NEW_DEFAULT: { + int structTypeIdx = readStructTypeIndex(); + for (int fieldIdx = module.structTypeFieldCount(structTypeIdx) - 1; fieldIdx >= 0; fieldIdx--) { + if (!WasmType.hasDefaultValue(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))) { + Assert.fail(Failure.TYPE_MISMATCH, "struct.new_default: field %d of struct type %d has non-defaultable type %s", fieldIdx, structTypeIdx, + WasmType.toString(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))); + } + } + state.push(WasmType.withNullable(false, structTypeIdx)); + state.addInstruction(Bytecode.STRUCT_NEW_DEFAULT, structTypeIdx); + break; + } + case Instructions.STRUCT_GET: + case Instructions.STRUCT_GET_S: + case Instructions.STRUCT_GET_U: { + int structTypeIdx = readStructTypeIndex(); + int fieldIdx = readUnsignedInt32(); + Assert.assertUnsignedIntLess(fieldIdx, module.structTypeFieldCount(structTypeIdx), Failure.INVALID_FIELD_INDEX); + int fieldType = module.structTypeFieldTypeAt(structTypeIdx, fieldIdx); + Assert.assertTrue(aggregateOpcode == Instructions.STRUCT_GET != WasmType.isPackedType(fieldType), Failure.INVALID_STRUCT_GETTER_SIGNEDNESS); + state.popChecked(WasmType.withNullable(true, structTypeIdx)); + state.push(WasmType.unpack(fieldType)); + state.addInstruction(aggregateOpcode, structTypeIdx, fieldIdx); + break; + } + case Instructions.STRUCT_SET: { + int structTypeIdx = readStructTypeIndex(); + int fieldIdx = readUnsignedInt32(); + Assert.assertUnsignedIntLess(fieldIdx, module.structTypeFieldCount(structTypeIdx), Failure.INVALID_FIELD_INDEX); + Assert.assertTrue(module.structTypeFieldMutabilityAt(structTypeIdx, fieldIdx) == Mutability.MUTABLE, Failure.FIELD_IS_IMMUTABLE); + int fieldType = module.structTypeFieldTypeAt(structTypeIdx, fieldIdx); + state.popChecked(WasmType.unpack(fieldType)); + state.popChecked(WasmType.withNullable(true, structTypeIdx)); + state.addInstruction(Bytecode.STRUCT_SET, structTypeIdx, fieldIdx); + break; + } + case Instructions.ARRAY_NEW: { + int arrayTypeIdx = readArrayTypeIndex(); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx))); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW, arrayTypeIdx); + break; + } + case Instructions.ARRAY_NEW_DEFAULT: { + int arrayTypeIdx = readArrayTypeIndex(); + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_DEFAULT, arrayTypeIdx); + break; + } + case Instructions.ARRAY_NEW_FIXED: { + int arrayTypeIdx = readArrayTypeIndex(); + int length = readUnsignedInt32(); + module.limits().checkArrayNewFixedLength(length); + int unpackedElementType = WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx)); + for (int i = 0; i < length; i++) { + state.popChecked(unpackedElementType); + } + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_FIXED, arrayTypeIdx, length); + break; + } + case Instructions.ARRAY_NEW_DATA: { + int arrayTypeIdx = readArrayTypeIndex(); + int dataIdx = readDataSegmentIndex(); + int unpackedElementType = WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx)); + Assert.assertTrue(WasmType.isNumberType(unpackedElementType) || WasmType.isVectorType(unpackedElementType), Failure.ARRAY_TYPE_IS_NOT_NUMERIC_OR_VECTOR); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_DATA, arrayTypeIdx, dataIdx); + break; + } + case Instructions.ARRAY_NEW_ELEM: { + int arrayTypeIdx = readArrayTypeIndex(); + int elemIdx = readElemSegmentIndex(); + module.checkElemType(elemIdx, module.arrayTypeElemType(arrayTypeIdx)); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_ELEM, arrayTypeIdx, elemIdx); + break; + } + case Instructions.ARRAY_GET: + case Instructions.ARRAY_GET_S: + case Instructions.ARRAY_GET_U: { + int arrayTypeIdx = readArrayTypeIndex(); + int elemType = module.arrayTypeElemType(arrayTypeIdx); + Assert.assertTrue(aggregateOpcode == Instructions.ARRAY_GET != WasmType.isPackedType(elemType), Failure.INVALID_ARRAY_GETTER_SIGNEDNESS); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, arrayTypeIdx)); + state.push(WasmType.unpack(elemType)); + state.addInstruction(aggregateOpcode, arrayTypeIdx); + break; + } + case Instructions.ARRAY_SET: { + int arrayTypeIdx = readArrayTypeIndex(); + Assert.assertTrue(module.arrayTypeMutability(arrayTypeIdx) == Mutability.MUTABLE, Failure.ARRAY_IS_IMMUTABLE); + state.popChecked(WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx))); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_SET, arrayTypeIdx); + break; + } + case Instructions.ARRAY_LEN: { + state.popChecked(WasmType.withNullable(true, ARRAY_HEAPTYPE)); + state.push(I32_TYPE); + state.addInstruction(Bytecode.ARRAY_LEN); + break; + } + case Instructions.ARRAY_FILL: { + int arrayTypeIdx = readArrayTypeIndex(); + Assert.assertTrue(module.arrayTypeMutability(arrayTypeIdx) == Mutability.MUTABLE, Failure.ARRAY_IS_IMMUTABLE); + state.popChecked(WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx))); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_FILL, arrayTypeIdx); + break; + } + case Instructions.ARRAY_COPY: { + int dstArrayTypeIdx = readArrayTypeIndex(); + int srcArrayTypeIdx = readArrayTypeIndex(); + Assert.assertTrue(module.matchesType(module.arrayTypeElemType(dstArrayTypeIdx), module.arrayTypeElemType(srcArrayTypeIdx)), Failure.ARRAY_TYPES_DO_NOT_MATCH); + Assert.assertTrue(module.arrayTypeMutability(dstArrayTypeIdx) == Mutability.MUTABLE, Failure.ARRAY_IS_IMMUTABLE); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, srcArrayTypeIdx)); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, dstArrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_COPY, dstArrayTypeIdx); + break; + } + case Instructions.ARRAY_INIT_DATA: { + int arrayTypeIdx = readArrayTypeIndex(); + int dataIdx = readDataSegmentIndex(); + int unpackedElementType = WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx)); + Assert.assertTrue(WasmType.isNumberType(unpackedElementType) || WasmType.isVectorType(unpackedElementType), Failure.ARRAY_TYPE_IS_NOT_NUMERIC_OR_VECTOR); + Assert.assertTrue(module.arrayTypeMutability(arrayTypeIdx) == Mutability.MUTABLE, Failure.ARRAY_IS_IMMUTABLE); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_INIT_DATA, arrayTypeIdx, dataIdx); + break; + } + case Instructions.ARRAY_INIT_ELEM: { + int arrayTypeIdx = readArrayTypeIndex(); + int elemIdx = readElemSegmentIndex(); + module.checkElemType(elemIdx, module.arrayTypeElemType(arrayTypeIdx)); + Assert.assertTrue(module.arrayTypeMutability(arrayTypeIdx) == Mutability.MUTABLE, Failure.ARRAY_IS_IMMUTABLE); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.withNullable(true, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_INIT_ELEM, elemIdx); + break; + } + case Instructions.REF_TEST_NON_NULL: + case Instructions.REF_TEST_NULL: { + int expectedHeapType = readHeapType(); + int topHeapType = module.topHeapTypeOf(expectedHeapType); + int expectedReferenceType = WasmType.withNullable(aggregateOpcode == Instructions.REF_TEST_NULL, expectedHeapType); + int topReferenceType = WasmType.withNullable(true, topHeapType); + state.popChecked(topReferenceType); + state.push(I32_TYPE); + state.addInstruction(REF_TEST, expectedReferenceType); + break; + } + case Instructions.REF_CAST_NON_NULL: + case Instructions.REF_CAST_NULL: { + int expectedHeapType = readHeapType(); + int topHeapType = module.topHeapTypeOf(expectedHeapType); + int expectedReferenceType = WasmType.withNullable(aggregateOpcode == Instructions.REF_CAST_NULL, expectedHeapType); + int topReferenceType = WasmType.withNullable(true, topHeapType); + state.popChecked(topReferenceType); + state.push(expectedReferenceType); + state.addInstruction(REF_CAST, expectedReferenceType); + break; + } + case Instructions.BR_ON_CAST: + case Instructions.BR_ON_CAST_FAIL: { + final byte castOp = readCastOp(); + final int branchLabel = readTargetOffset(); + final int topHeapType = readHeapType(); + final int topReferenceType = WasmType.withNullable((castOp & 0x01) != 0, topHeapType); + final int successfulHeapType = readHeapType(); + final int successfulReferenceType = WasmType.withNullable((castOp & 0x02) != 0, successfulHeapType); + final int failedReferenceType = WasmType.difference(topReferenceType, successfulReferenceType); + Assert.assertTrue(module.isSubtypeOf(successfulReferenceType, topReferenceType), Failure.TYPE_MISMATCH); + BrOnCast branchOp = new BrOnCast(aggregateOpcode == Instructions.BR_ON_CAST_FAIL, successfulReferenceType); + if (aggregateOpcode == Instructions.BR_ON_CAST) { + state.addBranchOnCast(branchLabel, topReferenceType, successfulReferenceType, failedReferenceType, branchOp); + } else { + state.addBranchOnCast(branchLabel, topReferenceType, failedReferenceType, successfulReferenceType, branchOp); + } + break; + } + case Instructions.ANY_CONVERT_EXTERN: { + int externrefType = state.popChecked(EXTERNREF_TYPE); + state.push(WasmType.withNullable(WasmType.isNullable(externrefType), ANY_HEAPTYPE)); + // nop - undo the aggregate flag + state.retreat(); + break; + } + case Instructions.EXTERN_CONVERT_ANY: { + int anyrefType = state.popChecked(ANYREF_TYPE); + state.push(WasmType.withNullable(WasmType.isNullable(anyrefType), EXTERN_HEAPTYPE)); + // nop - undo the aggregate flag + state.retreat(); + break; + } + case Instructions.REF_I31: { + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, I31_HEAPTYPE)); + state.addInstruction(Bytecode.REF_I31); + break; + } + case Instructions.I31_GET_S: + case Instructions.I31_GET_U: { + state.popChecked(I31REF_TYPE); + state.push(I32_TYPE); + state.addInstruction(aggregateOpcode); + break; + } + } + break; case Instructions.ATOMIC: checkThreadsSupport(opcode); int atomicOpcode = readUnsignedInt32(); @@ -2487,6 +2813,10 @@ private void checkRelaxedSIMDSupport(int vectorOpcode) { checkContextOption(wasmContext.getContextOptions().supportRelaxedSIMD(), "Relaxed vector instructions are not enabled (opcode: 0x%02x 0x%x)", Instructions.VECTOR, vectorOpcode); } + private static void checkLegacyExceptionHandlingSupport(int opcode) { + Assert.fail(Failure.UNSPECIFIED_MALFORMED, "Legacy exception handling is not supported (opcode: 0x%02x)", opcode); + } + private void checkExceptionHandlingSupport(int opcode) { checkContextOption(wasmContext.getContextOptions().supportExceptions(), "Exception handling is not enabled (opcode: 0x%02x)", opcode); } @@ -2495,6 +2825,10 @@ private void checkTypedFunctionReferencesSupport(int opcode) { checkContextOption(wasmContext.getContextOptions().supportTypedFunctionReferences(), "Typed function references are not enabled (opcode: 0x%02x)", opcode); } + private void checkGCSupport(int opcode) { + checkContextOption(wasmContext.getContextOptions().supportGC(), "Garbage collected types are not enabled (opcode: 0x%02x)", opcode); + } + private void store(ParserState state, int type, int n, long[] result) { int alignHint = readAlignHint(n); final int memoryIndex = readMemoryIndexFromAlignHint(alignHint); @@ -2655,28 +2989,33 @@ private ExceptionHandler[] readExceptionHandlers(ParserState state) { return handlers; } - private Pair readOffsetExpression(int endOffset) { + private ConstantExpression readOffsetExpression(int endOffset) { // Table offset expression must be a constant expression with result type i32. // https://webassembly.github.io/spec/core/syntax/modules.html#element-segments // https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions - Pair result = readConstantExpression(I32_TYPE, endOffset); - if (result.getRight() == null) { - return Pair.create((int) result.getLeft(), null); + ConstantExpression constExpr = readConstantExpression(I32_TYPE, endOffset); + if (constExpr.constantValue() == null) { + return new ConstantExpression<>(-1, constExpr.bytecode()); } else { - return Pair.create(-1, result.getRight()); + // drop the bytecode if we have a constant value + return new ConstantExpression<>((Integer) constExpr.constantValue(), null); } } - private Pair readLongOffsetExpression(int endOffset) { - Pair result = readConstantExpression(I64_TYPE, endOffset); - if (result.getRight() == null) { - return Pair.create((long) result.getLeft(), null); + private ConstantExpression readLongOffsetExpression(int endOffset) { + ConstantExpression constExpr = readConstantExpression(I64_TYPE, endOffset); + if (constExpr.constantValue() == null) { + return new ConstantExpression<>(-1L, constExpr.bytecode()); } else { - return Pair.create(-1L, result.getRight()); + // drop the bytecode if we have a constant value + return new ConstantExpression<>((Long) constExpr.constantValue(), null); } } - private Pair readConstantExpression(int resultType, int endOffset) { + private record ConstantExpression(T constantValue, byte[] bytecode) { + } + + private ConstantExpression readConstantExpression(int resultType, int endOffset) { // Read the constant expression. // https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions final RuntimeBytecodeGen bytecode = new RuntimeBytecodeGen(); @@ -2749,7 +3088,7 @@ private Pair readConstantExpression(int resultType, int endOffse break; case Instructions.GLOBAL_GET: { final int index = readGlobalIndex(); - assertIntEqual(module.globalMutability(index), GlobalModifier.CONSTANT, Failure.CONSTANT_EXPRESSION_REQUIRED); + assertIntEqual(module.globalMutability(index), Mutability.CONSTANT, Failure.CONSTANT_EXPRESSION_REQUIRED); state.push(module.symbolTable().globalValueType(index)); state.addUnsignedInstruction(Bytecode.GLOBAL_GET_U8, index); calculable = false; @@ -2797,6 +3136,97 @@ private Pair readConstantExpression(int resultType, int endOffse }); } break; + case Instructions.AGGREGATE: + checkGCSupport(opcode); + int aggregateOpcode = read1() & 0xFF; + state.addAggregateFlag(); + switch (aggregateOpcode) { + case Instructions.STRUCT_NEW: { + int structTypeIdx = readStructTypeIndex(); + for (int fieldIdx = module.structTypeFieldCount(structTypeIdx) - 1; fieldIdx >= 0; fieldIdx--) { + state.popChecked(WasmType.unpack(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))); + } + state.push(WasmType.withNullable(false, structTypeIdx)); + state.addInstruction(Bytecode.STRUCT_NEW, structTypeIdx); + // We cannot cache computed struct values because they are mutable + calculable = false; + break; + } + case Instructions.STRUCT_NEW_DEFAULT: { + int structTypeIdx = readStructTypeIndex(); + for (int fieldIdx = module.structTypeFieldCount(structTypeIdx) - 1; fieldIdx >= 0; fieldIdx--) { + if (!WasmType.hasDefaultValue(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))) { + Assert.fail(Failure.TYPE_MISMATCH, "struct.new_default: field %d of struct type %d has non-defaultable type %s", fieldIdx, structTypeIdx, + WasmType.toString(module.structTypeFieldTypeAt(structTypeIdx, fieldIdx))); + } + } + state.push(WasmType.withNullable(false, structTypeIdx)); + state.addInstruction(Bytecode.STRUCT_NEW_DEFAULT, structTypeIdx); + // We cannot cache computed struct values because they are mutable + calculable = false; + break; + } + case Instructions.ARRAY_NEW: { + int arrayTypeIdx = readArrayTypeIndex(); + state.popChecked(I32_TYPE); + state.popChecked(WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx))); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW, arrayTypeIdx); + // We cannot cache computed array values because they are mutable + calculable = false; + break; + } + case Instructions.ARRAY_NEW_DEFAULT: { + int arrayTypeIdx = readArrayTypeIndex(); + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_DEFAULT, arrayTypeIdx); + // We cannot cache computed array values because they are mutable + calculable = false; + break; + } + case Instructions.ARRAY_NEW_FIXED: { + int arrayTypeIdx = readArrayTypeIndex(); + int length = readUnsignedInt32(); + module.limits().checkArrayNewFixedLength(length); + int unpackedElementType = WasmType.unpack(module.arrayTypeElemType(arrayTypeIdx)); + for (int i = 0; i < length; i++) { + state.popChecked(unpackedElementType); + } + state.push(WasmType.withNullable(false, arrayTypeIdx)); + state.addInstruction(Bytecode.ARRAY_NEW_FIXED, arrayTypeIdx, length); + // We cannot cache computed array values because they are mutable + calculable = false; + break; + } + case Instructions.ANY_CONVERT_EXTERN: { + int externrefType = state.popChecked(EXTERNREF_TYPE); + state.push(WasmType.withNullable(WasmType.isNullable(externrefType), ANY_HEAPTYPE)); + // nop - undo the aggregate flag + state.retreat(); + break; + } + case Instructions.EXTERN_CONVERT_ANY: { + int anyrefType = state.popChecked(ANYREF_TYPE); + state.push(WasmType.withNullable(WasmType.isNullable(anyrefType), EXTERN_HEAPTYPE)); + // nop - undo the aggregate flag + state.retreat(); + break; + } + case Instructions.REF_I31: { + state.popChecked(I32_TYPE); + state.push(WasmType.withNullable(false, I31_HEAPTYPE)); + state.addInstruction(Bytecode.REF_I31); + if (calculable) { + stack.add((int) stack.removeLast() & ~(1 << 31)); + } + break; + } + default: + fail(Failure.ILLEGAL_OPCODE, "Invalid instruction for constant expression: 0x%02X 0x%02X", opcode, aggregateOpcode); + break; + } + break; case Instructions.VECTOR: checkSIMDSupport(); int vectorOpcode = read1() & 0xFF; @@ -2826,23 +3256,19 @@ private Pair readConstantExpression(int resultType, int endOffse Assert.assertTrue(opcode == Instructions.END, Failure.UNEXPECTED_END); assertIntEqual(state.valueStackSize(), 1, Failure.TYPE_MISMATCH, "Unexpected number of results on stack at constant expression end"); state.exit(multiValue); - if (calculable) { - return Pair.create(stack.removeLast(), null); - } else { - return Pair.create(null, bytecode.toArray()); - } + return new ConstantExpression<>(calculable ? stack.removeLast() : null, bytecode.toArray()); } - private long[] readFunctionIndices(int elemType) { + private Object[] readFunctionIndices(int elemType) { final int functionIndexCount = readLength(); - final long[] functionIndices = new long[functionIndexCount]; + final Object[] functionIndices = new Object[functionIndexCount]; for (int index = 0; index != functionIndexCount; index++) { assertTrue(!isEOF(), Failure.LENGTH_OUT_OF_BOUNDS); final int functionIndex = readDeclaredFunctionIndex(); module.addFunctionReference(functionIndex); final int functionReferenceType = WasmType.withNullable(false, module.function(functionIndex).typeIndex()); Assert.assertTrue(module.matchesType(elemType, functionReferenceType), Failure.TYPE_MISMATCH); - functionIndices[index] = ((long) ELEM_ITEM_REF_FUNC_ENTRY_PREFIX << 32) | functionIndex; + functionIndices[index] = functionIndex; } return functionIndices; } @@ -2854,62 +3280,12 @@ private void checkElemKind() { } } - private long[] readElemExpressions(int elemType) { + private Object[] readElemExpressions(int elemType, int endOffset) { final int expressionCount = readLength(); - final long[] elements = new long[expressionCount]; + final Object[] elements = new Object[expressionCount]; for (int index = 0; index != expressionCount; index++) { assertTrue(!isEOF(), Failure.LENGTH_OUT_OF_BOUNDS); - int opcode = read1() & 0xFF; - switch (opcode) { - case Instructions.I32_CONST: - case Instructions.I64_CONST: - case Instructions.F32_CONST: - case Instructions.F64_CONST: - throw WasmException.format(Failure.TYPE_MISMATCH, "Invalid constant expression for table elem expression: 0x%02X", opcode); - case Instructions.I32_ADD: - case Instructions.I32_SUB: - case Instructions.I32_MUL: - case Instructions.I64_ADD: - case Instructions.I64_SUB: - case Instructions.I64_MUL: - if (wasmContext.getContextOptions().supportExtendedConstExpressions()) { - throw WasmException.format(Failure.TYPE_MISMATCH, "Invalid constant expression for table elem expression: 0x%02X", opcode); - } else { - throw WasmException.format(Failure.ILLEGAL_OPCODE, "Illegal opcode for constant expression: 0x%02X", opcode); - } - case Instructions.REF_NULL: - final int heapType = readHeapType(); - final int nullableReferenceType = WasmType.withNullable(true, heapType); - Assert.assertTrue(module.matchesType(elemType, nullableReferenceType), "Invalid ref.null type: 0x%02X", Failure.TYPE_MISMATCH); - elements[index] = ((long) ELEM_ITEM_REF_NULL_ENTRY_PREFIX << 32); - break; - case Instructions.REF_FUNC: - final int functionIndex = readDeclaredFunctionIndex(); - module.addFunctionReference(functionIndex); - final int functionReferenceType = WasmType.withNullable(false, module.function(functionIndex).typeIndex()); - Assert.assertTrue(module.matchesType(elemType, functionReferenceType), "Invalid element type: 0x%02X", Failure.TYPE_MISMATCH); - elements[index] = ((long) ELEM_ITEM_REF_FUNC_ENTRY_PREFIX << 32) | functionIndex; - break; - case Instructions.GLOBAL_GET: - final int globalIndex = readGlobalIndex(); - assertIntEqual(module.globalMutability(globalIndex), GlobalModifier.CONSTANT, Failure.CONSTANT_EXPRESSION_REQUIRED); - final int valueType = module.globalValueType(globalIndex); - Assert.assertTrue(module.matchesType(elemType, valueType), Failure.TYPE_MISMATCH); - elements[index] = ((long) ELEM_ITEM_GLOBAL_GET_ENTRY_PREFIX << 32) | globalIndex; - break; - case Instructions.VECTOR: - checkSIMDSupport(); - int vectorOpcode = read1() & 0xFF; - switch (vectorOpcode) { - case Instructions.VECTOR_V128_CONST: - throw WasmException.format(Failure.TYPE_MISMATCH, "Invalid constant expression for table elem expression: 0x%02X 0x%02X", opcode, vectorOpcode); - default: - throw WasmException.format(Failure.ILLEGAL_OPCODE, "Illegal opcode for constant expression: 0x%02X 0x%02X", opcode, vectorOpcode); - } - default: - throw WasmException.format(Failure.ILLEGAL_OPCODE, "Illegal opcode for constant expression: 0x%02X", opcode); - } - readEnd(); + elements[index] = readConstantExpression(elemType, endOffset).bytecode(); } return elements; } @@ -2922,7 +3298,7 @@ private void readElementSection(RuntimeBytecodeGen bytecode, int endOffset) { int mode; final int currentOffsetAddress; final byte[] currentOffsetBytecode; - final long[] elements; + final Object[] elements; final int tableIndex; final int elemType; if (bulkMemoryAndRefTypes) { @@ -2937,9 +3313,9 @@ private void readElementSection(RuntimeBytecodeGen bytecode, int endOffset) { } else { tableIndex = 0; } - Pair offsetExpression = readOffsetExpression(endOffset); - currentOffsetAddress = offsetExpression.getLeft(); - currentOffsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readOffsetExpression(endOffset); + currentOffsetAddress = offsetExpression.constantValue(); + currentOffsetBytecode = offsetExpression.bytecode(); } else { mode = useTableIndex ? SegmentMode.DECLARATIVE : SegmentMode.PASSIVE; tableIndex = 0; @@ -2952,7 +3328,7 @@ private void readElementSection(RuntimeBytecodeGen bytecode, int endOffset) { } else { elemType = FUNCREF_TYPE; } - elements = readElemExpressions(elemType); + elements = readElemExpressions(elemType, endOffset); } else { if (useType) { checkElemKind(); @@ -2965,9 +3341,9 @@ private void readElementSection(RuntimeBytecodeGen bytecode, int endOffset) { } else { mode = SegmentMode.ACTIVE; tableIndex = readTableIndex(); - Pair offsetExpression = readOffsetExpression(endOffset); - currentOffsetAddress = offsetExpression.getLeft(); - currentOffsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readOffsetExpression(endOffset); + currentOffsetAddress = offsetExpression.constantValue(); + currentOffsetBytecode = offsetExpression.bytecode(); elemType = FUNCREF_TYPE; elements = readFunctionIndices(elemType); } @@ -2990,30 +3366,16 @@ private void readElementSection(RuntimeBytecodeGen bytecode, int endOffset) { store.linker().resolvePassiveElemSegment(store, instance, currentElemSegmentId, bytecodeOffset, elementCount); }); } - for (long element : elements) { - final int initType = (int) (element >> 32); - switch (initType) { - case ELEM_ITEM_REF_NULL_ENTRY_PREFIX: - bytecode.addElemNull(); - break; - case ELEM_ITEM_REF_FUNC_ENTRY_PREFIX: - final int functionIndex = (int) element; - bytecode.addElemFunctionIndex(functionIndex); - break; - case ELEM_ITEM_GLOBAL_GET_ENTRY_PREFIX: - final int globalIndex = (int) element; - bytecode.addElemGlobalIndex(globalIndex); - break; + for (Object element : elements) { + if (element instanceof Integer functionIndex) { + bytecode.addElemFunctionIndex(functionIndex); + } else { + bytecode.addElemBytecode((byte[]) element); } } } } - private void readEnd() { - final byte instruction = read1(); - assertByteEqual(instruction, (byte) Instructions.END, Failure.TYPE_MISMATCH); - } - private void readStartSection() { int startFunctionIndex = readDeclaredFunctionIndex(); module.symbolTable().setStartFunction(startFunctionIndex); @@ -3072,7 +3434,7 @@ private void readTagSection() { assertTrue(!isEOF(), Failure.LENGTH_OUT_OF_BOUNDS); // 0x00 means exception final byte attribute = readTagAttribute(); - final int type = readTypeIndex(); + final int type = readFunctionTypeIndex(); module.symbolTable().allocateTag(tagIndex, attribute, type); } @@ -3089,9 +3451,9 @@ private void readGlobalSection(int endOffset) { final byte mutability = readMutability(); // Global initialization expressions must be constant expressions: // https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions - Pair initExpression = readConstantExpression(type, endOffset); - final Object initValue = initExpression.getLeft(); - final byte[] initBytecode = initExpression.getRight(); + ConstantExpression initExpression = readConstantExpression(type, endOffset); + final Object initValue = initExpression.constantValue(); + final byte[] initBytecode = initExpression.constantValue() == null ? initExpression.bytecode() : null; final boolean isInitialized = initBytecode == null; module.symbolTable().declareGlobal(globalIndex, type, mutability, isInitialized, initBytecode, initValue); @@ -3136,13 +3498,13 @@ private void readDataSection(RuntimeBytecodeGen bytecode, int endOffset) { if (mode == SegmentMode.ACTIVE) { checkMemoryIndex(memoryIndex); if (module.memoryHasIndexType64(memoryIndex)) { - Pair offsetExpression = readLongOffsetExpression(endOffset); - offsetAddress = offsetExpression.getLeft(); - offsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readLongOffsetExpression(endOffset); + offsetAddress = offsetExpression.constantValue(); + offsetBytecode = offsetExpression.bytecode(); } else { - Pair offsetExpression = readOffsetExpression(endOffset); - offsetAddress = offsetExpression.getLeft(); - offsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readOffsetExpression(endOffset); + offsetAddress = offsetExpression.constantValue(); + offsetBytecode = offsetExpression.bytecode(); } } else { offsetAddress = -1; @@ -3157,13 +3519,13 @@ private void readDataSection(RuntimeBytecodeGen bytecode, int endOffset) { memoryIndex = 0; } if (module.memoryHasIndexType64(memoryIndex)) { - Pair offsetExpression = readLongOffsetExpression(endOffset); - offsetAddress = offsetExpression.getLeft(); - offsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readLongOffsetExpression(endOffset); + offsetAddress = offsetExpression.constantValue(); + offsetBytecode = offsetExpression.bytecode(); } else { - Pair offsetExpression = readOffsetExpression(endOffset); - offsetAddress = offsetExpression.getLeft(); - offsetBytecode = offsetExpression.getRight(); + ConstantExpression offsetExpression = readOffsetExpression(endOffset); + offsetAddress = offsetExpression.constantValue(); + offsetBytecode = offsetExpression.bytecode(); } } @@ -3197,7 +3559,24 @@ private void readDataSection(RuntimeBytecodeGen bytecode, int endOffset) { } } - private void readFunctionType() { + private void readArrayType(int arrayTypeIdx) { + int fieldType = readStorageType(); + byte mutability = readMutability(); + module.registerArrayType(arrayTypeIdx, fieldType, mutability); + } + + private void readStructType(int structTypeIdx) { + int fieldCount = readLength(); + module.limits().checkStructFieldCount(fieldCount); + module.registerStructType(structTypeIdx, fieldCount); + for (int fieldIdx = 0; fieldIdx < fieldCount; fieldIdx++) { + int fieldType = readStorageType(); + byte fieldMutability = readMutability(); + module.registerStructTypeField(structTypeIdx, fieldIdx, fieldType, fieldMutability); + } + } + + private void readFunctionType(int funcTypeIdx) { int paramCount = readLength(); module.limits().checkParamCount(paramCount); int[] paramTypes = new int[paramCount]; @@ -3212,17 +3591,17 @@ private void readFunctionType() { resultTypes[resultIdx] = readValueType(); } - int funcTypeIdx = module.symbolTable().allocateFunctionType(paramCount, resultCount, multiValue); + module.symbolTable().registerFunctionType(funcTypeIdx, paramCount, resultCount, multiValue); for (int paramIdx = 0; paramIdx < paramCount; paramIdx++) { module.symbolTable().registerFunctionTypeParameterType(funcTypeIdx, paramIdx, paramTypes[paramIdx]); } for (int resultIdx = 0; resultIdx < resultCount; resultIdx++) { module.symbolTable().registerFunctionTypeResultType(funcTypeIdx, resultIdx, resultTypes[resultIdx]); } - module.symbolTable().finishFunctionType(funcTypeIdx); } protected int readValueType() { + final int typeOffset = offset; final int type = readSignedInt32(); return switch (type) { case I32_TYPE, I64_TYPE, F32_TYPE, F64_TYPE -> type; @@ -3238,6 +3617,10 @@ protected int readValueType() { Assert.assertTrue(exceptions, Failure.MALFORMED_VALUE_TYPE); yield type; } + case NULLEXNREF_TYPE, NULLFUNCREF_TYPE, NULLEXTERNREF_TYPE, NULLREF_TYPE, ANYREF_TYPE, EQREF_TYPE, I31REF_TYPE, STRUCTREF_TYPE, ARRAYREF_TYPE -> { + Assert.assertTrue(gc, Failure.MALFORMED_VALUE_TYPE); + yield type; + } case REF_NULL_TYPE_HEADER -> { Assert.assertTrue(typedFunctionReferences, Failure.MALFORMED_VALUE_TYPE); yield WasmType.withNullable(true, readHeapType()); @@ -3246,7 +3629,19 @@ protected int readValueType() { Assert.assertTrue(typedFunctionReferences, Failure.MALFORMED_VALUE_TYPE); yield WasmType.withNullable(false, readHeapType()); } - default -> throw Assert.fail(Failure.MALFORMED_VALUE_TYPE, "Invalid value type: 0x%02X", type); + default -> throw Assert.fail(Failure.MALFORMED_VALUE_TYPE, "Invalid value type: 0x%02X", peek1(data, typeOffset)); + }; + } + + private int readStorageType() { + long typeAndLength = peekSignedInt32AndLength(data, offset); + int type = value(typeAndLength); + return switch (type) { + case I8_TYPE, I16_TYPE -> { + offset += length(typeAndLength); + yield type; + } + default -> readValueType(); }; } @@ -3283,6 +3678,11 @@ protected void readBlockType(int[] result) { result[0] = type; result[1] = BLOCK_TYPE_VALTYPE; } + case NULLEXNREF_TYPE, NULLFUNCREF_TYPE, NULLEXTERNREF_TYPE, NULLREF_TYPE, ANYREF_TYPE, EQREF_TYPE, I31REF_TYPE, STRUCTREF_TYPE, ARRAYREF_TYPE -> { + Assert.assertTrue(gc, Failure.MALFORMED_VALUE_TYPE); + result[0] = type; + result[1] = BLOCK_TYPE_VALTYPE; + } case REF_NULL_TYPE_HEADER, REF_TYPE_HEADER -> { boolean nullable = type == REF_NULL_TYPE_HEADER; int heapType = readHeapType(); @@ -3337,18 +3737,83 @@ private int readDeclaredFunctionIndex() { * @throws WasmException If the given function type is greater or equal to the given maximum. */ public void checkFunctionTypeExists(int typeIndex) { + checkTypeExists(typeIndex); + checkIsFunctionType(typeIndex); + } + + /** + * Checks if the given type is a struct type. + * + * @param typeIndex The type index. + * @throws WasmException If the given type is not a struct type. + */ + public void checkIsArrayType(int typeIndex) { + if (!module.isArrayType(typeIndex)) { + throw ValidationErrors.createExpectedArrayType(typeIndex); + } + } + + /** + * Checks if the given type is a struct type. + * + * @param typeIndex The type index. + * @throws WasmException If the given type is not a struct type. + */ + public void checkIsStructType(int typeIndex) { + if (!module.isStructType(typeIndex)) { + throw ValidationErrors.createExpectedStructType(typeIndex); + } + } + + /** + * Checks if the given type is a function type. + * + * @param typeIndex The type index. + * @throws WasmException If the given type is not a function type. + */ + public void checkIsFunctionType(int typeIndex) { + if (!module.isFunctionType(typeIndex)) { + throw ValidationErrors.createExpectedFunctionType(typeIndex); + } + } + + /** + * Checks if the given type is within range. + * + * @param typeIndex The defined type. + * @throws WasmException If the given type index is greater or equal to the given maximum. + */ + public void checkTypeExists(int typeIndex) { if (compareUnsigned(typeIndex, module.typeCount()) >= 0) { if (module.typeCount() > 0) { - throw ValidationErrors.createMissingFunctionType(typeIndex, module.typeCount() - 1); + throw ValidationErrors.createMissingType(typeIndex, module.typeCount() - 1); } else { - throw ValidationErrors.createMissingFunctionType(typeIndex); + throw ValidationErrors.createMissingType(typeIndex); } } } private int readTypeIndex() { final int typeIndex = readUnsignedInt32(); - checkFunctionTypeExists(typeIndex); + checkTypeExists(typeIndex); + return typeIndex; + } + + private int readArrayTypeIndex() { + final int typeIndex = readTypeIndex(); + checkIsArrayType(typeIndex); + return typeIndex; + } + + private int readStructTypeIndex() { + final int typeIndex = readTypeIndex(); + checkIsStructType(typeIndex); + return typeIndex; + } + + private int readFunctionTypeIndex() { + final int typeIndex = readTypeIndex(); + checkIsFunctionType(typeIndex); return typeIndex; } @@ -3383,6 +3848,18 @@ private int readGlobalIndex() { return index; } + private int readDataSegmentIndex() { + final int index = readUnsignedInt32(); + module.checkDataSegmentIndex(index); + return index; + } + + private int readElemSegmentIndex() { + final int index = readUnsignedInt32(); + module.checkElemIndex(index); + return index; + } + private int readLocalIndex() { return readUnsignedInt32(); } @@ -3399,6 +3876,14 @@ private byte readImportType() { return read1(); } + private byte readCastOp() { + byte castOp = read1(); + if (castOp < 0x00 || castOp > 0x03) { + throw Assert.fail(Failure.MALFORMED_CASTOP_FLAGS, "unexpected value for castop flag: 0x%02X", castOp); + } + return castOp; + } + private int readRefType() { final int refType = readSignedInt32(); return switch (refType) { @@ -3407,6 +3892,10 @@ private int readRefType() { assertTrue(exceptions, Failure.MALFORMED_REFERENCE_TYPE); yield refType; } + case NULLEXNREF_TYPE, NULLFUNCREF_TYPE, NULLEXTERNREF_TYPE, NULLREF_TYPE, ANYREF_TYPE, EQREF_TYPE, I31REF_TYPE, STRUCTREF_TYPE, ARRAYREF_TYPE -> { + assertTrue(gc, Failure.MALFORMED_REFERENCE_TYPE); + yield refType; + } case REF_NULL_TYPE_HEADER -> { assertTrue(typedFunctionReferences, Failure.MALFORMED_REFERENCE_TYPE); yield WasmType.withNullable(true, readHeapType()); @@ -3427,11 +3916,13 @@ private int readHeapType() { assertTrue(exceptions, Failure.MALFORMED_HEAP_TYPE); yield heapType; } + case NOEXN_HEAPTYPE, NOFUNC_HEAPTYPE, NOEXTERN_HEAPTYPE, NONE_HEAPTYPE, ANY_HEAPTYPE, EQ_HEAPTYPE, I31_HEAPTYPE, STRUCT_HEAPTYPE, ARRAY_HEAPTYPE -> { + assertTrue(gc, Failure.MALFORMED_HEAP_TYPE); + yield heapType; + } default -> { - if (heapType < 0 || heapType >= module.typeCount()) { - throw fail(Failure.UNKNOWN_TYPE, "Unknown heap type %d", heapType); - } assertTrue(typedFunctionReferences, Failure.MALFORMED_HEAP_TYPE); + checkTypeExists(heapType); yield heapType; } }; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryStreamParser.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryStreamParser.java index 4ca161b0c6ea..6a5172f92303 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryStreamParser.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryStreamParser.java @@ -47,7 +47,7 @@ import java.nio.ByteOrder; import java.util.Arrays; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; @@ -200,9 +200,9 @@ protected byte readMutability() { protected byte peekMutability() { final byte mut = peek1(); - if (mut == GlobalModifier.CONSTANT) { + if (mut == Mutability.CONSTANT) { return mut; - } else if (mut == GlobalModifier.MUTABLE) { + } else if (mut == Mutability.MUTABLE) { return mut; } else { throw Assert.fail(Failure.MALFORMED_MUTABILITY, "Invalid mutability flag: 0x%02x", mut); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/EmbedderDataHolder.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/EmbedderDataHolder.java index 2e4da16dd841..792a84336c44 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/EmbedderDataHolder.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/EmbedderDataHolder.java @@ -41,17 +41,11 @@ package org.graalvm.wasm; /** - * Superclass for wasm entities that can hold embedder data. + * Interface for wasm entities that can hold embedder data. */ -public class EmbedderDataHolder { - private Object embedderData = WasmConstant.VOID; +public interface EmbedderDataHolder { - public void setEmbedderData(Object embedderData) { - this.embedderData = embedderData; - } - - public Object getEmbedderData() { - return embedderData; - } + void setEmbedderData(Object embedderData); + Object getEmbedderData(); } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/GlobalRegistry.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/GlobalRegistry.java index 8bfc6fb9b07a..e8599f7f7c0a 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/GlobalRegistry.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/GlobalRegistry.java @@ -43,7 +43,7 @@ import java.util.BitSet; import java.util.Objects; -import org.graalvm.wasm.api.Vector128; +import org.graalvm.wasm.vector.Vector128; import org.graalvm.wasm.globals.WasmGlobal; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java index 8a68f57a569a..01b89cb335f5 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java @@ -87,15 +87,28 @@ import org.graalvm.wasm.Linker.ResolutionDag.Resolver; import org.graalvm.wasm.Linker.ResolutionDag.Sym; import org.graalvm.wasm.api.ExecuteHostFunctionNode; -import org.graalvm.wasm.api.Vector128; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.array.WasmArray; +import org.graalvm.wasm.array.WasmFloat32Array; +import org.graalvm.wasm.array.WasmFloat64Array; +import org.graalvm.wasm.array.WasmInt16Array; +import org.graalvm.wasm.array.WasmInt32Array; +import org.graalvm.wasm.array.WasmInt64Array; +import org.graalvm.wasm.array.WasmInt8Array; +import org.graalvm.wasm.array.WasmRefArray; +import org.graalvm.wasm.array.WasmVec128Array; import org.graalvm.wasm.constants.Bytecode; import org.graalvm.wasm.constants.BytecodeBitEncoding; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; import org.graalvm.wasm.globals.WasmGlobal; import org.graalvm.wasm.memory.WasmMemory; import org.graalvm.wasm.memory.WasmMemoryLibrary; +import org.graalvm.wasm.struct.WasmStruct; +import org.graalvm.wasm.struct.WasmStructAccess; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.ValueType; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; @@ -103,6 +116,7 @@ import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.staticobject.StaticProperty; public class Linker { public enum LinkState { @@ -191,7 +205,6 @@ private void tryLinkOutsidePartialEvaluation(WasmInstance entryPointInstance, Im ArrayList failures = new ArrayList<>(); final int maxStartFunctionIndex = runLinkActions(store, instances, importValues, failures); linkTopologically(store, failures, maxStartFunctionIndex); - assignTypeEquivalenceClasses(store); resolutionDag = null; runStartFunctions(instances, failures); checkFailures(failures); @@ -238,39 +251,6 @@ private void linkTopologically(WasmStore store, ArrayList failures, i } } - private static void assignTypeEquivalenceClasses(WasmStore store) { - final Map instances = store.moduleInstances(); - for (WasmInstance instance : instances.values()) { - WasmModule module = instance.module(); - if (instance.isLinkInProgress() && !module.isParsed()) { - assignTypeEquivalenceClasses(module, store.language()); - } - } - } - - private static void assignTypeEquivalenceClasses(WasmModule module, WasmLanguage language) { - var lock = module.getLock(); - lock.lock(); - try { - if (module.isParsed()) { - return; - } - final SymbolTable symtab = module.symbolTable(); - for (int index = 0; index < symtab.typeCount(); index++) { - SymbolTable.ClosedFunctionType type = symtab.closedFunctionTypeAt(index); - int equivalenceClass = language.equivalenceClassFor(type); - symtab.setEquivalenceClass(index, equivalenceClass); - } - for (int index = 0; index < symtab.numFunctions(); index++) { - final WasmFunction function = symtab.function(index); - function.setTypeEquivalenceClass(symtab.equivalenceClass(function.typeIndex())); - } - module.setParsed(); - } finally { - lock.unlock(); - } - } - private static void runStartFunctions(Map instances, ArrayList failures) { List instanceList = new ArrayList<>(instances.values()); instanceList.sort(Comparator.comparingInt(RuntimeState::startFunctionIndex)); @@ -350,19 +330,19 @@ void resolveGlobalImport(WasmStore store, WasmInstance instance, ImportDescripto externalGlobal = importedInstance.externalGlobal(exportedGlobalIndex); } - SymbolTable.ClosedValueType importType = instance.symbolTable().closedTypeOf(valueType); - SymbolTable.ClosedValueType exportType = externalGlobal.getClosedType(); + ValueType importType = instance.symbolTable().closedTypeOf(valueType); + ValueType exportType = externalGlobal.getClosedType(); if (mutability != externalGlobal.getMutability()) { throw WasmException.create(Failure.INCOMPATIBLE_IMPORT_TYPE, "Global variable '" + importedGlobalName + "' is imported into module '" + instance.name() + - "' with the modifier " + GlobalModifier.asString(mutability) + ", " + - "but it was exported in the module '" + importedModuleName + "' with the modifier " + GlobalModifier.asString(externalGlobal.getMutability()) + "."); + "' with the modifier " + Mutability.asString(mutability) + ", " + + "but it was exported in the module '" + importedModuleName + "' with the modifier " + Mutability.asString(externalGlobal.getMutability()) + "."); } // matching for mutable globals does not work by subtyping, but requires equivalent // types - if (!(externalGlobal.isMutable() ? importType.equals(exportType) : importType.isSupertypeOf(exportType))) { + if (!(externalGlobal.isMutable() ? importType.equals(exportType) : exportType.isSubtypeOf(importType))) { throw WasmException.create(Failure.INCOMPATIBLE_IMPORT_TYPE, "Global variable '" + importedGlobalName + "' is imported into module '" + instance.name() + - "' with the type " + GlobalModifier.asString(mutability) + " " + WasmType.toString(valueType) + ", " + - "but it was exported in the module '" + importedModuleName + "' with the type " + GlobalModifier.asString(externalGlobal.getMutability()) + " " + + "' with the type " + Mutability.asString(mutability) + " " + WasmType.toString(valueType) + ", " + + "but it was exported in the module '" + importedModuleName + "' with the type " + Mutability.asString(externalGlobal.getMutability()) + " " + WasmType.toString(externalGlobal.getType()) + "."); } instance.setExternalGlobal(globalIndex, externalGlobal); @@ -424,7 +404,7 @@ void resolveFunctionImport(WasmStore store, WasmInstance instance, WasmFunction Object externalFunctionInstance = lookupImportObject(instance, importDescriptor, imports, Object.class); if (externalFunctionInstance != null) { if (externalFunctionInstance instanceof WasmFunctionInstance functionInstance) { - if (!function.closedType().isSupertypeOf(functionInstance.function().closedType())) { + if (!functionInstance.type().isSubtypeOf(function.type())) { throw WasmException.create(Failure.INCOMPATIBLE_IMPORT_TYPE); } instance.setTarget(function.index(), functionInstance.target()); @@ -457,7 +437,7 @@ void resolveFunctionImport(WasmStore store, WasmInstance instance, WasmFunction throw WasmException.create(Failure.UNKNOWN_IMPORT, "The imported function '" + function.importedFunctionName() + "', referenced in the module '" + instance.name() + "', does not exist in the imported module '" + function.importedModuleName() + "'."); } - if (!function.closedType().isSupertypeOf(importedFunction.closedType())) { + if (!importedFunction.type().isSubtypeOf(function.type())) { throw WasmException.create(Failure.INCOMPATIBLE_IMPORT_TYPE); } final CallTarget target = importedInstance.target(importedFunction.index()); @@ -530,7 +510,7 @@ void resolveMemoryExport(WasmInstance instance, int memoryIndex, String exported }); } - void resolveTagImport(WasmStore store, WasmInstance instance, ImportDescriptor importDescriptor, int tagIndex, SymbolTable.ClosedFunctionType type, ImportValueSupplier imports) { + void resolveTagImport(WasmStore store, WasmInstance instance, ImportDescriptor importDescriptor, int tagIndex, DefinedType type, ImportValueSupplier imports) { final String importedModuleName = importDescriptor.moduleName(); final String importedTagName = importDescriptor.memberName(); final Runnable resolveAction = () -> { @@ -595,9 +575,13 @@ private static Object lookupGlobal(WasmInstance instance, int index) { } public static Object evalConstantExpression(WasmInstance instance, byte[] bytecode) { - int offset = 0; + return evalConstantExpression(instance, bytecode, 0, bytecode.length); + } + + public static Object evalConstantExpression(WasmInstance instance, byte[] bytecode, int start, int end) { + int offset = start; List stack = new ArrayList<>(); - while (offset < bytecode.length) { + while (offset < end) { int opcode = rawPeekU8(bytecode, offset); offset++; switch (opcode) { @@ -649,12 +633,6 @@ public static Object evalConstantExpression(WasmInstance instance, byte[] byteco stack.add(value); break; } - case Bytecode.VECTOR_V128_CONST: { - Vector128 value = new Vector128(rawPeekI128(bytecode, offset)); - offset += 16; - stack.add(value); - break; - } case Bytecode.REF_NULL: stack.add(WasmConstant.NULL); break; @@ -693,6 +671,212 @@ public static Object evalConstantExpression(WasmInstance instance, byte[] byteco stack.add(result); break; } + case Bytecode.AGGREGATE: + int aggregateOpcode = rawPeekU8(bytecode, offset); + offset++; + switch (aggregateOpcode) { + case Bytecode.STRUCT_NEW: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + SymbolTable symtab = instance.symbolTable(); + WasmStructAccess structAccess = symtab.structTypeAccess(structTypeIdx); + + WasmStruct struct = structAccess.shape().getFactory().create(symtab.closedTypeAt(structTypeIdx)); + for (int fieldIndex = symtab.structTypeFieldCount(structTypeIdx) - 1; fieldIndex >= 0; fieldIndex--) { + int fieldType = symtab.structTypeFieldTypeAt(structTypeIdx, fieldIndex); + StaticProperty property = structAccess.properties()[fieldIndex]; + Object fieldValue = stack.removeLast(); + switch (fieldType) { + case WasmType.I8_TYPE -> property.setByte(struct, (byte) (int) fieldValue); + case WasmType.I16_TYPE -> property.setShort(struct, (short) (int) fieldValue); + case WasmType.I32_TYPE -> property.setInt(struct, (int) fieldValue); + case WasmType.I64_TYPE -> property.setLong(struct, (long) fieldValue); + case WasmType.F32_TYPE -> property.setFloat(struct, (float) fieldValue); + case WasmType.F64_TYPE -> property.setDouble(struct, (double) fieldValue); + case WasmType.V128_TYPE -> property.setObject(struct, fieldValue); + default -> { + assert WasmType.isReferenceType(fieldType); + property.setObject(struct, fieldValue); + } + } + } + stack.add(struct); + break; + } + case Bytecode.STRUCT_NEW_DEFAULT: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + SymbolTable symtab = instance.symbolTable(); + + WasmStruct struct = symtab.structTypeAccess(structTypeIdx).shape().getFactory().create(symtab.closedTypeAt(structTypeIdx)); + stack.add(struct); + break; + } + case Bytecode.ARRAY_NEW: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + SymbolTable symtab = instance.symbolTable(); + DefinedType arrayType = symtab.closedTypeAt(arrayTypeIdx); + int elemType = symtab.arrayTypeElemType(arrayTypeIdx); + + int length = (int) stack.removeLast(); + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> { + byte initialValue = (byte) (int) stack.removeLast(); + yield new WasmInt8Array(arrayType, length, initialValue); + } + case WasmType.I16_TYPE -> { + short initialValue = (short) (int) stack.removeLast(); + yield new WasmInt16Array(arrayType, length, initialValue); + } + case WasmType.I32_TYPE -> { + int initialValue = (int) stack.removeLast(); + yield new WasmInt32Array(arrayType, length, initialValue); + } + case WasmType.I64_TYPE -> { + long initialValue = (long) stack.removeLast(); + yield new WasmInt64Array(arrayType, length, initialValue); + } + case WasmType.F32_TYPE -> { + float initialValue = (float) stack.removeLast(); + yield new WasmFloat32Array(arrayType, length, initialValue); + } + case WasmType.F64_TYPE -> { + double initialValue = (double) stack.removeLast(); + yield new WasmFloat64Array(arrayType, length, initialValue); + } + case WasmType.V128_TYPE -> { + Vector128 initialValue = (Vector128) stack.removeLast(); + yield new WasmVec128Array(arrayType, length, initialValue); + } + default -> { + Object initialValue = stack.removeLast(); + yield new WasmRefArray(arrayType, length, initialValue); + } + }; + stack.add(array); + break; + } + case Bytecode.ARRAY_NEW_DEFAULT: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + SymbolTable symtab = instance.symbolTable(); + DefinedType arrayType = symtab.closedTypeAt(arrayTypeIdx); + int elemType = symtab.arrayTypeElemType(arrayTypeIdx); + + int length = (int) stack.removeLast(); + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> new WasmInt8Array(arrayType, length); + case WasmType.I16_TYPE -> new WasmInt16Array(arrayType, length); + case WasmType.I32_TYPE -> new WasmInt32Array(arrayType, length); + case WasmType.I64_TYPE -> new WasmInt64Array(arrayType, length); + case WasmType.F32_TYPE -> new WasmFloat32Array(arrayType, length); + case WasmType.F64_TYPE -> new WasmFloat64Array(arrayType, length); + case WasmType.V128_TYPE -> new WasmVec128Array(arrayType, length); + default -> new WasmRefArray(arrayType, length); + }; + stack.add(array); + break; + } + case Bytecode.ARRAY_NEW_FIXED: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + final int length = rawPeekI32(bytecode, offset + 4); + offset += 8; + + SymbolTable symtab = instance.symbolTable(); + DefinedType arrayType = symtab.closedTypeAt(arrayTypeIdx); + int elemType = symtab.arrayTypeElemType(arrayTypeIdx); + + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> { + byte[] fixedArray = new byte[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (byte) (int) stack.removeLast(); + } + yield new WasmInt8Array(arrayType, fixedArray); + } + case WasmType.I16_TYPE -> { + short[] fixedArray = new short[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (short) (int) stack.removeLast(); + } + yield new WasmInt16Array(arrayType, fixedArray); + } + case WasmType.I32_TYPE -> { + int[] fixedArray = new int[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (int) stack.removeLast(); + } + yield new WasmInt32Array(arrayType, fixedArray); + } + case WasmType.I64_TYPE -> { + long[] fixedArray = new long[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (long) stack.removeLast(); + } + yield new WasmInt64Array(arrayType, fixedArray); + } + case WasmType.F32_TYPE -> { + float[] fixedArray = new float[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (float) stack.removeLast(); + } + yield new WasmFloat32Array(arrayType, fixedArray); + } + case WasmType.F64_TYPE -> { + double[] fixedArray = new double[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (double) stack.removeLast(); + } + yield new WasmFloat64Array(arrayType, fixedArray); + } + case WasmType.V128_TYPE -> { + byte[] fixedArray = new byte[length << 4]; + for (int i = length - 1; i >= 0; i--) { + Vector128 vec = (Vector128) stack.removeLast(); + System.arraycopy(vec.getBytes(), 0, fixedArray, i << 4, 4); + } + yield new WasmVec128Array(arrayType, fixedArray); + } + default -> { + Object[] fixedArray = new Object[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = stack.removeLast(); + } + yield new WasmRefArray(arrayType, fixedArray); + } + }; + stack.add(array); + break; + } + case Bytecode.REF_I31: { + stack.add((int) stack.removeLast() & ~(1 << 31)); + break; + } + default: + fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X 0x%02X", opcode, aggregateOpcode); + break; + } + break; + case Bytecode.VECTOR: + int vectorOpcode = rawPeekU8(bytecode, offset); + offset++; + switch (vectorOpcode) { + case Bytecode.VECTOR_V128_CONST: { + Vector128 value = new Vector128(rawPeekI128(bytecode, offset)); + offset += 16; + stack.add(value); + break; + } + default: + fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X 0x%02X", opcode, vectorOpcode); + break; + } + break; default: fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X", opcode); break; @@ -703,9 +887,13 @@ public static Object evalConstantExpression(WasmInstance instance, byte[] byteco } private static List dependenciesOfConstantExpression(WasmInstance instance, byte[] bytecode) { + return dependenciesOfConstantExpression(instance, bytecode, 0, bytecode.length); + } + + private static List dependenciesOfConstantExpression(WasmInstance instance, byte[] bytecode, int start, int end) { List dependencies = new ArrayList<>(); - int offset = 0; - while (offset < bytecode.length) { + int offset = start; + while (offset < end) { int opcode = rawPeekU8(bytecode, offset); offset++; switch (opcode) { @@ -733,9 +921,6 @@ private static List dependenciesOfConstantExpression(WasmInstance instance, case Bytecode.F64_CONST: offset += 8; break; - case Bytecode.VECTOR_V128_CONST: - offset += 16; - break; case Bytecode.REF_FUNC: final int functionIndex = rawPeekI32(bytecode, offset); final WasmFunction function = instance.symbolTable().function(functionIndex); @@ -752,6 +937,41 @@ private static List dependenciesOfConstantExpression(WasmInstance instance, case Bytecode.I64_SUB: case Bytecode.I64_MUL: break; + case Bytecode.AGGREGATE: + int aggregateOpcode = rawPeekU8(bytecode, offset); + offset++; + switch (aggregateOpcode) { + case Bytecode.REF_I31: { + break; + } + case Bytecode.STRUCT_NEW: + case Bytecode.STRUCT_NEW_DEFAULT: + case Bytecode.ARRAY_NEW: + case Bytecode.ARRAY_NEW_DEFAULT: { + offset += 4; + break; + } + case Bytecode.ARRAY_NEW_FIXED: { + offset += 8; + break; + } + default: + fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X 0x%02X", opcode, aggregateOpcode); + break; + } + break; + case Bytecode.VECTOR: + int vectorOpcode = rawPeekU8(bytecode, offset); + offset++; + switch (vectorOpcode) { + case Bytecode.VECTOR_V128_CONST: + offset += 16; + break; + default: + fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X 0x%02X", opcode, vectorOpcode); + break; + } + break; default: fail(Failure.ILLEGAL_OPCODE, "Invalid bytecode instruction for constant expression: 0x%02X", opcode); break; @@ -890,25 +1110,21 @@ private static void addElemItemDependencies(WasmInstance instance, int bytecodeO elementOffset++; final int type = opcode & BytecodeBitEncoding.ELEM_ITEM_TYPE_MASK; final int length = opcode & BytecodeBitEncoding.ELEM_ITEM_LENGTH_MASK; - if ((opcode & BytecodeBitEncoding.ELEM_ITEM_NULL_FLAG) != 0) { - // null constant - continue; - } - final int index; + final int value; switch (length) { case BytecodeBitEncoding.ELEM_ITEM_LENGTH_INLINE: - index = opcode & BytecodeBitEncoding.ELEM_ITEM_INLINE_VALUE; + value = opcode & BytecodeBitEncoding.ELEM_ITEM_INLINE_VALUE; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_U8: - index = BinaryStreamParser.rawPeekU8(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekU8(bytecode, elementOffset); elementOffset++; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_U16: - index = BinaryStreamParser.rawPeekU16(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekU16(bytecode, elementOffset); elementOffset += 2; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_I32: - index = BinaryStreamParser.rawPeekI32(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekI32(bytecode, elementOffset); elementOffset += 4; break; default: @@ -916,13 +1132,17 @@ private static void addElemItemDependencies(WasmInstance instance, int bytecodeO } if (type == BytecodeBitEncoding.ELEM_ITEM_TYPE_FUNCTION_INDEX) { // function index - final WasmFunction function = instance.module().function(index); + final int functionIndex = value; + final WasmFunction function = instance.module().function(functionIndex); if (function.importDescriptor() != null) { dependencies.add(new ImportFunctionSym(instance.name(), function.importDescriptor(), function.index())); } } else { - // global index - dependencies.add(new InitializeGlobalSym(instance.name(), index)); + // bytecode + assert type == BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE; + final int elementBytecodeLength = value; + dependencies.addAll(dependenciesOfConstantExpression(instance, bytecode, elementOffset, elementOffset + elementBytecodeLength)); + elementOffset += elementBytecodeLength; } } } @@ -936,26 +1156,21 @@ private static Object[] extractElemItems(WasmInstance instance, int bytecodeOffs elementOffset++; final int type = opcode & BytecodeBitEncoding.ELEM_ITEM_TYPE_MASK; final int length = opcode & BytecodeBitEncoding.ELEM_ITEM_LENGTH_MASK; - if ((opcode & BytecodeBitEncoding.ELEM_ITEM_NULL_FLAG) != 0) { - // null constant - elemItems[elementIndex] = WasmConstant.NULL; - continue; - } - final int index; + final int value; switch (length) { case BytecodeBitEncoding.ELEM_ITEM_LENGTH_INLINE: - index = opcode & BytecodeBitEncoding.ELEM_ITEM_INLINE_VALUE; + value = opcode & BytecodeBitEncoding.ELEM_ITEM_INLINE_VALUE; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_U8: - index = BinaryStreamParser.rawPeekU8(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekU8(bytecode, elementOffset); elementOffset++; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_U16: - index = BinaryStreamParser.rawPeekU16(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekU16(bytecode, elementOffset); elementOffset += 2; break; case BytecodeBitEncoding.ELEM_ITEM_LENGTH_I32: - index = BinaryStreamParser.rawPeekI32(bytecode, elementOffset); + value = BinaryStreamParser.rawPeekI32(bytecode, elementOffset); elementOffset += 4; break; default: @@ -963,11 +1178,15 @@ private static Object[] extractElemItems(WasmInstance instance, int bytecodeOffs } if (type == BytecodeBitEncoding.ELEM_ITEM_TYPE_FUNCTION_INDEX) { // function index - final WasmFunction function = instance.module().function(index); + final int functionIndex = value; + final WasmFunction function = instance.module().function(functionIndex); elemItems[elementIndex] = instance.functionInstance(function); } else { - assert type == BytecodeBitEncoding.ELEM_ITEM_TYPE_GLOBAL_INDEX; - elemItems[elementIndex] = instance.globals().loadAsReference(instance.module().globalAddress(index)); + // bytecode + assert type == BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE; + final int elementBytecodeLength = value; + elemItems[elementIndex] = Linker.evalConstantExpression(instance, bytecode, elementOffset, elementOffset + elementBytecodeLength); + elementOffset += elementBytecodeLength; } } return elemItems; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/ModuleLimits.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/ModuleLimits.java index 8b32814a25ec..6a53093030ef 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/ModuleLimits.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/ModuleLimits.java @@ -58,45 +58,51 @@ public final class ModuleLimits { private final int moduleSizeLimit; private final int typeCountLimit; + private final int subtypeDepthLimit; private final int functionCountLimit; - private final int tableCountLimit; - private final int multiMemoryCountLimit; private final int importCountLimit; private final int exportCountLimit; private final int globalCountLimit; + private final int tagCountLimit; private final int dataSegmentCountLimit; private final int elementSegmentCountLimit; - private final int functionSizeLimit; + private final int tableCountLimit; + private final int multiMemoryCountLimit; private final int paramCountLimit; private final int multiValueResultCountLimit; + private final int functionSizeLimit; private final int localCountLimit; + private final int structFieldCountLimit; + private final int arrayNewFixedLengthLimit; private final int tableInstanceSizeLimit; private final int memoryInstanceSizeLimit; private final long memory64InstanceSizeLimit; - private final int tagCountLimit; - public ModuleLimits(int moduleSizeLimit, int typeCountLimit, int functionCountLimit, int tableCountLimit, int memoryCountLimit, int importCountLimit, - int exportCountLimit, int globalCountLimit, - int dataSegmentCountLimit, int elementSegmentCountLimit, int functionSizeLimit, int paramCountLimit, int resultCountLimit, int localCountLimit, - int tableInstanceSizeLimit, int memoryInstanceSizeLimit, long memory64InstanceSizeLimit, int tagCountLimit) { + public ModuleLimits(int moduleSizeLimit, int typeCountLimit, int subtypeDepthLimit, int functionCountLimit, int importCountLimit, int exportCountLimit, int globalCountLimit, int tagCountLimit, + int dataSegmentCountLimit, int elementSegmentCountLimit, int tableCountLimit, int memoryCountLimit, + int paramCountLimit, int resultCountLimit, int functionSizeLimit, int localCountLimit, + int structFieldCountLimit, int arrayNewFixedLengthLimit, int tableInstanceSizeLimit, int memoryInstanceSizeLimit, long memory64InstanceSizeLimit) { this.moduleSizeLimit = minUnsigned(moduleSizeLimit, Integer.MAX_VALUE); this.typeCountLimit = minUnsigned(typeCountLimit, WasmType.MAX_TYPE_INDEX); + this.subtypeDepthLimit = minUnsigned(subtypeDepthLimit, Integer.MAX_VALUE); this.functionCountLimit = minUnsigned(functionCountLimit, Integer.MAX_VALUE); - this.tableCountLimit = minUnsigned(tableCountLimit, Integer.MAX_VALUE); - this.multiMemoryCountLimit = minUnsigned(memoryCountLimit, Integer.MAX_VALUE); this.importCountLimit = minUnsigned(importCountLimit, Integer.MAX_VALUE); this.exportCountLimit = minUnsigned(exportCountLimit, Integer.MAX_VALUE); this.globalCountLimit = minUnsigned(globalCountLimit, Integer.MAX_VALUE); + this.tagCountLimit = minUnsigned(tagCountLimit, Integer.MAX_VALUE); this.dataSegmentCountLimit = minUnsigned(dataSegmentCountLimit, Integer.MAX_VALUE); this.elementSegmentCountLimit = minUnsigned(elementSegmentCountLimit, Integer.MAX_VALUE); - this.functionSizeLimit = minUnsigned(functionSizeLimit, Integer.MAX_VALUE); + this.tableCountLimit = minUnsigned(tableCountLimit, Integer.MAX_VALUE); + this.multiMemoryCountLimit = minUnsigned(memoryCountLimit, Integer.MAX_VALUE); this.paramCountLimit = minUnsigned(paramCountLimit, Integer.MAX_VALUE); this.multiValueResultCountLimit = minUnsigned(resultCountLimit, Integer.MAX_VALUE); + this.functionSizeLimit = minUnsigned(functionSizeLimit, Integer.MAX_VALUE); this.localCountLimit = minUnsigned(localCountLimit, Integer.MAX_VALUE); + this.structFieldCountLimit = minUnsigned(structFieldCountLimit, Integer.MAX_VALUE); + this.arrayNewFixedLengthLimit = minUnsigned(arrayNewFixedLengthLimit, Integer.MAX_VALUE); this.tableInstanceSizeLimit = minUnsigned(tableInstanceSizeLimit, MAX_TABLE_INSTANCE_SIZE); this.memoryInstanceSizeLimit = minUnsigned(memoryInstanceSizeLimit, MAX_MEMORY_INSTANCE_SIZE); this.memory64InstanceSizeLimit = minUnsigned(memory64InstanceSizeLimit, MAX_MEMORY_64_INSTANCE_SIZE); - this.tagCountLimit = minUnsigned(tagCountLimit, Integer.MAX_VALUE); } private static int minUnsigned(int a, int b) { @@ -122,10 +128,13 @@ private static long minUnsigned(long a, long b) { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, + Integer.MAX_VALUE, + Integer.MAX_VALUE, + Integer.MAX_VALUE, + Integer.MAX_VALUE, MAX_TABLE_INSTANCE_SIZE, MAX_MEMORY_INSTANCE_SIZE, - MAX_MEMORY_64_INSTANCE_SIZE, - Integer.MAX_VALUE); + MAX_MEMORY_64_INSTANCE_SIZE); public void checkModuleSize(int size) { assertUnsignedIntLessOrEqual(size, moduleSizeLimit, Failure.MODULE_SIZE_LIMIT_EXCEEDED); @@ -135,19 +144,12 @@ public void checkTypeCount(int count) { assertUnsignedIntLessOrEqual(count, typeCountLimit, Failure.TYPE_COUNT_LIMIT_EXCEEDED); } - public void checkFunctionCount(int count) { - assertUnsignedIntLessOrEqual(count, functionCountLimit, Failure.FUNCTION_COUNT_LIMIT_EXCEEDED); + public void checkSubtypeDepth(int depth) { + assertUnsignedIntLessOrEqual(depth, subtypeDepthLimit, Failure.SUB_TYPE_DEPTH_LIMIT_EXCEEDED); } - public void checkTableCount(int count) { - assertUnsignedIntLessOrEqual(count, tableCountLimit, Failure.TABLE_COUNT_LIMIT_EXCEEDED); - } - - public void checkMemoryCount(int count, boolean multiMemory) { - if (!multiMemory) { - assertUnsignedIntLessOrEqual(count, SINGLE_MEMORY_COUNT_LIMIT, Failure.MULTIPLE_MEMORIES); - } - assertUnsignedIntLessOrEqual(count, multiMemoryCountLimit, Failure.MEMORY_COUNT_LIMIT_EXCEEDED); + public void checkFunctionCount(int count) { + assertUnsignedIntLessOrEqual(count, functionCountLimit, Failure.FUNCTION_COUNT_LIMIT_EXCEEDED); } public void checkImportCount(int count) { @@ -162,6 +164,10 @@ public void checkGlobalCount(int count) { assertUnsignedIntLessOrEqual(count, globalCountLimit, Failure.GLOBAL_COUNT_LIMIT_EXCEEDED); } + public void checkTagCount(int count) { + assertUnsignedIntLessOrEqual(count, tagCountLimit, Failure.TAG_COUNT_LIMIT_EXCEEDED); + } + public void checkDataSegmentCount(int count) { assertUnsignedIntLessOrEqual(count, dataSegmentCountLimit, Failure.DATA_SEGMENT_COUNT_LIMIT_EXCEEDED); } @@ -170,8 +176,15 @@ public void checkElementSegmentCount(int count) { assertUnsignedIntLessOrEqual(count, elementSegmentCountLimit, Failure.ELEMENT_SEGMENT_COUNT_LIMIT_EXCEEDED); } - public void checkFunctionSize(int size) { - assertUnsignedIntLessOrEqual(size, functionSizeLimit, Failure.FUNCTION_SIZE_LIMIT_EXCEEDED); + public void checkTableCount(int count) { + assertUnsignedIntLessOrEqual(count, tableCountLimit, Failure.TABLE_COUNT_LIMIT_EXCEEDED); + } + + public void checkMemoryCount(int count, boolean multiMemory) { + if (!multiMemory) { + assertUnsignedIntLessOrEqual(count, SINGLE_MEMORY_COUNT_LIMIT, Failure.MULTIPLE_MEMORIES); + } + assertUnsignedIntLessOrEqual(count, multiMemoryCountLimit, Failure.MEMORY_COUNT_LIMIT_EXCEEDED); } public void checkParamCount(int count) { @@ -185,10 +198,22 @@ public void checkResultCount(int count, boolean multiValue) { assertUnsignedIntLessOrEqual(count, multiValueResultCountLimit, Failure.RESULT_COUNT_LIMIT_EXCEEDED); } + public void checkFunctionSize(int size) { + assertUnsignedIntLessOrEqual(size, functionSizeLimit, Failure.FUNCTION_SIZE_LIMIT_EXCEEDED); + } + public void checkLocalCount(int count) { assertUnsignedIntLessOrEqual(count, localCountLimit, Failure.TOO_MANY_LOCALS); } + public void checkStructFieldCount(int count) { + assertUnsignedIntLessOrEqual(count, structFieldCountLimit, Failure.TOO_MANY_STRUCT_FIELDS); + } + + public void checkArrayNewFixedLength(int length) { + assertUnsignedIntLessOrEqual(length, arrayNewFixedLengthLimit, Failure.ARRAY_NEW_FIXED_LIMIT_EXCEEDED); + } + public void checkTableInstanceSize(int size) { assertUnsignedIntLessOrEqual(size, tableInstanceSizeLimit, Failure.TABLE_INSTANCE_SIZE_LIMIT_EXCEEDED); } @@ -201,10 +226,6 @@ public void checkMemoryInstanceSize(long size, boolean indexType64) { } } - public void checkTagCount(int count) { - assertUnsignedIntLessOrEqual(count, tagCountLimit, Failure.TAG_COUNT_LIMIT_EXCEEDED); - } - public int tableInstanceSizeLimit() { return tableInstanceSizeLimit; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/SymbolTable.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/SymbolTable.java index b644681d240c..534c866c551a 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/SymbolTable.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/SymbolTable.java @@ -50,22 +50,41 @@ import java.util.Arrays; import java.util.List; -import com.oracle.truffle.api.nodes.ExplodeLoop; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.EconomicSet; import org.graalvm.collections.MapCursor; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.constants.ImportIdentifier; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; -import org.graalvm.wasm.exception.WasmRuntimeException; import org.graalvm.wasm.memory.WasmMemory; import org.graalvm.wasm.memory.WasmMemoryFactory; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.staticobject.DefaultStaticProperty; +import com.oracle.truffle.api.staticobject.StaticProperty; +import com.oracle.truffle.api.staticobject.StaticShape; +import org.graalvm.wasm.struct.WasmStruct; +import org.graalvm.wasm.struct.WasmStructAccess; +import org.graalvm.wasm.struct.WasmStructFactory; +import org.graalvm.wasm.types.AbstractHeapType; +import org.graalvm.wasm.types.ArrayType; +import org.graalvm.wasm.types.CompositeType; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.FieldType; +import org.graalvm.wasm.types.FunctionType; +import org.graalvm.wasm.types.HeapType; +import org.graalvm.wasm.types.NumberType; +import org.graalvm.wasm.types.PackedType; +import org.graalvm.wasm.types.RecursiveTypes; +import org.graalvm.wasm.types.ReferenceType; +import org.graalvm.wasm.types.StorageType; +import org.graalvm.wasm.types.StructType; +import org.graalvm.wasm.types.SubType; +import org.graalvm.wasm.types.ValueType; +import org.graalvm.wasm.types.VectorType; /** * Contains the symbol information of a module. @@ -89,415 +108,13 @@ public abstract class SymbolTable { public static final int NO_EQUIVALENCE_CLASS = 0; public static final int FIRST_EQUIVALENCE_CLASS = NO_EQUIVALENCE_CLASS + 1; - /** - * Represents a WebAssembly value type in its closed form, with all type indices replaced with - * their definitions. You can query the subtyping relation on types using the predicates - * {@link #isSupertypeOf(ClosedValueType)} and {@link #isSubtypeOf(ClosedValueType)}, both of - * which are written to be PE-friendly provided the receiver type is a PE constant. - *

- * If you need to check whether two types are equivalent, instead of checking - * {@code A.isSupertypeOf(B) && A.isSubtypeOf(B)}, you can use {@code A.equals(B)}, since, in - * the WebAssembly type system, type equivalence corresponds to structural equality. - *

- */ - public abstract static sealed class ClosedValueType { - // This is a workaround until we can use pattern matching in JDK 21+. - public enum Kind { - Number, - Vector, - Reference - } - - public abstract boolean isSupertypeOf(ClosedValueType valueSubType); - - public abstract boolean isSubtypeOf(ClosedValueType valueSuperType); - - public abstract boolean matchesValue(Object value); - - public abstract Kind kind(); - } - - public abstract static sealed class ClosedHeapType { - // This is a workaround until we can use pattern matching in JDK 21+. - public enum Kind { - Abstract, - Function - } - - public abstract boolean isSupertypeOf(ClosedHeapType heapSubType); - - public abstract boolean isSubtypeOf(ClosedHeapType heapSuperType); - - public abstract boolean matchesValue(Object value); - - public abstract Kind kind(); - } - - public static final class NumberType extends ClosedValueType { - public static final NumberType I32 = new NumberType(WasmType.I32_TYPE); - public static final NumberType I64 = new NumberType(WasmType.I64_TYPE); - public static final NumberType F32 = new NumberType(WasmType.F32_TYPE); - public static final NumberType F64 = new NumberType(WasmType.F64_TYPE); - - private final int value; - - private NumberType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - @Override - public boolean isSupertypeOf(ClosedValueType valueSubType) { - return valueSubType == this; - } - - @Override - public boolean isSubtypeOf(ClosedValueType valueSuperType) { - return valueSuperType == this; - } - - @Override - public boolean matchesValue(Object val) { - return switch (value()) { - case WasmType.I32_TYPE -> val instanceof Integer; - case WasmType.I64_TYPE -> val instanceof Long; - case WasmType.F32_TYPE -> val instanceof Float; - case WasmType.F64_TYPE -> val instanceof Double; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } - - @Override - public Kind kind() { - return Kind.Number; - } - - @Override - public boolean equals(Object that) { - return this == that; - } - - @Override - public int hashCode() { - return value; - } - - @Override - public String toString() { - return switch (value()) { - case WasmType.I32_TYPE -> "i32"; - case WasmType.I64_TYPE -> "i64"; - case WasmType.F32_TYPE -> "f32"; - case WasmType.F64_TYPE -> "f64"; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } - } - - public static final class VectorType extends ClosedValueType { - public static final VectorType V128 = new VectorType(WasmType.V128_TYPE); - - private final int value; - - private VectorType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - @Override - public boolean isSupertypeOf(ClosedValueType valueSubType) { - return valueSubType == V128; - } - - @Override - public boolean isSubtypeOf(ClosedValueType valueSuperType) { - return valueSuperType == V128; - } - - @Override - public boolean matchesValue(Object val) { - return val instanceof Vector128; - } - - @Override - public Kind kind() { - return Kind.Vector; - } - - @Override - public boolean equals(Object that) { - return this == that; - } - - @Override - public int hashCode() { - return value; - } - - @Override - public String toString() { - return "v128"; - } - } - - public static final class ClosedReferenceType extends ClosedValueType { - public static final ClosedReferenceType FUNCREF = new ClosedReferenceType(true, AbstractHeapType.FUNC); - public static final ClosedReferenceType NONNULL_FUNCREF = new ClosedReferenceType(false, AbstractHeapType.FUNC); - public static final ClosedReferenceType EXTERNREF = new ClosedReferenceType(true, AbstractHeapType.EXTERN); - public static final ClosedReferenceType NONNULL_EXTERNREF = new ClosedReferenceType(false, AbstractHeapType.EXTERN); - public static final ClosedReferenceType EXNREF = new ClosedReferenceType(true, AbstractHeapType.EXN); - public static final ClosedReferenceType NONNULL_EXNREF = new ClosedReferenceType(false, AbstractHeapType.EXN); - - private final boolean nullable; - private final ClosedHeapType closedHeapType; - - public ClosedReferenceType(boolean nullable, ClosedHeapType closedHeapType) { - this.nullable = nullable; - this.closedHeapType = closedHeapType; - } - - public boolean nullable() { - return nullable; - } - - public ClosedHeapType heapType() { - return closedHeapType; - } - - @Override - public boolean isSupertypeOf(ClosedValueType valueSubType) { - return valueSubType instanceof ClosedReferenceType referenceSubType && (!referenceSubType.nullable || this.nullable) && - this.closedHeapType.isSupertypeOf(referenceSubType.closedHeapType); - } - - @Override - public boolean isSubtypeOf(ClosedValueType valueSuperType) { - return valueSuperType instanceof ClosedReferenceType referencedSuperType && (!this.nullable || referencedSuperType.nullable) && - this.closedHeapType.isSubtypeOf(referencedSuperType.closedHeapType); - } - - @Override - public boolean matchesValue(Object value) { - return nullable() && value == WasmConstant.NULL || heapType().matchesValue(value); - } - - @Override - public Kind kind() { - return Kind.Reference; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof ClosedReferenceType that && this.nullable == that.nullable && this.closedHeapType.equals(that.closedHeapType); - } - - @Override - public int hashCode() { - return Boolean.hashCode(nullable) ^ closedHeapType.hashCode(); - } - - @Override - public String toString() { - CompilerAsserts.neverPartOfCompilation(); - if (this == FUNCREF) { - return "funcref"; - } else if (this == EXTERNREF) { - return "externref"; - } else if (this == EXNREF) { - return "exnref"; - } else { - StringBuilder buf = new StringBuilder(); - buf.append("(ref "); - if (nullable) { - buf.append("null "); - } - buf.append(closedHeapType.toString()); - buf.append(")"); - return buf.toString(); - } - } - } - - public static final class AbstractHeapType extends ClosedHeapType { - public static final AbstractHeapType FUNC = new AbstractHeapType(WasmType.FUNC_HEAPTYPE); - public static final AbstractHeapType EXTERN = new AbstractHeapType(WasmType.EXTERN_HEAPTYPE); - public static final AbstractHeapType EXN = new AbstractHeapType(WasmType.EXN_HEAPTYPE); - - private final int value; - - private AbstractHeapType(int value) { - this.value = value; - } - - public int value() { - return value; - } - - @Override - public boolean isSupertypeOf(ClosedHeapType heapSubType) { - return switch (this.value) { - case WasmType.FUNC_HEAPTYPE -> heapSubType == FUNC || heapSubType instanceof ClosedFunctionType; - case WasmType.EXTERN_HEAPTYPE -> heapSubType == EXTERN; - case WasmType.EXN_HEAPTYPE -> heapSubType == EXN; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } - - @Override - public boolean isSubtypeOf(ClosedHeapType heapSuperType) { - return heapSuperType == this; - } - - @Override - public boolean matchesValue(Object val) { - return switch (this.value) { - case WasmType.FUNC_HEAPTYPE -> val instanceof WasmFunctionInstance; - case WasmType.EXTERN_HEAPTYPE -> true; - case WasmType.EXN_HEAPTYPE -> val instanceof WasmRuntimeException; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } - - @Override - public Kind kind() { - return Kind.Abstract; - } - - @Override - public boolean equals(Object that) { - return this == that; - } - - @Override - public int hashCode() { - return value; - } - - @Override - public String toString() { - return switch (this.value) { - case WasmType.FUNC_HEAPTYPE -> "func"; - case WasmType.EXTERN_HEAPTYPE -> "extern"; - case WasmType.EXN_HEAPTYPE -> "exn"; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } - } - - public static final class ClosedFunctionType extends ClosedHeapType { - @CompilationFinal(dimensions = 1) private final ClosedValueType[] paramTypes; - @CompilationFinal(dimensions = 1) private final ClosedValueType[] resultTypes; - - public ClosedFunctionType(ClosedValueType[] paramTypes, ClosedValueType[] resultTypes) { - this.paramTypes = paramTypes; - this.resultTypes = resultTypes; - } - - public ClosedValueType[] paramTypes() { - return paramTypes; - } - - public ClosedValueType[] resultTypes() { - return resultTypes; - } - - @Override - @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_UNROLL) - public boolean isSupertypeOf(ClosedHeapType heapSubType) { - if (!(heapSubType instanceof ClosedFunctionType functionSubType)) { - return false; - } - if (this.paramTypes.length != functionSubType.paramTypes.length) { - return false; - } - for (int i = 0; i < this.paramTypes.length; i++) { - CompilerAsserts.partialEvaluationConstant(this.paramTypes[i]); - if (!this.paramTypes[i].isSubtypeOf(functionSubType.paramTypes[i])) { - return false; - } - } - if (this.resultTypes.length != functionSubType.resultTypes.length) { - return false; - } - for (int i = 0; i < this.resultTypes.length; i++) { - CompilerAsserts.partialEvaluationConstant(this.resultTypes[i]); - if (!this.resultTypes[i].isSupertypeOf(functionSubType.resultTypes[i])) { - return false; - } - } - return true; - } - - @Override - @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_UNROLL) - public boolean isSubtypeOf(ClosedHeapType heapSuperType) { - if (heapSuperType == AbstractHeapType.FUNC) { - return true; - } - if (!(heapSuperType instanceof ClosedFunctionType functionSuperType)) { - return false; - } - if (this.paramTypes.length != functionSuperType.paramTypes.length) { - return false; - } - for (int i = 0; i < this.paramTypes.length; i++) { - CompilerAsserts.partialEvaluationConstant(this.paramTypes[i]); - if (!this.paramTypes[i].isSupertypeOf(functionSuperType.paramTypes[i])) { - return false; - } - } - if (this.resultTypes.length != functionSuperType.resultTypes.length) { - return false; - } - for (int i = 0; i < this.resultTypes.length; i++) { - CompilerAsserts.partialEvaluationConstant(this.resultTypes[i]); - if (!this.resultTypes[i].isSubtypeOf(functionSuperType.resultTypes[i])) { - return false; - } - } - return true; - } - - @Override - public boolean matchesValue(Object value) { - return value instanceof WasmFunctionInstance instance && isSupertypeOf(instance.function().closedType()); - } + private static final int FINAL_MASK = 1 << 31; + private static final int SUPERTYPE_MASK = FINAL_MASK - 1; + private static final int NO_SUPERTYPE = SUPERTYPE_MASK; - @Override - public Kind kind() { - return Kind.Function; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof ClosedFunctionType that && Arrays.equals(this.paramTypes, that.paramTypes) && Arrays.equals(this.resultTypes, that.resultTypes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(paramTypes) ^ Arrays.hashCode(resultTypes); - } - - @Override - public String toString() { - CompilerAsserts.neverPartOfCompilation(); - String[] paramNames = new String[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) { - paramNames[i] = paramTypes[i].toString(); - } - String[] resultNames = new String[resultTypes.length]; - for (int i = 0; i < resultTypes.length; i++) { - resultNames[i] = resultTypes[i].toString(); - } - return "(" + String.join(" ", paramNames) + ")->(" + String.join(" ", resultNames) + ")"; - } - } + public static final byte ARRAY_KIND = 1; + public static final byte STRUCT_KIND = 2; + public static final byte FUNCTION_KIND = 3; /** * @param initialSize Lower bound on table size. @@ -534,21 +151,41 @@ public record TagInfo(byte attribute, int typeIndex) { } /** - * Encodes the parameter and result types of each function type. + * Encodes the structure of each defined type. + *

+ * Given a defined type index, the {@link #typeOffsets} array indicates where the encoding for + * that defined type begins in this array. + *

+ * For an array type starting at index i, the encoding is the following + *

+ * + * i i+1 + * +--------------+------------+ + * | element type | mutability | + * +--------------+------------+ + * + *

+ * For a struct type starting at index i, the encoding is the following + *

+ * + * i i+1 i+2 i+1+2*(nf-1) i+1+2*(nf-1)+1 + * +-----+---------+--------------+-----+-------------+---------------+ + * | nf | type 1 | mutability 1 | ... | type nf | mutability nf | + * +-----+---------+--------------+-----+-------------+---------------+ + * *

- * Given a function type index, the {@link #typeOffsets} array indicates where the encoding for - * that function type begins in this array. + * where `nf` is the number of fields. *

* For a function type starting at index i, the encoding is the following *

* * i i+1 i+2+0 i+2+na-1 i+2+na+0 i+2+na+nr-1 * +-----+-----+-------+-----+--------+----------+-----+-----------+ - * | na | nr | par 1 | ... | par na | result 1 | ... | result nr | + * | np | nr | par 1 | ... | par np | result 1 | ... | result nr | * +-----+-----+-------+-----+--------+----------+-----+-----------+ * *

- * where `na` is the number of parameters, and `nr` is the number of result values. + * where `np` is the number of parameters, and `nr` is the number of result values. *

* This array is monotonically populated from left to right during parsing. Any code that uses * this array should only access the locations in the array that have already been populated. @@ -563,22 +200,35 @@ public record TagInfo(byte attribute, int typeIndex) { */ @CompilationFinal(dimensions = 1) private int[] typeOffsets; + /** + * Stores for each typed defined in {@link #typeData} the kind of the type (array, struct or + * function). The values stored are always one of {@link #ARRAY_KIND}, {@link #STRUCT_KIND} or + * {@link #FUNCTION_KIND}. + */ + @CompilationFinal(dimensions = 1) private byte[] typeKinds; + /** * Stores the closed forms of all the types defined in this module. Closed forms replace type * indices with the definitions of the referenced types, resulting in a tree-like data * structure. */ - @CompilationFinal(dimensions = 1) private ClosedHeapType[] closedTypes; + @CompilationFinal(dimensions = 1) private DefinedType[] closedTypes; /** - * Stores the type equivalence class. + * Stores metadata relevant for runtime type checks. For every defined type in + * {@link #typeData}, there is one {@code int} value with the following bit pattern: *

- * Since multiple types have the same shape, each type is mapped to an equivalence class, so - * that two types can be quickly compared. - *

- * The equivalence classes are computed globally for all the modules, during linking. + * + * 31 30 . . . . . . 0 + * +-------+-----------------+ + * | final | supertype index | + * +-------+-----------------+ + * */ - @CompilationFinal(dimensions = 1) private int[] typeEquivalenceClasses; + @CompilationFinal(dimensions = 1) private int[] superTypes; + private int[] superTypeDepth; + + @CompilationFinal(dimensions = 1) private WasmStructAccess[] structAccesses; @CompilationFinal private int typeDataSize; @CompilationFinal private int typeCount; @@ -786,8 +436,12 @@ public record TagInfo(byte attribute, int typeIndex) { CompilerAsserts.neverPartOfCompilation(); this.typeData = new int[INITIAL_DATA_SIZE]; this.typeOffsets = new int[INITIAL_TYPE_SIZE]; - this.closedTypes = new ClosedHeapType[INITIAL_TYPE_SIZE]; - this.typeEquivalenceClasses = new int[INITIAL_TYPE_SIZE]; + this.typeKinds = new byte[INITIAL_TYPE_SIZE]; + this.closedTypes = new DefinedType[INITIAL_TYPE_SIZE]; + this.superTypes = new int[INITIAL_TYPE_SIZE]; + Arrays.fill(superTypes, NO_SUPERTYPE); + this.superTypeDepth = new int[INITIAL_TYPE_SIZE]; + this.structAccesses = new WasmStructAccess[INITIAL_TYPE_SIZE]; this.typeDataSize = 0; this.typeCount = 0; this.importedSymbols = new ArrayList<>(); @@ -846,7 +500,7 @@ public void checkFunctionIndex(int funcIndex) { /** * Ensure that the {@link #typeData} array has enough space to store {@code index}. If there is - * no enough space, then a reallocation of the array takes place, doubling its capacity. + * not enough space, then a reallocation of the array takes place, doubling its capacity. *

* No synchronisation is required for this method, as it is only called during parsing, which is * carried out by a single thread. @@ -859,27 +513,99 @@ private void ensureTypeDataCapacity(int index) { } /** - * Ensure that the {@link #typeOffsets} and {@link #typeEquivalenceClasses} arrays have enough - * space to store the data for the type at {@code index}. If there is not enough space, then a - * reallocation of the array takes place, doubling its capacity. + * Ensure that the {@link #typeOffsets}, {@link #closedTypes} and {@link #superTypes} arrays + * have enough space to store the data for the type at {@code index}. If there is not enough + * space, then a reallocation of the array takes place, doubling its capacity. *

* No synchronisation is required for this method, as it is only called during parsing, which is * carried out by a single thread. */ private void ensureTypeCapacity(int index) { + int oldLength = typeOffsets.length; if (typeOffsets.length <= index) { - int newLength = Math.max(Integer.highestOneBit(index) << 1, 2 * typeOffsets.length); + int newLength = Math.max(Integer.highestOneBit(index) << 1, 2 * oldLength); typeOffsets = Arrays.copyOf(typeOffsets, newLength); + typeKinds = Arrays.copyOf(typeKinds, newLength); closedTypes = Arrays.copyOf(closedTypes, newLength); - typeEquivalenceClasses = Arrays.copyOf(typeEquivalenceClasses, newLength); + superTypes = Arrays.copyOf(superTypes, newLength); + Arrays.fill(superTypes, oldLength, newLength, NO_SUPERTYPE); + superTypeDepth = Arrays.copyOf(superTypeDepth, newLength); + structAccesses = Arrays.copyOf(structAccesses, newLength); } } - int allocateFunctionType(int paramCount, int resultCount, boolean isMultiValue) { + void declareRecursiveTypeGroup(int subTypeCount) { + checkNotParsed(); + ensureTypeCapacity(typeCount + subTypeCount - 1); + typeCount += subTypeCount; + } + + void registerFinalType(int typeIdx, boolean finalType) { + checkNotParsed(); + ensureTypeCapacity(typeIdx); + if (finalType) { + superTypes[typeIdx] |= FINAL_MASK; + } else { + superTypes[typeIdx] &= ~FINAL_MASK; + } + } + + public boolean isFinalType(int typeIdx) { + return (superTypes[typeIdx] & FINAL_MASK) != 0; + } + + void registerSuperType(int typeIdx, int superTypeIdx) { + assert (superTypeIdx & SUPERTYPE_MASK) == superTypeIdx; + checkNotParsed(); + ensureTypeCapacity(typeIdx); + superTypes[typeIdx] = superTypes[typeIdx] & ~SUPERTYPE_MASK | superTypeIdx; + superTypeDepth[typeIdx] = superTypeDepth[superTypeIdx] + 1; + } + + public boolean hasSuperType(int typeIdx) { + return (superTypes[typeIdx] & SUPERTYPE_MASK) != NO_SUPERTYPE; + } + + public int superType(int typeIdx) { + assert hasSuperType(typeIdx); + return superTypes[typeIdx] & SUPERTYPE_MASK; + } + + public int superTypeDepth(int typeIdx) { + checkNotParsed(); + return superTypeDepth[typeIdx]; + } + + void registerArrayType(int typeIdx, int elemType, byte mutability) { + checkNotParsed(); + ensureTypeCapacity(typeIdx); + typeOffsets[typeIdx] = typeDataSize; + typeKinds[typeIdx] = ARRAY_KIND; + + int size = 2; + ensureTypeCapacity(typeDataSize + size); + typeData[typeDataSize] = elemType; + typeData[typeDataSize + 1] = mutability; + typeDataSize += size; + } + + void registerStructType(int typeIdx, int fieldCount) { + checkNotParsed(); + ensureTypeCapacity(typeIdx); + typeOffsets[typeIdx] = typeDataSize; + typeKinds[typeIdx] = STRUCT_KIND; + + int size = 1 + 2 * fieldCount; + ensureTypeCapacity(typeDataSize + size); + typeData[typeDataSize] = fieldCount; + typeDataSize += size; + } + + void registerFunctionType(int typeIdx, int paramCount, int resultCount, boolean isMultiValue) { checkNotParsed(); - ensureTypeCapacity(typeCount); - int typeIdx = typeCount++; + ensureTypeCapacity(typeIdx); typeOffsets[typeIdx] = typeDataSize; + typeKinds[typeIdx] = FUNCTION_KIND; if (!isMultiValue && resultCount != 0 && resultCount != 1) { throw WasmException.create(Failure.INVALID_RESULT_ARITY, "A function can return at most one result."); @@ -890,22 +616,60 @@ int allocateFunctionType(int paramCount, int resultCount, boolean isMultiValue) typeData[typeDataSize + 0] = paramCount; typeData[typeDataSize + 1] = resultCount; typeDataSize += size; - return typeIdx; } - public int allocateFunctionType(int[] paramTypes, int[] resultTypes, boolean isMultiValue) { + public int allocateFunctionType(int[] paramTypes, int[] resultTypes, boolean isMultiValue, WasmLanguage language) { checkNotParsed(); - final int typeIdx = allocateFunctionType(paramTypes.length, resultTypes.length, isMultiValue); + final int typeIdx = typeCount; + declareRecursiveTypeGroup(1); + registerFinalType(typeIdx, true); + registerFunctionType(typeIdx, paramTypes.length, resultTypes.length, isMultiValue); for (int i = 0; i < paramTypes.length; i++) { registerFunctionTypeParameterType(typeIdx, i, paramTypes[i]); } for (int i = 0; i < resultTypes.length; i++) { registerFunctionTypeResultType(typeIdx, i, resultTypes[i]); } - finishFunctionType(typeIdx); + finishRecursiveTypeGroup(typeIdx, language); return typeIdx; } + ArrayType finishArrayType(int arrayTypeIdx, int recursiveTypeGroupStart) { + StorageType storageType = closedStorageTypeOf(arrayTypeElemType(arrayTypeIdx), recursiveTypeGroupStart); + byte mutability = arrayTypeMutability(arrayTypeIdx); + FieldType fieldType = new FieldType(storageType, mutability); + return new ArrayType(fieldType); + } + + StructType finishStructType(int structTypeIdx, int recursiveTypeGroupStart, WasmLanguage language) { + StaticShape.Builder shapeBuilder = StaticShape.newBuilder(language); + FieldType[] fieldTypes = new FieldType[structTypeFieldCount(structTypeIdx)]; + StaticProperty[] properties = new StaticProperty[structTypeFieldCount(structTypeIdx)]; + for (int i = 0; i < fieldTypes.length; i++) { + StorageType storageType = closedStorageTypeOf(structTypeFieldTypeAt(structTypeIdx, i), recursiveTypeGroupStart); + byte mutability = structTypeFieldMutabilityAt(structTypeIdx, i); + fieldTypes[i] = new FieldType(storageType, mutability); + properties[i] = new DefaultStaticProperty(Integer.toString(i)); + shapeBuilder.property(properties[i], fieldTypes[i].javaClass(), mutability == Mutability.CONSTANT); + } + StaticShape shape; + if (hasSuperType(structTypeIdx) && isStructType(superType(structTypeIdx))) { + WasmStructAccess superTypeAccess = structTypeAccess(superType(structTypeIdx)); + shape = shapeBuilder.build(superTypeAccess.shape()); + } else { + shape = shapeBuilder.build(WasmStruct.class, WasmStructFactory.class); + } + structAccesses[structTypeIdx] = new WasmStructAccess(shape, properties); + return new StructType(fieldTypes); + } + + void registerStructTypeField(int structTypeIdx, int fieldIdx, int fieldType, byte fieldMutability) { + checkNotParsed(); + int idx = typeOffsets[structTypeIdx] + 1 + 2 * fieldIdx; + typeData[idx] = fieldType; + typeData[idx + 1] = fieldMutability; + } + void registerFunctionTypeParameterType(int funcTypeIdx, int paramIdx, int type) { checkNotParsed(); int idx = 2 + typeOffsets[funcTypeIdx] + paramIdx; @@ -918,28 +682,53 @@ void registerFunctionTypeResultType(int funcTypeIdx, int resultIdx, int type) { typeData[idx] = type; } - void finishFunctionType(int funcTypeIdx) { - ClosedValueType[] paramTypes = new ClosedValueType[functionTypeParamCount(funcTypeIdx)]; + FunctionType finishFunctionType(int funcTypeIdx, int recursiveTypeGroupStart) { + ValueType[] paramTypes = new ValueType[functionTypeParamCount(funcTypeIdx)]; for (int i = 0; i < paramTypes.length; i++) { - paramTypes[i] = closedTypeOf(functionTypeParamTypeAt(funcTypeIdx, i)); + paramTypes[i] = closedTypeOf(functionTypeParamTypeAt(funcTypeIdx, i), recursiveTypeGroupStart); } - ClosedValueType[] resultTypes = new ClosedValueType[functionTypeResultCount(funcTypeIdx)]; + ValueType[] resultTypes = new ValueType[functionTypeResultCount(funcTypeIdx)]; for (int i = 0; i < resultTypes.length; i++) { - resultTypes[i] = closedTypeOf(functionTypeResultTypeAt(funcTypeIdx, i)); + resultTypes[i] = closedTypeOf(functionTypeResultTypeAt(funcTypeIdx, i), recursiveTypeGroupStart); } - closedTypes[funcTypeIdx] = new ClosedFunctionType(paramTypes, resultTypes); + return new FunctionType(paramTypes, resultTypes); } - public int equivalenceClass(int typeIndex) { - return typeEquivalenceClasses[typeIndex]; - } - - void setEquivalenceClass(int index, int eqClass) { - checkNotParsed(); - if (typeEquivalenceClasses[index] != NO_EQUIVALENCE_CLASS) { - throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Type at index " + index + " already has an equivalence class."); + void finishRecursiveTypeGroup(int recursiveTypeGroupStart, WasmLanguage language) { + SubType[] subTypes = new SubType[typeCount - recursiveTypeGroupStart]; + for (int typeIndex = recursiveTypeGroupStart; typeIndex < typeCount; typeIndex++) { + CompositeType compositeType = switch (typeKind(typeIndex)) { + case ARRAY_KIND -> finishArrayType(typeIndex, recursiveTypeGroupStart); + case STRUCT_KIND -> finishStructType(typeIndex, recursiveTypeGroupStart, language); + case FUNCTION_KIND -> finishFunctionType(typeIndex, recursiveTypeGroupStart); + default -> throw CompilerDirectives.shouldNotReachHere(); + }; + DefinedType superType; + if (hasSuperType(typeIndex)) { + int superTypeIndex = superType(typeIndex); + if (superTypeIndex >= recursiveTypeGroupStart) { + superType = DefinedType.makeRecursiveReference(superTypeIndex - recursiveTypeGroupStart); + } else { + superType = closedTypes[superTypeIndex]; + } + } else { + superType = null; + } + subTypes[typeIndex - recursiveTypeGroupStart] = new SubType(isFinalType(typeIndex), superType, compositeType); + } + RecursiveTypes recursiveTypes = new RecursiveTypes(subTypes); + for (int typeIndex = recursiveTypeGroupStart; typeIndex < typeCount; typeIndex++) { + DefinedType type = DefinedType.makeTopLevelType(recursiveTypes, typeIndex - recursiveTypeGroupStart); + int equivalenceClass = language.equivalenceClassFor(type); + type.setTypeEquivalenceClass(equivalenceClass); + if (isStructType(typeIndex)) { + type.setStructAccess(structTypeAccess(typeIndex)); + } + closedTypes[typeIndex] = type; + } + for (int subTypeIndex = 0; subTypeIndex < subTypes.length; subTypeIndex++) { + subTypes[subTypeIndex].unroll(recursiveTypes); } - typeEquivalenceClasses[index] = eqClass; } private void ensureFunctionsCapacity(int index) { @@ -1001,11 +790,13 @@ public WasmFunction function(String exportName) { } public int functionTypeParamCount(int typeIndex) { + assert isFunctionType(typeIndex); int typeOffset = typeOffsets[typeIndex]; return typeData[typeOffset + 0]; } public int functionTypeResultCount(int typeIndex) { + assert isFunctionType(typeIndex); int typeOffset = typeOffsets[typeIndex]; return typeData[typeOffset + 1]; } @@ -1019,18 +810,76 @@ public WasmFunction startFunction() { protected abstract WasmModule module(); + public byte typeKind(int typeIndex) { + return typeKinds[typeIndex]; + } + + public boolean isArrayType(int typeIndex) { + return typeKind(typeIndex) == ARRAY_KIND; + } + + public boolean isStructType(int typeIndex) { + return typeKind(typeIndex) == STRUCT_KIND; + } + + public boolean isFunctionType(int typeIndex) { + return typeKind(typeIndex) == FUNCTION_KIND; + } + + public int arrayTypeElemType(int typeIndex) { + assert isArrayType(typeIndex); + int typeOffset = typeOffsets[typeIndex]; + return typeData[typeOffset]; + } + + public byte arrayTypeMutability(int typeIndex) { + assert isArrayType(typeIndex); + int typeOffset = typeOffsets[typeIndex]; + return (byte) typeData[typeOffset + 1]; + } + + public int structTypeFieldCount(int typeIndex) { + assert isStructType(typeIndex); + int typeOffset = typeOffsets[typeIndex]; + return typeData[typeOffset]; + } + + public int structTypeFieldTypeAt(int typeIndex, int fieldIndex) { + assert isStructType(typeIndex); + assert fieldIndex < structTypeFieldCount(typeIndex); + int typeOffset = typeOffsets[typeIndex]; + return typeData[typeOffset + 1 + 2 * fieldIndex]; + } + + public byte structTypeFieldMutabilityAt(int typeIndex, int fieldIndex) { + assert isStructType(typeIndex); + assert fieldIndex < structTypeFieldCount(typeIndex); + int typeOffset = typeOffsets[typeIndex]; + return (byte) typeData[typeOffset + 1 + 2 * fieldIndex + 1]; + } + + public WasmStructAccess structTypeAccess(int typeIndex) { + assert isStructType(typeIndex); + return structAccesses[typeIndex]; + } + public int functionTypeParamTypeAt(int typeIndex, int paramIndex) { + assert isFunctionType(typeIndex); + assert paramIndex < functionTypeParamCount(typeIndex); int typeOffset = typeOffsets[typeIndex]; return typeData[typeOffset + 2 + paramIndex]; } public int functionTypeResultTypeAt(int typeIndex, int resultIndex) { + assert isFunctionType(typeIndex); + assert resultIndex < functionTypeResultCount(typeIndex); int typeOffset = typeOffsets[typeIndex]; int paramCount = typeData[typeOffset]; return typeData[typeOffset + 2 + paramCount + resultIndex]; } public int[] functionTypeParamTypesAsArray(int typeIndex) { + assert isFunctionType(typeIndex); int paramCount = functionTypeParamCount(typeIndex); int[] paramTypes = new int[paramCount]; for (int i = 0; i < paramCount; ++i) { @@ -1040,6 +889,7 @@ public int[] functionTypeParamTypesAsArray(int typeIndex) { } public int[] functionTypeResultTypesAsArray(int typeIndex) { + assert isFunctionType(typeIndex); int resultTypeCount = functionTypeResultCount(typeIndex); int[] resultTypes = new int[resultTypeCount]; for (int i = 0; i < resultTypeCount; i++) { @@ -1048,26 +898,16 @@ public int[] functionTypeResultTypesAsArray(int typeIndex) { return resultTypes; } - int typeCount() { + public int typeCount() { return typeCount; } - /** - * Convenience function for calling {@link #closedTypeAt(int)} when the defined type at index - * {@code typeIndex} is known to be a function type. - * - * @see #closedTypeAt(int) - */ - public ClosedFunctionType closedFunctionTypeAt(int typeIndex) { - return (ClosedFunctionType) closedTypeAt(typeIndex); - } - /** * Fetches the closed form of a type defined in this module at index {@code typeIndex}. * * @param typeIndex index of a type defined in this module */ - public ClosedHeapType closedTypeAt(int typeIndex) { + public DefinedType closedTypeAt(int typeIndex) { return closedTypes[typeIndex]; } @@ -1077,13 +917,23 @@ public ClosedHeapType closedTypeAt(int typeIndex) { * * @see #closedTypeOf(int, SymbolTable) */ - public ClosedValueType closedTypeOf(int type) { - return SymbolTable.closedTypeOf(type, this); + public ValueType closedTypeOf(int type) { + return SymbolTable.closedTypeOf(type, this, Integer.MAX_VALUE); + } + + /** + * A convenient way of calling {@link #closedTypeOf(int, SymbolTable, int)} when a + * {@link SymbolTable} is present. + * + * @see #closedTypeOf(int, SymbolTable) + */ + private ValueType closedTypeOf(int type, int recursiveTypeGroupStart) { + return SymbolTable.closedTypeOf(type, this, recursiveTypeGroupStart); } /** * Maps a type encoded as an {@code int} (as per {@link WasmType}) into its closed form, - * represented as a {@link ClosedValueType}. Any type indices in the type are resolved using the + * represented as a {@link ValueType}. Any type indices in the type are resolved using the * provided symbol table. *

* It is legal to call this function with a null {@code symbolTable}. This is used in cases @@ -1094,7 +944,18 @@ public ClosedValueType closedTypeOf(int type) { * @param type the {@code int}-encoded Wasm type to be expanded * @param symbolTable used for lookup of type definitions when expanding type indices */ - public static ClosedValueType closedTypeOf(int type, SymbolTable symbolTable) { + public static ValueType closedTypeOf(int type, SymbolTable symbolTable) { + return closedTypeOf(type, symbolTable, Integer.MAX_VALUE); + } + + /** + * This overload of {@link #closedTypeOf(int, SymbolTable)} can detect recursive references and + * emit specially marked {@link DefinedType}s that are later unrolled. + * + * @param recursiveTypeGroupStart the type index of the first type of the current group of + * mutually recursive types (this lets us detect recursive references) + */ + private static ValueType closedTypeOf(int type, SymbolTable symbolTable, int recursiveTypeGroupStart) { return switch (type) { case WasmType.I32_TYPE -> NumberType.I32; case WasmType.I64_TYPE -> NumberType.I64; @@ -1104,17 +965,87 @@ public static ClosedValueType closedTypeOf(int type, SymbolTable symbolTable) { default -> { assert WasmType.isReferenceType(type); boolean nullable = WasmType.isNullable(type); - yield switch (WasmType.getAbstractHeapType(type)) { - case WasmType.FUNC_HEAPTYPE -> nullable ? ClosedReferenceType.FUNCREF : ClosedReferenceType.NONNULL_FUNCREF; - case WasmType.EXTERN_HEAPTYPE -> nullable ? ClosedReferenceType.EXTERNREF : ClosedReferenceType.NONNULL_EXTERNREF; - case WasmType.EXN_HEAPTYPE -> nullable ? ClosedReferenceType.EXNREF : ClosedReferenceType.NONNULL_EXNREF; - default -> { - assert WasmType.isConcreteReferenceType(type); - assert symbolTable != null; - int typeIndex = WasmType.getTypeIndex(type); - ClosedHeapType heapType = symbolTable.closedTypeAt(typeIndex); - yield new ClosedReferenceType(nullable, heapType); - } + int heapType = WasmType.getHeapType(type); + yield new ReferenceType(nullable, closedHeapTypeOf(heapType, symbolTable, recursiveTypeGroupStart)); + } + }; + } + + /** + * Like {@link #closedTypeOf(int)}, but for mapping heap types (both abstract and concrete) to + * {@link HeapType} objects. + */ + public HeapType closedHeapTypeOf(int type) { + return closedHeapTypeOf(type, this, Integer.MAX_VALUE); + } + + /** + * Like {@link #closedTypeOf(int, SymbolTable, int)}, but for mapping heap types (both abstract + * and concrete) to {@link HeapType} objects. + */ + private static HeapType closedHeapTypeOf(int heapType, SymbolTable symbolTable, int recursiveTypeGroupStart) { + return switch (heapType) { + case WasmType.NOEXN_HEAPTYPE -> AbstractHeapType.NOEXN; + case WasmType.NOFUNC_HEAPTYPE -> AbstractHeapType.NOFUNC; + case WasmType.NOEXTERN_HEAPTYPE -> AbstractHeapType.NOEXTERN; + case WasmType.NONE_HEAPTYPE -> AbstractHeapType.NONE; + case WasmType.FUNC_HEAPTYPE -> AbstractHeapType.FUNC; + case WasmType.EXTERN_HEAPTYPE -> AbstractHeapType.EXTERN; + case WasmType.ANY_HEAPTYPE -> AbstractHeapType.ANY; + case WasmType.EQ_HEAPTYPE -> AbstractHeapType.EQ; + case WasmType.I31_HEAPTYPE -> AbstractHeapType.I31; + case WasmType.STRUCT_HEAPTYPE -> AbstractHeapType.STRUCT; + case WasmType.ARRAY_HEAPTYPE -> AbstractHeapType.ARRAY; + case WasmType.EXN_HEAPTYPE -> AbstractHeapType.EXN; + default -> { + assert WasmType.isConcreteReferenceType(heapType); + assert symbolTable != null; + if (heapType >= recursiveTypeGroupStart) { + yield DefinedType.makeRecursiveReference(heapType - recursiveTypeGroupStart); + } else { + yield symbolTable.closedTypeAt(heapType); + } + } + }; + } + + /** + * A version of {@link #closedTypeOf(int)} that also handles storage types + * ({@link WasmType#I8_TYPE} and {@link WasmType#I16_TYPE}). + */ + public StorageType closedStorageTypeOf(int type) { + return closedStorageTypeOf(type, Integer.MAX_VALUE); + } + + /** + * A version of {@link #closedTypeOf(int, int)} that also handles storage types + * ({@link WasmType#I8_TYPE} and {@link WasmType#I16_TYPE}). + */ + private StorageType closedStorageTypeOf(int type, int recursiveTypeGroupStart) { + return switch (type) { + case WasmType.I8_TYPE -> PackedType.I8; + case WasmType.I16_TYPE -> PackedType.I16; + default -> closedTypeOf(type, recursiveTypeGroupStart); + }; + } + + /** + * Returns the most general abstract heap type that is a supertype of the input heap type. + */ + public int topHeapTypeOf(int heapType) { + return switch (heapType) { + case WasmType.BOT -> WasmType.TOP; + case WasmType.NOEXN_HEAPTYPE, WasmType.EXN_HEAPTYPE -> WasmType.EXN_HEAPTYPE; + case WasmType.NOFUNC_HEAPTYPE, WasmType.FUNC_HEAPTYPE -> WasmType.FUNC_HEAPTYPE; + case WasmType.NOEXTERN_HEAPTYPE, WasmType.EXTERN_HEAPTYPE -> WasmType.EXTERN_HEAPTYPE; + case WasmType.NONE_HEAPTYPE, WasmType.ANY_HEAPTYPE, WasmType.EQ_HEAPTYPE, WasmType.I31_HEAPTYPE, WasmType.STRUCT_HEAPTYPE, WasmType.ARRAY_HEAPTYPE -> WasmType.ANY_HEAPTYPE; + default -> { + assert WasmType.isConcreteReferenceType(heapType); + yield switch (typeKind(heapType)) { + case ARRAY_KIND -> WasmType.ARRAY_HEAPTYPE; + case STRUCT_KIND -> WasmType.STRUCT_HEAPTYPE; + case FUNCTION_KIND -> WasmType.FUNC_HEAPTYPE; + default -> throw CompilerDirectives.shouldNotReachHere(); }; } }; @@ -1141,7 +1072,17 @@ public boolean matchesType(int expectedType, int actualType) { return false; } } - return closedTypeOf(expectedType).isSupertypeOf(closedTypeOf(actualType)); + StorageType closedExpectedType = closedStorageTypeOf(expectedType); + StorageType closedActualType = closedStorageTypeOf(actualType); + return closedExpectedType.equals(closedActualType) || closedActualType.isSubtypeOf(closedExpectedType); + } + + /** + * An alternative wording of {@link #matchesType} which is more natural when expressing + * subtyping constraints in type judgments. + */ + public boolean isSubtypeOf(int subType, int superType) { + return matchesType(superType, subType); } public void importSymbol(ImportDescriptor descriptor) { @@ -1239,9 +1180,9 @@ void allocateGlobal(int index, int valueType, byte mutability, boolean initializ ensureGlobalsCapacity(index); numGlobals = maxUnsigned(index + 1, numGlobals); byte flags; - if (mutability == GlobalModifier.CONSTANT) { + if (mutability == Mutability.CONSTANT) { flags = 0; - } else if (mutability == GlobalModifier.MUTABLE) { + } else if (mutability == Mutability.MUTABLE) { flags = GLOBAL_MUTABLE_BIT; } else { throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Invalid mutability: " + mutability); @@ -1323,14 +1264,14 @@ public final int globalAddress(int index) { public byte globalMutability(int index) { if ((globalFlags(index) & GLOBAL_MUTABLE_BIT) != 0) { - return GlobalModifier.MUTABLE; + return Mutability.MUTABLE; } else { - return GlobalModifier.CONSTANT; + return Mutability.CONSTANT; } } public boolean isGlobalMutable(int index) { - return globalMutability(index) == GlobalModifier.MUTABLE; + return globalMutability(index) == Mutability.MUTABLE; } public int globalValueType(int index) { @@ -1629,7 +1570,7 @@ public void allocateTag(int index, byte attribute, int typeIndex) { checkNotParsed(); addTag(index, attribute, typeIndex); module().addLinkAction((context, store, instance, imports) -> { - final WasmTag tag = new WasmTag(closedFunctionTypeAt(typeIndex)); + final WasmTag tag = new WasmTag(closedTypeAt(typeIndex)); instance.setTag(index, tag); }); } @@ -1638,7 +1579,7 @@ public void importTag(String moduleName, String tagName, int index, byte attribu checkNotParsed(); addTag(index, attribute, typeIndex); final ImportDescriptor importedTag = new ImportDescriptor(moduleName, tagName, ImportIdentifier.TAG, index, numImportedSymbols()); - final ClosedFunctionType type = closedFunctionTypeAt(typeIndex); + final DefinedType type = closedTypeAt(typeIndex); importedTags.put(index, importedTag); importSymbol(importedTag); module().addLinkAction((context, store, instance, imports) -> { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContextOptions.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContextOptions.java index 085bd6f34cb1..fb6083613193 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContextOptions.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContextOptions.java @@ -62,6 +62,7 @@ public final class WasmContextOptions { @CompilationFinal private boolean relaxedSimd; @CompilationFinal private boolean exceptions; @CompilationFinal private boolean typedFunctionReferences; + @CompilationFinal private boolean gc; @CompilationFinal private boolean memoryOverheadMode; @CompilationFinal private boolean constantRandomGet; @@ -95,6 +96,7 @@ private void setOptionValues() { this.relaxedSimd = readBooleanOption(WasmOptions.RelaxedSIMD); this.exceptions = readBooleanOption(WasmOptions.Exceptions); this.typedFunctionReferences = readBooleanOption(WasmOptions.TypedFunctionReferences); + this.gc = readBooleanOption(WasmOptions.GC); this.memoryOverheadMode = readBooleanOption(WasmOptions.MemoryOverheadMode); this.constantRandomGet = readBooleanOption(WasmOptions.WasiConstantRandomGet); this.directByteBufferMemoryAccess = readBooleanOption(WasmOptions.DirectByteBufferMemoryAccess); @@ -109,6 +111,9 @@ private void checkOptionDependencies() { if (directByteBufferMemoryAccess && !unsafeMemory) { failDependencyCheck("DirectByteBufferMemoryAccess", "UseUnsafeMemory"); } + if (gc && !typedFunctionReferences) { + failDependencyCheck("GC", "TypedFunctionReferences"); + } } private boolean readBooleanOption(OptionKey key) { @@ -171,6 +176,10 @@ public boolean supportTypedFunctionReferences() { return typedFunctionReferences; } + public boolean supportGC() { + return gc; + } + public boolean memoryOverheadMode() { return memoryOverheadMode; } @@ -206,6 +215,7 @@ public int hashCode() { hash = 53 * hash + (this.relaxedSimd ? 1 : 0); hash = 53 * hash + (this.exceptions ? 1 : 0); hash = 53 * hash + (this.typedFunctionReferences ? 1 : 0); + hash = 53 * hash + (this.gc ? 1 : 0); hash = 53 * hash + (this.memoryOverheadMode ? 1 : 0); hash = 53 * hash + (this.constantRandomGet ? 1 : 0); hash = 53 * hash + (this.directByteBufferMemoryAccess ? 1 : 0); @@ -261,6 +271,9 @@ public boolean equals(Object obj) { if (this.typedFunctionReferences != other.typedFunctionReferences) { return false; } + if (this.gc != other.gc) { + return false; + } if (this.memoryOverheadMode != other.memoryOverheadMode) { return false; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunction.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunction.java index a54608092eea..b5fbf8f63692 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunction.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunction.java @@ -43,16 +43,14 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import org.graalvm.wasm.exception.Failure; -import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.types.DefinedType; public final class WasmFunction { private final SymbolTable symbolTable; private final int index; private final ImportDescriptor importDescriptor; private final int typeIndex; - private final SymbolTable.ClosedFunctionType closedFunctionType; - @CompilationFinal private int typeEquivalenceClass; + private final DefinedType definedType; @CompilationFinal private String debugName; @CompilationFinal private CallTarget callTarget; /** Interop call adapter for argument and return value validation and conversion. */ @@ -66,7 +64,7 @@ public WasmFunction(SymbolTable symbolTable, int index, int typeIndex, ImportDes this.index = index; this.importDescriptor = importDescriptor; this.typeIndex = typeIndex; - this.closedFunctionType = symbolTable.closedFunctionTypeAt(typeIndex); + this.definedType = symbolTable.closedTypeAt(typeIndex); } public String moduleName() { @@ -93,13 +91,6 @@ public int resultTypeAt(int returnIndex) { return symbolTable.functionTypeResultTypeAt(typeIndex, returnIndex); } - void setTypeEquivalenceClass(int typeEquivalenceClass) { - if (this.typeEquivalenceClass != SymbolTable.NO_EQUIVALENCE_CLASS) { - throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Function at index " + index + " already has an equivalence class."); - } - this.typeEquivalenceClass = typeEquivalenceClass; - } - public int[] resultTypes() { return symbolTable.functionTypeResultTypesAsArray(typeIndex); } @@ -156,12 +147,8 @@ public int typeIndex() { return typeIndex; } - public SymbolTable.ClosedFunctionType closedType() { - return closedFunctionType; - } - - public int typeEquivalenceClass() { - return typeEquivalenceClass; + public DefinedType type() { + return definedType; } public int index() { @@ -192,7 +179,7 @@ public CallTarget getOrCreateInteropCallAdapter(WasmLanguage language) { CallTarget callAdapter = this.interopCallAdapter; if (callAdapter == null) { // Benign initialization race: The call target will be the same each time. - callAdapter = language.interopCallAdapterFor(closedType()); + callAdapter = language.interopCallAdapterFor(type().asFunctionType()); this.interopCallAdapter = callAdapter; } return callAdapter; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunctionInstance.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunctionInstance.java index dcf20be2c260..a7deead30bf8 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunctionInstance.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmFunctionInstance.java @@ -58,7 +58,7 @@ import com.oracle.truffle.api.nodes.Node; @ExportLibrary(InteropLibrary.class) -public final class WasmFunctionInstance extends EmbedderDataHolder implements TruffleObject { +public final class WasmFunctionInstance extends WasmTypedHeapObject implements TruffleObject, EmbedderDataHolder { private final WasmContext context; private final WasmInstance moduleInstance; @@ -70,6 +70,8 @@ public final class WasmFunctionInstance extends EmbedderDataHolder implements Tr */ private Object importedFunction; + private Object embedderData = WasmConstant.VOID; + /** * Represents a call target that is a WebAssembly function or an imported function. */ @@ -78,6 +80,7 @@ public WasmFunctionInstance(WasmInstance moduleInstance, WasmFunction function, } public WasmFunctionInstance(WasmContext context, WasmInstance moduleInstance, WasmFunction function, CallTarget target) { + super(function.type()); this.context = Objects.requireNonNull(context, "context must be non-null"); this.moduleInstance = Objects.requireNonNull(moduleInstance, "module instance must be non-null"); this.function = Objects.requireNonNull(function, "function must be non-null"); @@ -179,4 +182,14 @@ static CallTarget getOrCreateInteropCallAdapter(WasmFunctionInstance functionIns return callAdapter; } } + + @Override + public Object getEmbedderData() { + return embedderData; + } + + @Override + public void setEmbedderData(Object embedderData) { + this.embedderData = embedderData; + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmInstantiator.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmInstantiator.java index 6e2a9053a3eb..47647244cb75 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmInstantiator.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmInstantiator.java @@ -69,6 +69,7 @@ import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.nodes.Node; +import org.graalvm.wasm.types.DefinedType; /** * Creates wasm instances by converting parser nodes into Truffle nodes. @@ -202,7 +203,7 @@ static List recreateLinkActions(WasmModule module) { for (int i = 0; i < module.tagCount(); i++) { final int tagIndex = i; final int typeIndex = module.tagTypeIndex(tagIndex); - final SymbolTable.ClosedFunctionType type = module.closedFunctionTypeAt(typeIndex); + final DefinedType type = module.closedTypeAt(typeIndex); final ImportDescriptor tagDescriptor = module.importedTag(tagIndex); if (tagDescriptor != null) { linkActions.add((context, store, instance, imports) -> { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmLanguage.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmLanguage.java index 514c954372c3..0d43ce73f3ef 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmLanguage.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmLanguage.java @@ -70,6 +70,8 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.FunctionType; @Registration(id = WasmLanguage.ID, // name = WasmLanguage.NAME, // @@ -95,11 +97,11 @@ public final class WasmLanguage extends TruffleLanguage { private final Map builtinModules = new ConcurrentHashMap<>(); - private final Map equivalenceClasses = new ConcurrentHashMap<>(); + private final Map equivalenceClasses = new ConcurrentHashMap<>(); private int nextEquivalenceClass = SymbolTable.FIRST_EQUIVALENCE_CLASS; - private final Map interopCallAdapters = new ConcurrentHashMap<>(); + private final Map interopCallAdapters = new ConcurrentHashMap<>(); - public int equivalenceClassFor(SymbolTable.ClosedFunctionType type) { + public int equivalenceClassFor(DefinedType type) { CompilerAsserts.neverPartOfCompilation(); Integer equivalenceClass = equivalenceClasses.get(type); if (equivalenceClass == null) { @@ -119,7 +121,7 @@ public int equivalenceClassFor(SymbolTable.ClosedFunctionType type) { * Gets or creates the interop call adapter for a function type. Always returns the same call * target for any particular type. */ - public CallTarget interopCallAdapterFor(SymbolTable.ClosedFunctionType type) { + public CallTarget interopCallAdapterFor(FunctionType type) { CompilerAsserts.neverPartOfCompilation(); CallTarget callAdapter = interopCallAdapters.get(type); if (callAdapter == null) { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmOptions.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmOptions.java index 54dc2e7f2538..20d70d1aa568 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmOptions.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmOptions.java @@ -142,6 +142,9 @@ public enum ConstantsStorePolicy { @Option(help = "Enable support for typed function references", category = OptionCategory.EXPERT, stability = OptionStability.EXPERIMENTAL, usageSyntax = "false|true") // public static final OptionKey TypedFunctionReferences = new OptionKey<>(false); + @Option(help = "Enable support for garbage collected types", category = OptionCategory.EXPERT, stability = OptionStability.EXPERIMENTAL, usageSyntax = "false|true") // + public static final OptionKey GC = new OptionKey<>(false); + @Option(help = "In this mode memories and tables are not initialized.", category = OptionCategory.INTERNAL, stability = OptionStability.EXPERIMENTAL, usageSyntax = "false|true") // public static final OptionKey MemoryOverheadMode = new OptionKey<>(false); diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTable.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTable.java index 85eafc7d5d83..2e44b1f78d3c 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTable.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTable.java @@ -50,8 +50,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.TruffleObject; +import org.graalvm.wasm.types.ValueType; -public final class WasmTable extends EmbedderDataHolder implements TruffleObject { +public final class WasmTable implements TruffleObject, EmbedderDataHolder { /** * @see #declaredMinSize() */ @@ -91,6 +92,8 @@ public final class WasmTable extends EmbedderDataHolder implements TruffleObject private Object[] elements; + private Object embedderData = WasmConstant.VOID; + @TruffleBoundary private WasmTable(int declaredMinSize, int declaredMaxSize, int initialSize, int maxAllowedSize, int elemType, Object initialValue, SymbolTable symbolTable) { assert compareUnsigned(declaredMinSize, initialSize) <= 0; @@ -163,8 +166,8 @@ public int declaredMaxSize() { *

* This table can only be imported with an equivalent elem type. * - * @return Either {@link WasmType#FUNCREF_TYPE}, {@link WasmType#EXTERNREF_TYPE} or some - * concrete reference type. + * @return Either an abstract reference type such as {@link WasmType#FUNCREF_TYPE} or + * {@link WasmType#EXTERNREF_TYPE}, or some concrete reference type. */ public int elemType() { return elemType; @@ -173,7 +176,7 @@ public int elemType() { /** * The closed form of the type of the elements in the table. */ - public SymbolTable.ClosedValueType closedElemType() { + public ValueType closedElemType() { return SymbolTable.closedTypeOf(elemType, symbolTable); } @@ -266,4 +269,14 @@ public int grow(int delta, Object value) { } return -1; } + + @Override + public Object getEmbedderData() { + return embedderData; + } + + @Override + public void setEmbedderData(Object embedderData) { + this.embedderData = embedderData; + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTag.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTag.java index f760faf8a368..214a30512dd9 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTag.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTag.java @@ -41,19 +41,33 @@ package org.graalvm.wasm; import com.oracle.truffle.api.interop.TruffleObject; +import org.graalvm.wasm.types.DefinedType; -public final class WasmTag extends EmbedderDataHolder implements TruffleObject { +public final class WasmTag implements TruffleObject, EmbedderDataHolder { public static final class Attribute { public static final int EXCEPTION = 0; } - private final SymbolTable.ClosedFunctionType type; + private final DefinedType type; - public WasmTag(SymbolTable.ClosedFunctionType type) { + private Object embedderData = WasmConstant.VOID; + + public WasmTag(DefinedType type) { + assert type.isFunctionType() && type.asFunctionType().resultTypes().length == 0; this.type = type; } - public SymbolTable.ClosedFunctionType type() { + public DefinedType type() { return type; } + + @Override + public Object getEmbedderData() { + return embedderData; + } + + @Override + public void setEmbedderData(Object embedderData) { + this.embedderData = embedderData; + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmType.java index 803b6e576f21..69efbac0bb09 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmType.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmType.java @@ -41,6 +41,7 @@ package org.graalvm.wasm; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; @@ -116,19 +117,61 @@ public class WasmType implements TruffleObject { public static final int V128_TYPE = -0x05; @CompilationFinal(dimensions = 1) public static final int[] V128_TYPE_ARRAY = {V128_TYPE}; + /** + * Packed Types. + */ + public static final int I8_TYPE = -0x08; + public static final int I16_TYPE = -0x09; + /** * Reference Types. */ + public static final int NOEXN_HEAPTYPE = -0x0c; + public static final int NOFUNC_HEAPTYPE = -0x0d; + public static final int NOEXTERN_HEAPTYPE = -0x0e; + public static final int NONE_HEAPTYPE = -0x0f; public static final int FUNC_HEAPTYPE = -0x10; public static final int EXTERN_HEAPTYPE = -0x11; + public static final int ANY_HEAPTYPE = -0x12; + public static final int EQ_HEAPTYPE = -0x13; + public static final int I31_HEAPTYPE = -0x14; + public static final int STRUCT_HEAPTYPE = -0x15; + public static final int ARRAY_HEAPTYPE = -0x16; public static final int EXN_HEAPTYPE = -0x17; + public static final int NULLEXNREF_TYPE = NOEXN_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] NULLEXNREF_TYPE_ARRAY = {NULLEXNREF_TYPE}; + + public static final int NULLFUNCREF_TYPE = NOFUNC_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] NULLFUNCREF_TYPE_ARRAY = {NULLFUNCREF_TYPE}; + + public static final int NULLEXTERNREF_TYPE = NOEXTERN_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] NULLEXTERNREF_TYPE_ARRAY = {NULLEXTERNREF_TYPE}; + + public static final int NULLREF_TYPE = NONE_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] NULLREF_TYPE_ARRAY = {NULLREF_TYPE}; + public static final int FUNCREF_TYPE = FUNC_HEAPTYPE; @CompilationFinal(dimensions = 1) public static final int[] FUNCREF_TYPE_ARRAY = {FUNCREF_TYPE}; public static final int EXTERNREF_TYPE = EXTERN_HEAPTYPE; @CompilationFinal(dimensions = 1) public static final int[] EXTERNREF_TYPE_ARRAY = {EXTERNREF_TYPE}; + public static final int ANYREF_TYPE = ANY_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] ANYREF_TYPE_ARRAY = {ANYREF_TYPE}; + + public static final int EQREF_TYPE = EQ_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] EQREF_TYPE_ARRAY = {EQREF_TYPE}; + + public static final int I31REF_TYPE = I31_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] I31REF_TYPE_ARRAY = {I31REF_TYPE}; + + public static final int STRUCTREF_TYPE = STRUCT_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] STRUCTREF_TYPE_ARRAY = {STRUCTREF_TYPE}; + + public static final int ARRAYREF_TYPE = ARRAY_HEAPTYPE; + @CompilationFinal(dimensions = 1) public static final int[] ARRAYREF_TYPE_ARRAY = {ARRAYREF_TYPE}; + public static final int EXNREF_TYPE = EXN_HEAPTYPE; @CompilationFinal(dimensions = 1) public static final int[] EXNREF_TYPE_ARRAY = {EXNREF_TYPE}; @@ -147,6 +190,8 @@ public class WasmType implements TruffleObject { */ public static final byte REF_TYPE_HEADER = -0x1c; public static final byte REF_NULL_TYPE_HEADER = -0x1d; + public static final byte I8_TYPE_BYTE = 0x77; + public static final byte I16_TYPE_BYTE = 0x78; // -0x40 is what the void block type byte 0x40 looks like when read as a signed LEB128 value. public static final byte VOID_BLOCK_TYPE = -0x40; @CompilationFinal(dimensions = 1) public static final int[] VOID_TYPE_ARRAY = {}; @@ -171,14 +216,25 @@ public static String toString(int valueType) { case F32_TYPE -> "f32"; case F64_TYPE -> "f64"; case V128_TYPE -> "v128"; + case I8_TYPE -> "i8"; + case I16_TYPE -> "i16"; case TOP -> "top"; case BOT -> "bot"; default -> { assert WasmType.isReferenceType(valueType); boolean nullable = WasmType.isNullable(valueType); yield switch (WasmType.getAbstractHeapType(valueType)) { + case NOEXN_HEAPTYPE -> nullable ? "nullexnref" : "(ref noexn)"; + case NOFUNC_HEAPTYPE -> nullable ? "nullfuncref" : "(ref nofunc)"; + case NOEXTERN_HEAPTYPE -> nullable ? "nullexternref" : "(ref noextern)"; + case NONE_HEAPTYPE -> nullable ? "nullref" : "(ref none)"; case FUNC_HEAPTYPE -> nullable ? "funcref" : "(ref func)"; case EXTERN_HEAPTYPE -> nullable ? "externref" : "(ref extern)"; + case ANY_HEAPTYPE -> nullable ? "anyref" : "(ref any)"; + case EQ_HEAPTYPE -> nullable ? "eqref" : "(ref eq)"; + case I31_HEAPTYPE -> nullable ? "i31ref" : "(ref i31)"; + case STRUCT_HEAPTYPE -> nullable ? "structref" : "(ref struct)"; + case ARRAY_HEAPTYPE -> nullable ? "arrayref" : "(ref array)"; case EXN_HEAPTYPE -> nullable ? "exnref" : "(ref exn)"; default -> { assert WasmType.isConcreteReferenceType(valueType); @@ -205,7 +261,12 @@ public static boolean isVectorType(int type) { } public static boolean isReferenceType(int type) { - return isConcreteReferenceType(type) || withNullable(true, type) == FUNC_HEAPTYPE || withNullable(true, type) == EXTERN_HEAPTYPE || withNullable(true, type) == EXN_HEAPTYPE || type == BOT; + int nullableType = withNullable(true, type); + return isConcreteReferenceType(type) || (nullableType >= EXN_HEAPTYPE && nullableType <= NOFUNC_HEAPTYPE) || nullableType == NOEXN_HEAPTYPE; + } + + public static boolean isPackedType(int type) { + return type == I8_TYPE || type == I16_TYPE || type == BOT; } /** @@ -234,6 +295,15 @@ public static int getAbstractHeapType(int type) { return withNullable(true, type); } + public static int getHeapType(int type) { + assert isReferenceType(type); + if (isConcreteReferenceType(type)) { + return getTypeIndex(type); + } else { + return getAbstractHeapType(type); + } + } + /** * Indicates whether this value types admits the value {@link WasmConstant#NULL}. Can only be * called on reference types. @@ -267,6 +337,54 @@ public static boolean hasDefaultValue(int type) { return !(isReferenceType(type) && !isNullable(type)); } + /** + * Maps the {@code storageType} to a value type that can hold its value on the operand stack. + * + * @param storageType a {@link #isPackedType packed type}, a value type or {@link WasmType#BOT + * bottom} + * @return a value type that can hold the value of {@code storageType} + */ + public static int unpack(int storageType) { + if (storageType == BOT) { + return BOT; + } else if (isPackedType(storageType)) { + return I32_TYPE; + } else { + return storageType; + } + } + + /** + * Returns the size in bytes that is occupied by a value of type {@code storageType}. + * + * @param storageType a {@link #isPackedType packed type} or a value type + */ + public static int storageByteSize(int storageType) { + assert isPackedType(storageType) || isNumberType(storageType) || isVectorType(storageType); + return switch (storageType) { + case I8_TYPE -> 1; + case I16_TYPE -> 2; + case I32_TYPE, F32_TYPE -> 4; + case I64_TYPE, F64_TYPE -> 8; + case V128_TYPE -> 16; + default -> throw CompilerDirectives.shouldNotReachHere("storageByteSize of reference type"); + }; + } + + /** + * Computes an approximation to the type containing values of type {@code typeA} that are not in + * {@code typeB}. Given WebAssembly's type system, this can only have the effect of removing + * {@code ref.null} when subtracting a nullable reference type from another nullable reference + * type. + */ + public static int difference(int typeA, int typeB) { + if (isReferenceType(typeA) && isNullable(typeA) && isReferenceType(typeB) && isNullable(typeB)) { + return withNullable(false, typeA); + } else { + return typeA; + } + } + public static int getCommonValueType(int[] types) { int type = 0; for (int resultType : types) { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTypedHeapObject.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTypedHeapObject.java new file mode 100644 index 000000000000..8b758de6f9e8 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmTypedHeapObject.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm; + +import com.oracle.truffle.api.interop.TruffleObject; +import org.graalvm.wasm.types.DefinedType; + +public abstract class WasmTypedHeapObject implements TruffleObject { + + private final DefinedType type; + + protected WasmTypedHeapObject(DefinedType type) { + this.type = type; + } + + public final DefinedType type() { + return type; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ExecuteHostFunctionNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ExecuteHostFunctionNode.java index cb6277d78f4e..0986ee0b2248 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ExecuteHostFunctionNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ExecuteHostFunctionNode.java @@ -40,7 +40,6 @@ */ package org.graalvm.wasm.api; -import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmArguments; import org.graalvm.wasm.WasmConstant; import org.graalvm.wasm.WasmContext; @@ -66,6 +65,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.BranchProfile; +import org.graalvm.wasm.types.ValueType; /** * Wrapper call target for executing imported host functions in the parent context. @@ -128,7 +128,7 @@ public Object execute(VirtualFrame frame) { */ private Object convertResult(Object result, int resultType) throws UnsupportedMessageException { CompilerAsserts.partialEvaluationConstant(resultType); - SymbolTable.ClosedValueType closedResultType = module.closedTypeOf(resultType); + ValueType closedResultType = module.closedTypeOf(resultType); CompilerAsserts.partialEvaluationConstant(closedResultType); return switch (resultType) { case WasmType.I32_TYPE -> asInt(result); @@ -166,7 +166,7 @@ private void pushMultiValueResult(Object result, int resultCount) { for (int i = 0; i < resultCount; i++) { int resultType = module.symbolTable().functionTypeResultTypeAt(functionTypeIndex, i); CompilerAsserts.partialEvaluationConstant(resultType); - SymbolTable.ClosedValueType closedResultType = module.closedTypeOf(resultType); + ValueType closedResultType = module.closedTypeOf(resultType); CompilerAsserts.partialEvaluationConstant(closedResultType); Object value = arrayInterop.readArrayElement(result, i); switch (resultType) { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/FuncType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/FuncType.java index 2cef0d2abe41..d4b997e9340d 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/FuncType.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/FuncType.java @@ -41,10 +41,13 @@ package org.graalvm.wasm.api; -import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.exception.WasmJsApiException; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.FunctionType; +import org.graalvm.wasm.types.RecursiveTypes; +import org.graalvm.wasm.types.SubType; /** * Represents the type of functions and exceptions in the JS API. @@ -84,9 +87,12 @@ private static ValueType[] parseTypeString(String typesString, int start, int en } } - public static FuncType fromClosedFunctionType(SymbolTable.ClosedFunctionType functionType) { - final SymbolTable.ClosedValueType[] paramTypes = functionType.paramTypes(); - final SymbolTable.ClosedValueType[] resultTypes = functionType.resultTypes(); + public static FuncType fromDefinedType(DefinedType definedType) { + assert definedType.isFunctionType(); + + final FunctionType functionType = definedType.asFunctionType(); + final org.graalvm.wasm.types.ValueType[] paramTypes = functionType.paramTypes(); + final org.graalvm.wasm.types.ValueType[] resultTypes = functionType.resultTypes(); final ValueType[] params = new ValueType[paramTypes.length]; final ValueType[] results = new ValueType[resultTypes.length]; @@ -116,9 +122,9 @@ public int resultCount() { return results.length; } - public SymbolTable.ClosedFunctionType toClosedFunctionType() { - var paramTypes = new SymbolTable.ClosedValueType[params.length]; - var resultTypes = new SymbolTable.ClosedValueType[results.length]; + public DefinedType toDefinedType() { + var paramTypes = new org.graalvm.wasm.types.ValueType[params.length]; + var resultTypes = new org.graalvm.wasm.types.ValueType[results.length]; for (int i = 0; i < paramTypes.length; i++) { paramTypes[i] = params[i].asClosedValueType(); @@ -126,7 +132,13 @@ public SymbolTable.ClosedFunctionType toClosedFunctionType() { for (int i = 0; i < resultTypes.length; i++) { resultTypes[i] = results[i].asClosedValueType(); } - return new SymbolTable.ClosedFunctionType(paramTypes, resultTypes); + + FunctionType functionType = new FunctionType(paramTypes, resultTypes); + SubType subType = new SubType(true, null, functionType); + RecursiveTypes recursiveTypeGroup = new RecursiveTypes(new SubType[]{subType}); + // This DefinedType will not have a typeEquivalence class. It will not need one, as it will + // never participate in runtime type checking. + return DefinedType.makeTopLevelType(recursiveTypeGroup, 0); } public String toString(StringBuilder b) { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/InteropCallAdapterNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/InteropCallAdapterNode.java index 82aa7f8f9865..29a16d8ce9f0 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/InteropCallAdapterNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/InteropCallAdapterNode.java @@ -40,12 +40,10 @@ */ package org.graalvm.wasm.api; -import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmArguments; import org.graalvm.wasm.WasmContext; import org.graalvm.wasm.WasmFunctionInstance; import org.graalvm.wasm.WasmLanguage; -import org.graalvm.wasm.WasmType; import org.graalvm.wasm.nodes.WasmIndirectCallNode; import com.oracle.truffle.api.CallTarget; @@ -57,6 +55,9 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.BranchProfile; +import org.graalvm.wasm.types.FunctionType; +import org.graalvm.wasm.types.NumberType; +import org.graalvm.wasm.types.ValueType; /** * Wrapper call target for executing wasm functions, validating and adapting the arguments for wasm @@ -72,11 +73,11 @@ public final class InteropCallAdapterNode extends RootNode { private static final int MAX_UNROLL = 32; - private final SymbolTable.ClosedFunctionType functionType; + private final FunctionType functionType; private final BranchProfile errorBranch = BranchProfile.create(); @Child private WasmIndirectCallNode callNode; - public InteropCallAdapterNode(WasmLanguage language, SymbolTable.ClosedFunctionType functionType) { + public InteropCallAdapterNode(WasmLanguage language, FunctionType functionType) { super(language); this.functionType = functionType; this.callNode = WasmIndirectCallNode.create(); @@ -107,7 +108,7 @@ public Object execute(VirtualFrame frame) { } private Object[] validateArguments(Object[] arguments, int offset) throws ArityException, UnsupportedTypeException { - final SymbolTable.ClosedValueType[] paramTypes = functionType.paramTypes(); + final ValueType[] paramTypes = functionType.paramTypes(); final int paramCount = paramTypes.length; CompilerAsserts.partialEvaluationConstant(paramCount); if (arguments.length - offset != paramCount) { @@ -124,14 +125,14 @@ private Object[] validateArguments(Object[] arguments, int offset) throws ArityE } @ExplodeLoop - private static void validateArgumentsUnroll(Object[] arguments, int offset, SymbolTable.ClosedValueType[] paramTypes, int paramCount) throws UnsupportedTypeException { + private static void validateArgumentsUnroll(Object[] arguments, int offset, ValueType[] paramTypes, int paramCount) throws UnsupportedTypeException { for (int i = 0; i < paramCount; i++) { validateArgument(arguments, offset, paramTypes, i); } } - private static void validateArgument(Object[] arguments, int offset, SymbolTable.ClosedValueType[] paramTypes, int i) throws UnsupportedTypeException { - SymbolTable.ClosedValueType paramType = paramTypes[i]; + private static void validateArgument(Object[] arguments, int offset, ValueType[] paramTypes, int i) throws UnsupportedTypeException { + ValueType paramType = paramTypes[i]; Object value = arguments[i + offset]; if (!paramType.matchesValue(value)) { throw UnsupportedTypeException.create(arguments); @@ -142,7 +143,7 @@ private Object multiValueStackAsArray(WasmLanguage language) { final var multiValueStack = language.multiValueStack(); final long[] primitiveMultiValueStack = multiValueStack.primitiveStack(); final Object[] objectMultiValueStack = multiValueStack.objectStack(); - final SymbolTable.ClosedValueType[] resultTypes = functionType.resultTypes(); + final ValueType[] resultTypes = functionType.resultTypes(); final int resultCount = resultTypes.length; assert primitiveMultiValueStack.length >= resultCount; assert objectMultiValueStack.length >= resultCount; @@ -159,25 +160,21 @@ private Object multiValueStackAsArray(WasmLanguage language) { } @ExplodeLoop - private static void popMultiValueResultUnroll(Object[] values, long[] primitiveMultiValueStack, Object[] objectMultiValueStack, SymbolTable.ClosedValueType[] resultTypes, int resultCount) { + private static void popMultiValueResultUnroll(Object[] values, long[] primitiveMultiValueStack, Object[] objectMultiValueStack, ValueType[] resultTypes, int resultCount) { for (int i = 0; i < resultCount; i++) { values[i] = popMultiValueResult(primitiveMultiValueStack, objectMultiValueStack, resultTypes, i); } } - private static Object popMultiValueResult(long[] primitiveMultiValueStack, Object[] objectMultiValueStack, SymbolTable.ClosedValueType[] resultTypes, int i) { - final SymbolTable.ClosedValueType resultType = resultTypes[i]; - return switch (resultType.kind()) { - case Number -> { - SymbolTable.NumberType numberType = (SymbolTable.NumberType) resultType; - yield switch (numberType.value()) { - case WasmType.I32_TYPE -> (int) primitiveMultiValueStack[i]; - case WasmType.I64_TYPE -> primitiveMultiValueStack[i]; - case WasmType.F32_TYPE -> Float.intBitsToFloat((int) primitiveMultiValueStack[i]); - case WasmType.F64_TYPE -> Double.longBitsToDouble(primitiveMultiValueStack[i]); - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - } + private static Object popMultiValueResult(long[] primitiveMultiValueStack, Object[] objectMultiValueStack, ValueType[] resultTypes, int i) { + final ValueType resultType = resultTypes[i]; + return switch (resultType.valueKind()) { + case Number -> switch ((NumberType) resultType) { + case I32 -> (int) primitiveMultiValueStack[i]; + case I64 -> primitiveMultiValueStack[i]; + case F32 -> Float.intBitsToFloat((int) primitiveMultiValueStack[i]); + case F64 -> Double.longBitsToDouble(primitiveMultiValueStack[i]); + }; case Vector, Reference -> { Object obj = objectMultiValueStack[i]; objectMultiValueStack[i] = null; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/JsConstants.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/JsConstants.java index 4b37d7e3e951..679a573edb89 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/JsConstants.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/JsConstants.java @@ -49,40 +49,46 @@ private JsConstants() { // Limits specified by https://www.w3.org/TR/wasm-js-api/#limits private static final int MODULE_SIZE_LIMIT = 1 << 30; - private static final int TYPE_COUNT_LIMIT = 1000000; - private static final int FUNCTION_COUNT_LIMIT = 1000000; - private static final int IMPORT_COUNT_LIMIT = 100000; - private static final int EXPORT_COUNT_LIMIT = 100000; - private static final int GLOBAL_COUNT_LIMIT = 1000000; - private static final int DATA_SEGMENT_LIMIT = 100000; - private static final int TABLE_COUNT_LIMIT = 100000; - private static final int MULTI_MEMORY_COUNT_LIMIT = 100; - private static final int ELEMENT_SEGMENT_LIMIT = 10000000; - private static final int FUNCTION_SIZE_LIMIT = 7654321; - private static final int PARAM_COUNT_LIMIT = 1000; - private static final int MULTI_VALUE_RESULT_COUNT_LIMIT = 1000; - private static final int LOCAL_COUNT_LIMIT = 50000; - private static final int TABLE_SIZE_LIMIT = 10000000; - private static final int MEMORY_SIZE_LIMIT = 65536; + private static final int TYPE_COUNT_LIMIT = 1_000_000; + private static final int SUBTYPE_DEPTH_LIMIT = 63; + private static final int FUNCTION_COUNT_LIMIT = 1_000_000; + private static final int IMPORT_COUNT_LIMIT = 1_000_000; + private static final int EXPORT_COUNT_LIMIT = 1_000_000; + private static final int GLOBAL_COUNT_LIMIT = 1_000_000; private static final int TAG_COUNT_LIMIT = 1_000_000; + private static final int DATA_SEGMENT_LIMIT = 100_000; + private static final int TABLE_COUNT_LIMIT = 100_000; + private static final int MULTI_MEMORY_COUNT_LIMIT = 100; + private static final int ELEMENT_SEGMENT_LIMIT = 1_000_0000; + private static final int PARAM_COUNT_LIMIT = 1_000; + private static final int MULTI_VALUE_RESULT_COUNT_LIMIT = 1_000; + private static final int FUNCTION_SIZE_LIMIT = 7_654_321; + private static final int LOCAL_COUNT_LIMIT = 50_000; + private static final int STRUCT_FIELD_COUNT_LIMIT = 10_000; + private static final int ARRAY_NEW_FIXED_LENGTH_LIMIT = 10_000; + private static final int TABLE_SIZE_LIMIT = 10_000_000; + private static final int MEMORY_SIZE_LIMIT = 65_536; public static final ModuleLimits JS_LIMITS = new ModuleLimits( MODULE_SIZE_LIMIT, TYPE_COUNT_LIMIT, + SUBTYPE_DEPTH_LIMIT, FUNCTION_COUNT_LIMIT, - TABLE_COUNT_LIMIT, - MULTI_MEMORY_COUNT_LIMIT, IMPORT_COUNT_LIMIT, EXPORT_COUNT_LIMIT, GLOBAL_COUNT_LIMIT, + TAG_COUNT_LIMIT, DATA_SEGMENT_LIMIT, ELEMENT_SEGMENT_LIMIT, - FUNCTION_SIZE_LIMIT, + TABLE_COUNT_LIMIT, + MULTI_MEMORY_COUNT_LIMIT, PARAM_COUNT_LIMIT, MULTI_VALUE_RESULT_COUNT_LIMIT, + FUNCTION_SIZE_LIMIT, LOCAL_COUNT_LIMIT, + STRUCT_FIELD_COUNT_LIMIT, + ARRAY_NEW_FIXED_LENGTH_LIMIT, TABLE_SIZE_LIMIT, MEMORY_SIZE_LIMIT, - MEMORY_SIZE_LIMIT, - TAG_COUNT_LIMIT); + MEMORY_SIZE_LIMIT); } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/TableKind.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/TableKind.java index 91ab0519f9d5..411c19093c70 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/TableKind.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/TableKind.java @@ -41,6 +41,7 @@ package org.graalvm.wasm.api; import org.graalvm.wasm.WasmType; +import org.graalvm.wasm.exception.WasmJsApiException; public enum TableKind { externref(WasmType.EXTERNREF_TYPE), @@ -60,7 +61,7 @@ public static String toString(int value) { return switch (value) { case WasmType.EXTERNREF_TYPE -> "externref"; case WasmType.FUNCREF_TYPE -> "anyfunc"; - default -> ""; + default -> throw WasmJsApiException.invalidValueType(value); }; } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ValueType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ValueType.java index 8bccec71a4ca..bf9a78489aec 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ValueType.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ValueType.java @@ -40,12 +40,15 @@ */ package org.graalvm.wasm.api; -import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmType; import com.oracle.truffle.api.CompilerAsserts; -import org.graalvm.wasm.exception.Failure; -import org.graalvm.wasm.exception.WasmException; +import org.graalvm.wasm.exception.WasmJsApiException; +import org.graalvm.wasm.types.AbstractHeapType; +import org.graalvm.wasm.types.HeapType; +import org.graalvm.wasm.types.NumberType; +import org.graalvm.wasm.types.ReferenceType; +import org.graalvm.wasm.types.VectorType; public enum ValueType { i32(WasmType.I32_TYPE), @@ -76,11 +79,7 @@ public static ValueType fromValue(int value) { yield switch (WasmType.getAbstractHeapType(value)) { case WasmType.FUNC_HEAPTYPE -> anyfunc; case WasmType.EXTERN_HEAPTYPE -> externref; - case WasmType.EXN_HEAPTYPE -> exnref; - default -> { - assert WasmType.isConcreteReferenceType(value); - yield anyfunc; - } + default -> throw WasmJsApiException.invalidValueType(value); }; } }; @@ -102,35 +101,38 @@ public static boolean isReferenceType(ValueType valueType) { return valueType == anyfunc || valueType == externref || valueType == exnref; } - public SymbolTable.ClosedValueType asClosedValueType() { + public org.graalvm.wasm.types.ValueType asClosedValueType() { return switch (this) { - case i32 -> SymbolTable.NumberType.I32; - case i64 -> SymbolTable.NumberType.I64; - case f32 -> SymbolTable.NumberType.F32; - case f64 -> SymbolTable.NumberType.F64; - case v128 -> SymbolTable.VectorType.V128; - case anyfunc -> SymbolTable.ClosedReferenceType.FUNCREF; - case externref -> SymbolTable.ClosedReferenceType.EXTERNREF; - case exnref -> SymbolTable.ClosedReferenceType.EXNREF; + case i32 -> NumberType.I32; + case i64 -> NumberType.I64; + case f32 -> NumberType.F32; + case f64 -> NumberType.F64; + case v128 -> VectorType.V128; + case anyfunc -> ReferenceType.FUNCREF; + case externref -> ReferenceType.EXTERNREF; + case exnref -> ReferenceType.EXNREF; }; } - public static ValueType fromClosedValueType(SymbolTable.ClosedValueType closedValueType) { - return switch (closedValueType.kind()) { - case Number -> fromValue(((SymbolTable.NumberType) closedValueType).value()); - case Vector -> fromValue(((SymbolTable.VectorType) closedValueType).value()); + public static ValueType fromClosedValueType(org.graalvm.wasm.types.ValueType closedValueType) { + return switch (closedValueType.valueKind()) { + case Number -> switch ((NumberType) closedValueType) { + case I32 -> i32; + case I64 -> i64; + case F32 -> f32; + case F64 -> f64; + }; + case Vector -> v128; case Reference -> { - SymbolTable.ClosedReferenceType referenceType = (SymbolTable.ClosedReferenceType) closedValueType; - yield switch (referenceType.heapType().kind()) { - case Abstract -> { - SymbolTable.AbstractHeapType abstractHeapType = (SymbolTable.AbstractHeapType) referenceType.heapType(); - yield switch (abstractHeapType.value()) { - case WasmType.FUNC_HEAPTYPE -> anyfunc; - case WasmType.EXTERNREF_TYPE -> externref; - default -> throw WasmException.create(Failure.UNSPECIFIED_INTERNAL, null, "Unknown value type: 0x" + Integer.toHexString(abstractHeapType.value())); - }; - } - case Function -> anyfunc; + HeapType heapType = ((ReferenceType) closedValueType).heapType(); + yield switch (heapType.heapKind()) { + case Abstract -> switch ((AbstractHeapType) heapType) { + case FUNC -> anyfunc; + case EXTERN -> externref; + case EXN -> exnref; + default -> throw WasmJsApiException.invalidValueType(closedValueType); + }; + default -> throw WasmJsApiException.invalidValueType(closedValueType); }; } }; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/WebAssembly.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/WebAssembly.java index e899c8bf5dc4..4024e3103614 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/WebAssembly.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/WebAssembly.java @@ -580,7 +580,7 @@ private static String tagTypeToString(WasmModule module, int tagIndex) { assert attribute == WasmTag.Attribute.EXCEPTION; final int typeIndex = module.tagTypeIndex(tagIndex); - return FuncType.fromClosedFunctionType(module.closedFunctionTypeAt(typeIndex)).toString(); + return FuncType.fromDefinedType(module.closedTypeAt(typeIndex)).toString(); } private static Object memAlloc(Object[] args) { @@ -780,14 +780,14 @@ public WasmGlobal globalAlloc(ValueType valueType, boolean mutable, Object value case i64 -> WasmGlobal.alloc64(valueType, mutable, (long) value); case f32 -> WasmGlobal.alloc32(valueType, mutable, Float.floatToRawIntBits((float) value)); case f64 -> WasmGlobal.alloc64(valueType, mutable, Double.doubleToRawLongBits((double) value)); - case v128 -> throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.V128_VALUE_ACCESS); + case v128 -> throw WasmJsApiException.invalidValueType(WasmType.V128_TYPE); case anyfunc, externref -> { if (!refTypes) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type. Reference types are not enabled."); } yield WasmGlobal.allocRef(valueType, mutable, value); } - case exnref -> throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.EXNREF_VALUE_ACCESS); + case exnref -> throw WasmJsApiException.invalidValueType(WasmType.EXNREF_TYPE); }; } @@ -805,14 +805,14 @@ public Object globalRead(WasmGlobal global) { case WasmType.I64_TYPE -> global.loadAsLong(); case WasmType.F32_TYPE -> Float.intBitsToFloat(global.loadAsInt()); case WasmType.F64_TYPE -> Double.longBitsToDouble(global.loadAsLong()); - case WasmType.V128_TYPE -> throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.V128_VALUE_ACCESS); + case WasmType.V128_TYPE -> throw WasmJsApiException.invalidValueType(WasmType.V128_TYPE); default -> { assert WasmType.isReferenceType(global.getType()); if (!refTypes) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type. Reference types are not enabled."); } - if (SymbolTable.closedTypeOf(WasmType.EXNREF_TYPE, null).isSupertypeOf(global.getClosedType())) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.EXNREF_VALUE_ACCESS); + if (global.getClosedType().isSubtypeOf(SymbolTable.closedTypeOf(WasmType.EXNREF_TYPE, null))) { + throw WasmJsApiException.invalidValueType(WasmType.EXNREF_TYPE); } yield global.loadAsReference(); } @@ -839,14 +839,14 @@ public Object globalWrite(WasmGlobal global, Object value) { case WasmType.I64_TYPE -> global.storeLong((long) value); case WasmType.F32_TYPE -> global.storeInt(Float.floatToRawIntBits((float) value)); case WasmType.F64_TYPE -> global.storeLong(Double.doubleToRawLongBits((double) value)); - case WasmType.V128_TYPE -> throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.V128_VALUE_ACCESS); + case WasmType.V128_TYPE -> throw WasmJsApiException.invalidValueType(WasmType.V128_TYPE); default -> { assert WasmType.isReferenceType(global.getType()); if (!refTypes) { throw WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Reference types are not enabled."); } - if (SymbolTable.closedTypeOf(WasmType.EXNREF_TYPE, null).isSupertypeOf(global.getClosedType())) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, WasmJsApiException.EXNREF_VALUE_ACCESS); + if (global.getClosedType().isSubtypeOf(SymbolTable.closedTypeOf(WasmType.EXNREF_TYPE, null))) { + throw WasmJsApiException.invalidValueType(WasmType.EXNREF_TYPE); } global.storeReference(value); } @@ -870,7 +870,7 @@ public static Object tagAlloc(Object[] args) { } public static WasmTag tagAlloc(FuncType type) { - return new WasmTag(type.toClosedFunctionType()); + return new WasmTag(type.toDefinedType()); } public static Object tagType(Object[] args) { @@ -878,7 +878,7 @@ public static Object tagType(Object[] args) { if (!(args[0] instanceof WasmTag tag)) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be a wasm tag"); } - return tag.type().toString(); + return FuncType.fromDefinedType(tag.type()).toString(); } public WasmRuntimeException exnAlloc(Object[] args) { @@ -886,7 +886,7 @@ public WasmRuntimeException exnAlloc(Object[] args) { if (!(args[0] instanceof WasmTag tag)) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be a wasm tag"); } - final FuncType type = FuncType.fromClosedFunctionType(tag.type()); + final FuncType type = FuncType.fromDefinedType(tag.type()); final int paramCount = type.paramCount(); checkArgumentCount(args, paramCount + 1); final Object[] fields = new Object[paramCount]; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmArray.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmArray.java new file mode 100644 index 000000000000..96177ecc7dc4 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmArray.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.memory.ByteArraySupport; +import org.graalvm.wasm.WasmTypedHeapObject; +import org.graalvm.wasm.constants.Mutability; +import org.graalvm.wasm.types.DefinedType; + +@ExportLibrary(InteropLibrary.class) +public abstract class WasmArray extends WasmTypedHeapObject { + + protected static final ByteArraySupport byteArraySupport = ByteArraySupport.littleEndian(); + + protected final int size; + + protected WasmArray(DefinedType type, int size) { + super(type); + assert type.isArrayType(); + this.size = size; + } + + public final int length() { + return size; + } + + public abstract Object getObj(int index); + + public abstract void setObj(int index, Object value) throws UnsupportedTypeException; + + @ExportMessage + protected static boolean hasArrayElements(@SuppressWarnings("unused") WasmArray receiver) { + return true; + } + + @ExportMessage + protected long getArraySize() { + return size; + } + + @ExportMessage + protected boolean isArrayElementReadable(long index) { + return index >= 0 && index < size; + } + + @ExportMessage + protected Object readArrayElement(long index) throws InvalidArrayIndexException { + if (isArrayElementReadable(index)) { + return getObj((int) index); + } else { + throw InvalidArrayIndexException.create(index); + } + } + + @ExportMessage + protected boolean isArrayElementModifiable(long index) { + return type().asArrayType().fieldType().mutability() == Mutability.MUTABLE && index >= 0 && index < size; + } + + @ExportMessage + protected void writeArrayElement(long index, Object value) throws InvalidArrayIndexException, UnsupportedTypeException { + if (isArrayElementModifiable(index)) { + setObj((int) index, value); + } else { + throw InvalidArrayIndexException.create(index); + } + } + + @ExportMessage + protected boolean isArrayElementInsertable(@SuppressWarnings("unused") long index) { + return false; + } + + @Override + @TruffleBoundary + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("wasm-array:<"); + for (int i = 0; i < size; i++) { + sb.append(getObj(i)); + if (i < size - 1) { + sb.append(", "); + } + } + sb.append(">"); + return sb.toString(); + } + + @ExportMessage + protected String toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) { + return toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat32Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat32Array.java new file mode 100644 index 000000000000..484f65931765 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat32Array.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.NumberType; + +import java.util.Arrays; + +public final class WasmFloat32Array extends WasmArray { + + private final float[] array; + + public WasmFloat32Array(DefinedType type, float[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == NumberType.F32; + this.array = array; + } + + public WasmFloat32Array(DefinedType type, int length, float initialValue) { + this(type, new float[length]); + fill(0, length, initialValue); + } + + public WasmFloat32Array(DefinedType type, int length) { + this(type, new float[length]); + } + + public WasmFloat32Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new float[length]); + initialize(source, srcOffset, 0, length); + } + + public float get(int index) { + return array[index]; + } + + public void set(int index, float value) { + array[index] = value; + } + + public void copyFrom(WasmFloat32Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, float value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + for (int i = 0; i < length; i++) { + array[dstOffset + i] = byteArraySupport.getFloat(source, srcOffset + (i << 2)); + } + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Float floatValue) { + array[index] = floatValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat64Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat64Array.java new file mode 100644 index 000000000000..e598aad8f7af --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmFloat64Array.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.NumberType; + +import java.util.Arrays; + +public final class WasmFloat64Array extends WasmArray { + + private final double[] array; + + public WasmFloat64Array(DefinedType type, double[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == NumberType.F64; + this.array = array; + } + + public WasmFloat64Array(DefinedType type, int length, double initialValue) { + this(type, new double[length]); + fill(0, length, initialValue); + } + + public WasmFloat64Array(DefinedType type, int length) { + this(type, new double[length]); + } + + public WasmFloat64Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new double[length]); + initialize(source, srcOffset, 0, length); + } + + public double get(int index) { + return array[index]; + } + + public void set(int index, double value) { + array[index] = value; + } + + public void copyFrom(WasmFloat64Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, double value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + for (int i = 0; i < length; i++) { + array[dstOffset + i] = byteArraySupport.getDouble(source, srcOffset + (i << 3)); + } + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Double doubleValue) { + array[index] = doubleValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt16Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt16Array.java new file mode 100644 index 000000000000..f876b7735721 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt16Array.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.PackedType; + +import java.util.Arrays; + +public final class WasmInt16Array extends WasmArray { + + private final short[] array; + + public WasmInt16Array(DefinedType type, short[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == PackedType.I16; + this.array = array; + } + + public WasmInt16Array(DefinedType type, int length, short initialValue) { + this(type, new short[length]); + fill(0, length, initialValue); + } + + public WasmInt16Array(DefinedType type, int length) { + this(type, new short[length]); + } + + public WasmInt16Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new short[length]); + initialize(source, srcOffset, 0, length); + } + + public short get(int index) { + return array[index]; + } + + public void set(int index, short value) { + array[index] = value; + } + + public void copyFrom(WasmInt16Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, short value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + for (int i = 0; i < length; i++) { + array[dstOffset + i] = byteArraySupport.getShort(source, srcOffset + (i << 1)); + } + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Short shortValue) { + array[index] = shortValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt32Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt32Array.java new file mode 100644 index 000000000000..f023cb8d2f30 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt32Array.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.NumberType; + +import java.util.Arrays; + +public final class WasmInt32Array extends WasmArray { + + private final int[] array; + + public WasmInt32Array(DefinedType type, int[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == NumberType.I32; + this.array = array; + } + + public WasmInt32Array(DefinedType type, int length, int initialValue) { + this(type, new int[length]); + fill(0, length, initialValue); + } + + public WasmInt32Array(DefinedType type, int length) { + this(type, new int[length]); + } + + public WasmInt32Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new int[length]); + initialize(source, srcOffset, 0, length); + } + + public int get(int index) { + return array[index]; + } + + public void set(int index, int value) { + array[index] = value; + } + + public void copyFrom(WasmInt32Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, int value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + for (int i = 0; i < length; i++) { + array[dstOffset + i] = byteArraySupport.getInt(source, srcOffset + (i << 2)); + } + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Integer intValue) { + array[index] = intValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt64Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt64Array.java new file mode 100644 index 000000000000..78724c5a989d --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt64Array.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.NumberType; + +import java.util.Arrays; + +public final class WasmInt64Array extends WasmArray { + + private final long[] array; + + public WasmInt64Array(DefinedType type, long[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == NumberType.I64; + this.array = array; + } + + public WasmInt64Array(DefinedType type, int length, long initialValue) { + this(type, new long[length]); + fill(0, length, initialValue); + } + + public WasmInt64Array(DefinedType type, int length) { + this(type, new long[length]); + } + + public WasmInt64Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new long[length]); + initialize(source, srcOffset, 0, length); + } + + public long get(int index) { + return array[index]; + } + + public void set(int index, long value) { + array[index] = value; + } + + public void copyFrom(WasmInt64Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, long value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + for (int i = 0; i < length; i++) { + array[dstOffset + i] = byteArraySupport.getLong(source, srcOffset + (i << 3)); + } + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Long longValue) { + array[index] = longValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt8Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt8Array.java new file mode 100644 index 000000000000..60e9aa07c58b --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmInt8Array.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.PackedType; + +import java.util.Arrays; + +public final class WasmInt8Array extends WasmArray { + + private final byte[] array; + + public WasmInt8Array(DefinedType type, byte[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == PackedType.I8; + this.array = array; + } + + public WasmInt8Array(DefinedType type, int length, byte initialValue) { + this(type, new byte[length]); + fill(0, length, initialValue); + } + + public WasmInt8Array(DefinedType type, int length) { + this(type, new byte[length]); + } + + public WasmInt8Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new byte[length]); + initialize(source, srcOffset, 0, length); + } + + public byte get(int index) { + return array[index]; + } + + public void set(int index, byte value) { + array[index] = value; + } + + public void copyFrom(WasmInt8Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, byte value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + System.arraycopy(source, srcOffset, array, dstOffset, length); + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Byte byteValue) { + array[index] = byteValue; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmRefArray.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmRefArray.java new file mode 100644 index 000000000000..59b54a162a72 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmRefArray.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.WasmConstant; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.ReferenceType; + +import java.util.Arrays; + +public final class WasmRefArray extends WasmArray { + + private final Object[] array; + + public WasmRefArray(DefinedType type, Object[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() instanceof ReferenceType; + this.array = array; + } + + public WasmRefArray(DefinedType type, int length, Object initialValue) { + this(type, new Object[length]); + fill(0, length, initialValue); + } + + public WasmRefArray(DefinedType type, int length) { + this(type, new Object[length]); + Arrays.fill(array, WasmConstant.NULL); + } + + public WasmRefArray(DefinedType type, int length, Object[] source, int srcOffset) { + this(type, new Object[length]); + initialize(source, srcOffset, 0, length); + } + + public Object get(int index) { + return array[index]; + } + + public void set(int index, Object value) { + array[index] = value; + } + + public void copyFrom(WasmRefArray src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset, this.array, dstOffset, length); + } + + public void fill(int offset, int length, Object value) { + Arrays.fill(array, offset, offset + length, value); + } + + public void initialize(Object[] source, int srcOffset, int dstOffset, int length) { + System.arraycopy(source, srcOffset, array, dstOffset, length); + } + + @Override + public Object getObj(int index) { + return array[index]; + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (type().matchesValue(value)) { + array[index] = value; + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmVec128Array.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmVec128Array.java new file mode 100644 index 000000000000..042374a04b26 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/array/WasmVec128Array.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.array; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.VectorType; + +import java.util.Arrays; + +public final class WasmVec128Array extends WasmArray { + + private final byte[] array; + + public WasmVec128Array(DefinedType type, byte[] array) { + super(type, array.length); + assert type.asArrayType().fieldType().storageType() == VectorType.V128; + this.array = array; + } + + public WasmVec128Array(DefinedType type, int length, V128 initialValue, Vector128Ops vector128Ops) { + this(type, new byte[length << 4]); + fill(0, length, initialValue, vector128Ops); + } + + public WasmVec128Array(DefinedType type, int length, Vector128 initialValue) { + this(type, new byte[length << 4]); + for (int i = 0; i < length; i++) { + System.arraycopy(initialValue.getBytes(), 0, this.array, i << 4, 16); + } + } + + public WasmVec128Array(DefinedType type, int length) { + this(type, new byte[length << 4]); + } + + public WasmVec128Array(DefinedType type, int length, byte[] source, int srcOffset) { + this(type, new byte[length << 4]); + initialize(source, srcOffset, 0, length); + } + + public V128 get(int index, Vector128Ops vector128Ops) { + return vector128Ops.fromArray(array, index << 4); + } + + public void set(int index, V128 value, Vector128Ops vector128Ops) { + vector128Ops.intoArray(value, array, index << 4); + } + + public void copyFrom(WasmVec128Array src, int srcOffset, int dstOffset, int length) { + System.arraycopy(src.array, srcOffset << 4, this.array, dstOffset << 4, length << 4); + } + + public void fill(int offset, int length, V128 value, Vector128Ops vector128Ops) { + for (int i = offset; i < offset + length; i++) { + vector128Ops.intoArray(value, this.array, i << 4); + } + } + + public void initialize(byte[] source, int srcOffset, int dstOffset, int length) { + System.arraycopy(source, srcOffset, array, dstOffset << 4, length << 4); + } + + @Override + public Object getObj(int index) { + return new Vector128(Arrays.copyOfRange(array, index << 4, (index + 1) << 4)); + } + + @Override + public void setObj(int index, Object value) throws UnsupportedTypeException { + if (value instanceof Vector128 vec) { + System.arraycopy(vec.getBytes(), 0, array, index << 4, 16); + } else { + throw UnsupportedTypeException.create(new Object[]{value}); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/collection/ByteArrayList.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/collection/ByteArrayList.java index f377ae6d2331..fdd9f8220ab6 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/collection/ByteArrayList.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/collection/ByteArrayList.java @@ -105,6 +105,13 @@ public byte get(int index) { return array[index]; } + public void dropLast() { + if (size() == 0) { + throw new IndexOutOfBoundsException("Trying to drop last element of empty list."); + } + size--; + } + public int size() { return size; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Bytecode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Bytecode.java index ae0d5d040127..42d2d3f4a94b 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Bytecode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Bytecode.java @@ -336,11 +336,42 @@ public class Bytecode { public static final int CALL_REF_U8 = 0xF9; public static final int CALL_REF_I32 = 0xFA; - public static final int MISC = 0xFB; - - public static final int ATOMIC = 0xFC; + public static final int AGGREGATE = 0xFB; + public static final int MISC = 0xFC; public static final int VECTOR = 0xFD; - public static final int NOTIFY = 0xFE; + public static final int ATOMIC = 0xFE; + public static final int NOTIFY = 0xFF; + + // Aggregate opcodes + public static final int STRUCT_NEW = 0x00; + public static final int STRUCT_NEW_DEFAULT = 0x01; + public static final int STRUCT_GET = 0x02; + public static final int STRUCT_GET_S = 0x03; + public static final int STRUCT_GET_U = 0x04; + public static final int STRUCT_SET = 0x05; + public static final int ARRAY_NEW = 0x06; + public static final int ARRAY_NEW_DEFAULT = 0x07; + public static final int ARRAY_NEW_FIXED = 0x08; + public static final int ARRAY_NEW_DATA = 0x09; + public static final int ARRAY_NEW_ELEM = 0x0A; + public static final int ARRAY_GET = 0x0B; + public static final int ARRAY_GET_S = 0x0C; + public static final int ARRAY_GET_U = 0x0D; + public static final int ARRAY_SET = 0x0E; + public static final int ARRAY_LEN = 0x0F; + public static final int ARRAY_FILL = 0x10; + public static final int ARRAY_COPY = 0x11; + public static final int ARRAY_INIT_DATA = 0x12; + public static final int ARRAY_INIT_ELEM = 0x13; + public static final int REF_TEST = 0x14; + public static final int BR_ON_CAST_U8 = 0x15; + public static final int REF_CAST = 0x16; + public static final int BR_ON_CAST_I32 = 0x17; + public static final int BR_ON_CAST_FAIL_U8 = 0x18; + public static final int BR_ON_CAST_FAIL_I32 = 0x19; + public static final int REF_I31 = 0x1C; + public static final int I31_GET_S = 0x1D; + public static final int I31_GET_U = 0x1E; // Misc opcodes public static final int I32_TRUNC_SAT_F32_S = 0x00; @@ -371,11 +402,11 @@ public class Bytecode { public static final int TABLE_SIZE = 0x19; public static final int TABLE_FILL = 0x1A; - // Exception opcodes + // Misc - Exception opcodes public static final int THROW = 0x1B; public static final int THROW_REF = 0x1C; - // Typed function references opcodes + // Misc - Typed function references opcodes public static final int TABLE_GET = 0x1D; public static final int TABLE_SET = 0x1E; public static final int REF_AS_NON_NULL = 0x1F; @@ -384,6 +415,9 @@ public class Bytecode { public static final int BR_ON_NON_NULL_U8 = 0x22; public static final int BR_ON_NON_NULL_I32 = 0x23; + // Misc - GC opcodes + public static final int REF_EQ = 0x24; + // Atomic opcodes public static final int ATOMIC_I32_LOAD = 0x00; public static final int ATOMIC_I64_LOAD = 0x01; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/BytecodeBitEncoding.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/BytecodeBitEncoding.java index 9d1a247c6061..dbffcc702192 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/BytecodeBitEncoding.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/BytecodeBitEncoding.java @@ -146,13 +146,9 @@ public class BytecodeBitEncoding { // Elem items - public static final int ELEM_ITEM_REF_NULL_ENTRY_PREFIX = 0; - public static final int ELEM_ITEM_REF_FUNC_ENTRY_PREFIX = 1; - public static final int ELEM_ITEM_GLOBAL_GET_ENTRY_PREFIX = 2; - public static final int ELEM_ITEM_TYPE_MASK = 0b1000_0000; public static final int ELEM_ITEM_TYPE_FUNCTION_INDEX = 0b0000_0000; - public static final int ELEM_ITEM_TYPE_GLOBAL_INDEX = 0b1000_0000; + public static final int ELEM_ITEM_TYPE_BYTECODE = 0b1000_0000; public static final int ELEM_ITEM_LENGTH_MASK = 0b0110_0000; public static final int ELEM_ITEM_LENGTH_INLINE = 0b0000_0000; @@ -160,9 +156,8 @@ public class BytecodeBitEncoding { public static final int ELEM_ITEM_LENGTH_U16 = 0b0100_0000; public static final int ELEM_ITEM_LENGTH_I32 = 0b0110_0000; - public static final int ELEM_ITEM_NULL_FLAG = 0b0001_0000; - - public static final int ELEM_ITEM_INLINE_VALUE = 0b0000_1111; + public static final int ELEM_ITEM_INLINE_VALUE = 0b0001_1111; + public static final int ELEM_ITEM_MAX_INLINE_VALUE = ELEM_ITEM_INLINE_VALUE; // Code entries diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Instructions.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Instructions.java index 183245e9cdda..6d7fa6e79dfe 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Instructions.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Instructions.java @@ -54,7 +54,10 @@ public final class Instructions { public static final int IF = 0x04; public static final int ELSE = 0x05; + public static final int TRY = 0x06; // legacy exception handling + public static final int CATCH = 0x07; // legacy exception handling public static final int THROW = 0x08; + public static final int RETHROW = 0x09; // legacy exception handling public static final int THROW_REF = 0x0A; public static final int END = 0x0B; @@ -66,6 +69,10 @@ public final class Instructions { public static final int RETURN = 0x0F; public static final int CALL = 0x10; public static final int CALL_INDIRECT = 0x11; + public static final int CALL_REF = 0x14; + + public static final int DELEGATE = 0x18; // legacy exception handling + public static final int CATCH_ALL = 0x19; // legacy exception handling public static final int DROP = 0x1A; public static final int SELECT = 0x1B; @@ -245,6 +252,54 @@ public final class Instructions { public static final int F32_REINTERPRET_I32 = 0xBE; public static final int F64_REINTERPRET_I64 = 0xBF; + public static final int I32_EXTEND8_S = 0xC0; + public static final int I32_EXTEND16_S = 0xC1; + public static final int I64_EXTEND8_S = 0xC2; + public static final int I64_EXTEND16_S = 0xC3; + public static final int I64_EXTEND32_S = 0xC4; + + public static final int REF_NULL = 0xD0; + public static final int REF_IS_NULL = 0xD1; + public static final int REF_FUNC = 0xD2; + public static final int REF_EQ = 0xD3; + public static final int REF_AS_NON_NULL = 0xD4; + public static final int BR_ON_NULL = 0xD5; + public static final int BR_ON_NON_NULL = 0xD6; + + public static final int AGGREGATE = 0xFB; + + public static final int STRUCT_NEW = 0x00; + public static final int STRUCT_NEW_DEFAULT = 0x01; + public static final int STRUCT_GET = 0x02; + public static final int STRUCT_GET_S = 0x03; + public static final int STRUCT_GET_U = 0x04; + public static final int STRUCT_SET = 0x05; + public static final int ARRAY_NEW = 0x06; + public static final int ARRAY_NEW_DEFAULT = 0x07; + public static final int ARRAY_NEW_FIXED = 0x08; + public static final int ARRAY_NEW_DATA = 0x09; + public static final int ARRAY_NEW_ELEM = 0x0A; + public static final int ARRAY_GET = 0x0B; + public static final int ARRAY_GET_S = 0x0C; + public static final int ARRAY_GET_U = 0x0D; + public static final int ARRAY_SET = 0x0E; + public static final int ARRAY_LEN = 0x0F; + public static final int ARRAY_FILL = 0x10; + public static final int ARRAY_COPY = 0x11; + public static final int ARRAY_INIT_DATA = 0x12; + public static final int ARRAY_INIT_ELEM = 0x13; + public static final int REF_TEST_NON_NULL = 0x14; + public static final int REF_TEST_NULL = 0x15; + public static final int REF_CAST_NON_NULL = 0x16; + public static final int REF_CAST_NULL = 0x17; + public static final int BR_ON_CAST = 0x18; + public static final int BR_ON_CAST_FAIL = 0x19; + public static final int ANY_CONVERT_EXTERN = 0x1A; + public static final int EXTERN_CONVERT_ANY = 0x1B; + public static final int REF_I31 = 0x1C; + public static final int I31_GET_S = 0x1D; + public static final int I31_GET_U = 0x1E; + public static final int MISC = 0xFC; public static final int I32_TRUNC_SAT_F32_S = 0x00; @@ -256,16 +311,6 @@ public final class Instructions { public static final int I64_TRUNC_SAT_F64_S = 0x06; public static final int I64_TRUNC_SAT_F64_U = 0x07; - public static final int I32_EXTEND8_S = 0xC0; - public static final int I32_EXTEND16_S = 0xC1; - public static final int I64_EXTEND8_S = 0xC2; - public static final int I64_EXTEND16_S = 0xC3; - public static final int I64_EXTEND32_S = 0xC4; - - public static final int REF_NULL = 0xD0; - public static final int REF_IS_NULL = 0xD1; - public static final int REF_FUNC = 0xD2; - public static final int MEMORY_INIT = 0x08; public static final int DATA_DROP = 0x09; public static final int MEMORY_COPY = 0x0A; @@ -277,11 +322,6 @@ public final class Instructions { public static final int TABLE_SIZE = 0x10; public static final int TABLE_FILL = 0x11; - public static final int CALL_REF = 0x14; - public static final int REF_AS_NON_NULL = 0xD4; - public static final int BR_ON_NULL = 0xD5; - public static final int BR_ON_NON_NULL = 0xD6; - public static final int ATOMIC = 0xFE; public static final int ATOMIC_NOTIFY = 0x00; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/GlobalModifier.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Mutability.java similarity index 96% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/GlobalModifier.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Mutability.java index 961702377c76..8809d6b126bf 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/GlobalModifier.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/Mutability.java @@ -45,7 +45,7 @@ import com.oracle.truffle.api.CompilerAsserts; -public final class GlobalModifier { +public final class Mutability { public static final byte CONSTANT = 0x00; public static final byte MUTABLE = 0x01; @@ -55,12 +55,12 @@ public static String asString(int modifier) { case CONSTANT: return "const"; case MUTABLE: - return "var"; + return "mut"; default: throw WasmException.create(Failure.UNSPECIFIED_INVALID, "Unknown modifier: 0x" + Integer.toHexString(modifier)); } } - private GlobalModifier() { + private Mutability() { } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/StackEffects.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/StackEffects.java index 710f3bf543c2..4e8d96e512a3 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/StackEffects.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/constants/StackEffects.java @@ -56,12 +56,47 @@ public final class StackEffects { private static final byte POP_1 = -1; private static final byte POP_2 = -2; private static final byte POP_3 = -3; + private static final byte POP_4 = -4; + private static final byte POP_5 = -5; private static final byte UNREACHABLE = Byte.MIN_VALUE; + @CompilationFinal(dimensions = 1) private static final byte[] aggregateOpStackEffects = new byte[256]; @CompilationFinal(dimensions = 1) private static final byte[] miscOpStackEffects = new byte[256]; @CompilationFinal(dimensions = 1) private static final byte[] vectorOpStackEffects = new byte[256]; static { + // unused, because stack effect is variable + aggregateOpStackEffects[Bytecode.STRUCT_NEW] = UNREACHABLE; + aggregateOpStackEffects[Bytecode.STRUCT_NEW_DEFAULT] = PUSH_1; + aggregateOpStackEffects[Bytecode.STRUCT_GET] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.STRUCT_GET_S] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.STRUCT_GET_U] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.STRUCT_SET] = POP_2; + aggregateOpStackEffects[Bytecode.ARRAY_NEW] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_NEW_DEFAULT] = NO_EFFECT; + // unused, because stack effect is variable + aggregateOpStackEffects[Bytecode.ARRAY_NEW_FIXED] = UNREACHABLE; + aggregateOpStackEffects[Bytecode.ARRAY_NEW_DATA] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_NEW_ELEM] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_GET] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_GET_S] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_GET_U] = POP_1; + aggregateOpStackEffects[Bytecode.ARRAY_SET] = POP_3; + aggregateOpStackEffects[Bytecode.ARRAY_LEN] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.ARRAY_FILL] = POP_4; + aggregateOpStackEffects[Bytecode.ARRAY_COPY] = POP_5; + aggregateOpStackEffects[Bytecode.ARRAY_INIT_DATA] = POP_4; + aggregateOpStackEffects[Bytecode.ARRAY_INIT_ELEM] = POP_4; + aggregateOpStackEffects[Bytecode.REF_TEST] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.BR_ON_CAST_U8] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.REF_CAST] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.BR_ON_CAST_I32] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.BR_ON_CAST_FAIL_U8] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.BR_ON_CAST_FAIL_I32] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.REF_I31] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.I31_GET_S] = NO_EFFECT; + aggregateOpStackEffects[Bytecode.I31_GET_U] = NO_EFFECT; + miscOpStackEffects[Bytecode.I32_TRUNC_SAT_F32_S] = NO_EFFECT; miscOpStackEffects[Bytecode.I32_TRUNC_SAT_F32_U] = NO_EFFECT; miscOpStackEffects[Bytecode.I32_TRUNC_SAT_F64_S] = NO_EFFECT; @@ -87,21 +122,22 @@ public final class StackEffects { miscOpStackEffects[Bytecode.TABLE_GROW] = POP_1; miscOpStackEffects[Bytecode.TABLE_SIZE] = PUSH_1; miscOpStackEffects[Bytecode.TABLE_FILL] = POP_3; - miscOpStackEffects[Bytecode.THROW] = UNREACHABLE; // unused, because stack effect is - // followed by throw - miscOpStackEffects[Bytecode.THROW_REF] = UNREACHABLE; // unused, because stack effect is - // followed by throw + // unused, because stack effect is followed by throw + miscOpStackEffects[Bytecode.THROW] = UNREACHABLE; + // unused, because stack effect is followed by throw + miscOpStackEffects[Bytecode.THROW_REF] = UNREACHABLE; miscOpStackEffects[Bytecode.TABLE_GET] = NO_EFFECT; miscOpStackEffects[Bytecode.TABLE_SET] = POP_2; miscOpStackEffects[Bytecode.REF_AS_NON_NULL] = NO_EFFECT; - miscOpStackEffects[Bytecode.BR_ON_NULL_U8] = UNREACHABLE; // unused, because stack effect is - // dynamic - miscOpStackEffects[Bytecode.BR_ON_NULL_I32] = UNREACHABLE; // unused, because stack effect - // is dynamic - miscOpStackEffects[Bytecode.BR_ON_NON_NULL_U8] = UNREACHABLE; // unused, because stack - // effect is dynamic - miscOpStackEffects[Bytecode.BR_ON_NON_NULL_I32] = UNREACHABLE; // unused, because stack - // effect is dynamic + // unused, because stack effect is dynamic + miscOpStackEffects[Bytecode.BR_ON_NULL_U8] = UNREACHABLE; + // unused, because stack effect is dynamic + miscOpStackEffects[Bytecode.BR_ON_NULL_I32] = UNREACHABLE; + // unused, because stack effect is dynamic + miscOpStackEffects[Bytecode.BR_ON_NON_NULL_U8] = UNREACHABLE; + // unused, because stack effect is dynamic + miscOpStackEffects[Bytecode.BR_ON_NON_NULL_I32] = UNREACHABLE; + miscOpStackEffects[Bytecode.REF_EQ] = POP_1; vectorOpStackEffects[Bytecode.VECTOR_V128_LOAD] = NO_EFFECT; vectorOpStackEffects[Bytecode.VECTOR_V128_LOAD8X8_S] = NO_EFFECT; @@ -364,6 +400,20 @@ public final class StackEffects { private StackEffects() { } + /** + * Indicates by how much the stack grows (positive return value) or shrinks (negative return + * value) after executing the {@link Bytecode#AGGREGATE}-prefixed bytecode + * {@code aggregateOpcode}. + * + * @param aggregateOpcode the {@code AGGREGATE} bytecode being executed + * @return the difference between the stack size after executing the bytecode and before + * executing the bytecode + */ + public static byte getAggregateOpStackEffect(int aggregateOpcode) { + assert aggregateOpcode < 256; + return aggregateOpStackEffects[aggregateOpcode]; + } + /** * Indicates by how much the stack grows (positive return value) or shrinks (negative return * value) after executing the {@link Bytecode#MISC}-prefixed bytecode {@code miscOpcode}. diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java index 14a28d8591cb..08d49ecf8a5c 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/Failure.java @@ -70,6 +70,7 @@ public enum Failure { MALFORMED_MEMOP_FLAGS(Type.MALFORMED, "malformed memop flags"), MALFORMED_CATCH(Type.MALFORMED, "malformed catch clause"), MALFORMED_TAG_ATTRIBUTE(Type.MALFORMED, "malformed tag attribute"), + MALFORMED_CASTOP_FLAGS(Type.MALFORMED, "malformed castop flags"), // GraalWasm-specific: INVALID_SECTION_ORDER(Type.MALFORMED, "invalid section order"), DISABLED_MULTI_VALUE(Type.MALFORMED, "multi-value is not enabled"), @@ -90,6 +91,8 @@ public enum Failure { UNKNOWN_LABEL(Type.INVALID, "unknown label"), UNKNOWN_FUNCTION(Type.INVALID, "unknown function"), UNKNOWN_TYPE(Type.INVALID, "unknown type"), + SUB_TYPE(Type.INVALID, "sub type"), + SUB_TYPE_DOES_NOT_MATCH_SUPER_TYPE(Type.INVALID, "sub type does not match super type"), START_FUNCTION_RESULT_VALUE(Type.INVALID, "start function"), START_FUNCTION_PARAMS(Type.INVALID, "start function"), LIMIT_MINIMUM_GREATER_THAN_MAXIMUM(Type.INVALID, "size minimum must not be greater than maximum"), @@ -108,10 +111,19 @@ public enum Failure { UNKNOWN_REFERENCE(Type.INVALID, "unknown reference"), UNDECLARED_FUNCTION_REFERENCE(Type.INVALID, "undeclared function reference"), UNKNOWN_TAG(Type.INVALID, "unknown tag"), + INVALID_FIELD_INDEX(Type.INVALID, "invalid field index"), + INVALID_STRUCT_GETTER_SIGNEDNESS(Type.INVALID, "struct.get_s and struct.get_u must be used for fields of packed types"), + FIELD_IS_IMMUTABLE(Type.INVALID, "field is immutable"), + TOO_MANY_STRUCT_FIELDS(Type.INVALID, "too many struct fields"), + ARRAY_TYPE_IS_NOT_NUMERIC_OR_VECTOR(Type.INVALID, "array type is not numeric or vector"), + INVALID_ARRAY_GETTER_SIGNEDNESS(Type.INVALID, "array.get_s and array.get_u must be used for arrays of packed types"), + ARRAY_IS_IMMUTABLE(Type.INVALID, "array is immutable"), + ARRAY_TYPES_DO_NOT_MATCH(Type.INVALID, "array types do not match"), // GraalWasm-specific: MODULE_SIZE_LIMIT_EXCEEDED(Type.INVALID, "module size exceeds limit"), TYPE_COUNT_LIMIT_EXCEEDED(Type.INVALID, "type count exceeds limit"), + SUB_TYPE_DEPTH_LIMIT_EXCEEDED(Type.INVALID, "sub type depth exceeds limit"), FUNCTION_COUNT_LIMIT_EXCEEDED(Type.INVALID, "function count exceeds limit"), TABLE_COUNT_LIMIT_EXCEEDED(Type.INVALID, "table count exceeds limit"), MEMORY_COUNT_LIMIT_EXCEEDED(Type.INVALID, "memory count exceeds limit"), @@ -124,6 +136,7 @@ public enum Failure { PARAMETERS_COUNT_LIMIT_EXCEEDED(Type.INVALID, "parameters count exceeds limit"), RESULT_COUNT_LIMIT_EXCEEDED(Type.INVALID, "result values count exceeds limit"), TAG_COUNT_LIMIT_EXCEEDED(Type.INVALID, "tag count exceeds limit"), + ARRAY_NEW_FIXED_LIMIT_EXCEEDED(Type.INVALID, "array.new_fixed length exceeds limit"), // TODO(mbovel): replace UNSPECIFIED_UNLINKABLE usages with appropriate errors. UNSPECIFIED_UNLINKABLE(Type.UNLINKABLE, "unspecified"), @@ -146,10 +159,15 @@ public enum Failure { INDIRECT_CALL_TYPE_MISMATCH(Type.TRAP, "indirect call type mismatch"), INVALID_MULTI_VALUE_ARITY(Type.TRAP, "provided multi-value size does not match function type"), INVALID_TYPE_IN_MULTI_VALUE(Type.TRAP, "type of value in multi-value does not match the function type"), + CAST_FAILURE(Type.TRAP, "cast failure"), NULL_REFERENCE(Type.TRAP, "null reference"), NULL_FUNCTION_REFERENCE(Type.TRAP, "null function reference"), + NULL_STRUCTURE_REFERENCE(Type.TRAP, "null structure reference"), + NULL_ARRAY_REFERENCE(Type.TRAP, "null array reference"), + NULL_I31_REFERENCE(Type.TRAP, "null i31 reference"), OUT_OF_BOUNDS_TABLE_ACCESS(Type.TRAP, "out of bounds table access"), + OUT_OF_BOUNDS_ARRAY_ACCESS(Type.TRAP, "out of bounds array access"), // GraalWasm-specific: TABLE_INSTANCE_SIZE_LIMIT_EXCEEDED(Type.TRAP, "table instance size exceeds limit"), MEMORY_INSTANCE_SIZE_LIMIT_EXCEEDED(Type.TRAP, "memory instance size exceeds limit"), diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/WasmJsApiException.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/WasmJsApiException.java index 45dfa81237d8..0eb4df36003a 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/WasmJsApiException.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/exception/WasmJsApiException.java @@ -45,15 +45,14 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.exception.AbstractTruffleException; +import org.graalvm.wasm.WasmType; +import org.graalvm.wasm.types.ValueType; /** * Thrown for various error condition when using the Wasm-JS API. */ public class WasmJsApiException extends AbstractTruffleException { - public static final String V128_VALUE_ACCESS = "Invalid value type. Accessing v128 values from JS is not allowed."; - public static final String EXNREF_VALUE_ACCESS = "Invalid value type. Accessing exnref values from JS is not allowed."; - public enum Kind { TypeError, RangeError, @@ -97,6 +96,20 @@ public static WasmJsApiException format(WasmJsApiException.Kind kind, String s, return new WasmJsApiException(kind, String.format(Locale.ROOT, s, args)); } + @TruffleBoundary + public static WasmJsApiException invalidValueType(int valueType) { + if (WasmType.isConcreteReferenceType(valueType)) { + return WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Typed references are not allowed in JS."); + } else { + return WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Accessing %s values from JS is not allowed.", WasmType.toString(valueType)); + } + } + + @TruffleBoundary + public static WasmJsApiException invalidValueType(ValueType valueType) { + return WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Accessing %s values from JS is not allowed.", valueType.toString()); + } + public static ExceptionProvider provider() { return ExceptionProviders.WASM_JS_API_EXCEPTION_PROVIDER; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/globals/WasmGlobal.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/globals/WasmGlobal.java index a3ce0b467feb..0ff87a1d48f4 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/globals/WasmGlobal.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/globals/WasmGlobal.java @@ -43,11 +43,12 @@ import org.graalvm.wasm.EmbedderDataHolder; import org.graalvm.wasm.SymbolTable; +import org.graalvm.wasm.WasmConstant; import org.graalvm.wasm.WasmNamesObject; import org.graalvm.wasm.WasmType; import org.graalvm.wasm.api.ValueType; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.constants.Mutability; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; @@ -60,7 +61,7 @@ @SuppressWarnings("static-method") @ExportLibrary(InteropLibrary.class) -public final class WasmGlobal extends EmbedderDataHolder implements TruffleObject { +public final class WasmGlobal implements TruffleObject, EmbedderDataHolder { private final int type; private final boolean mutable; @@ -69,6 +70,8 @@ public final class WasmGlobal extends EmbedderDataHolder implements TruffleObjec private long globalValue; private Object globalObjectValue; + private Object embedderData = WasmConstant.VOID; + private WasmGlobal(int type, boolean mutable, SymbolTable symbolTable) { this.type = type; this.mutable = mutable; @@ -110,7 +113,7 @@ public int getType() { return type; } - public SymbolTable.ClosedValueType getClosedType() { + public org.graalvm.wasm.types.ValueType getClosedType() { return SymbolTable.closedTypeOf(getType(), symbolTable); } @@ -119,7 +122,7 @@ public boolean isMutable() { } public byte getMutability() { - return mutable ? GlobalModifier.MUTABLE : GlobalModifier.CONSTANT; + return mutable ? Mutability.MUTABLE : Mutability.CONSTANT; } public int loadAsInt() { @@ -246,4 +249,14 @@ void writeMember(String member, Object value, Object getMembers(@SuppressWarnings("unused") boolean includeInternal) { return new WasmNamesObject(new String[]{VALUE_MEMBER}); } + + @Override + public Object getEmbedderData() { + return embedderData; + } + + @Override + public void setEmbedderData(Object embedderData) { + this.embedderData = embedderData; + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java index 450dccae2bdc..466c50bcee77 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/ByteArrayWasmMemory.java @@ -50,8 +50,8 @@ import java.io.OutputStream; import java.util.Arrays; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/NativeWasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/NativeWasmMemory.java index c7b581f373e3..c5c932384448 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/NativeWasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/NativeWasmMemory.java @@ -51,8 +51,8 @@ import org.graalvm.wasm.MemoryContext; import org.graalvm.wasm.WasmMath; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; import org.graalvm.wasm.constants.Sizes; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java index 93140f3cc615..19558f5908e8 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/UnsafeWasmMemory.java @@ -54,8 +54,8 @@ import java.nio.Buffer; import java.nio.ByteBuffer; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/WasmMemory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/WasmMemory.java index c49537215cb3..7830577f3a14 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/WasmMemory.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/memory/WasmMemory.java @@ -52,6 +52,7 @@ import java.util.Locale; import org.graalvm.wasm.EmbedderDataHolder; +import org.graalvm.wasm.WasmConstant; import org.graalvm.wasm.api.WebAssembly; import org.graalvm.wasm.collection.ByteArrayList; import org.graalvm.wasm.constants.Sizes; @@ -77,7 +78,7 @@ import com.oracle.truffle.api.profiles.InlinedBranchProfile; @ExportLibrary(InteropLibrary.class) -public abstract class WasmMemory extends EmbedderDataHolder implements TruffleObject { +public abstract class WasmMemory implements TruffleObject, EmbedderDataHolder { /** * @see #declaredMinSize() @@ -115,6 +116,8 @@ public abstract class WasmMemory extends EmbedderDataHolder implements TruffleOb */ protected final boolean shared; + private Object embedderData = WasmConstant.VOID; + @TruffleBoundary protected WasmMemory(long declaredMinSize, long declaredMaxSize, long initialSize, long maxAllowedSize, boolean indexType64, boolean shared) { assertUnsignedLongLessOrEqual(initialSize, maxAllowedSize, Failure.MEMORY_SIZE_LIMIT_EXCEEDED, "Initial memory size exceeds implementation limit"); @@ -660,4 +663,14 @@ public final WasmMemory checkSize(WasmMemoryLibrary memoryLib, long initialSize) } return this; } + + @Override + public Object getEmbedderData() { + return embedderData; + } + + @Override + public void setEmbedderData(Object embedderData) { + this.embedderData = embedderData; + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFrame.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFrame.java index 502294e9ccf9..c3d494f5df33 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFrame.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFrame.java @@ -42,7 +42,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.VirtualFrame; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128Ops; public abstract class WasmFrame { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java index a9a81e0a73a0..6d7e877bb396 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionNode.java @@ -65,13 +65,13 @@ import org.graalvm.wasm.BinaryStreamParser; import org.graalvm.wasm.GlobalRegistry; -import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmArguments; import org.graalvm.wasm.WasmCodeEntry; import org.graalvm.wasm.WasmConstant; import org.graalvm.wasm.WasmContext; import org.graalvm.wasm.WasmFunction; import org.graalvm.wasm.WasmFunctionInstance; +import org.graalvm.wasm.WasmTypedHeapObject; import org.graalvm.wasm.WasmInstance; import org.graalvm.wasm.WasmLanguage; import org.graalvm.wasm.WasmMath; @@ -79,8 +79,17 @@ import org.graalvm.wasm.WasmTable; import org.graalvm.wasm.WasmTag; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; +import org.graalvm.wasm.array.WasmArray; +import org.graalvm.wasm.array.WasmFloat32Array; +import org.graalvm.wasm.array.WasmFloat64Array; +import org.graalvm.wasm.array.WasmInt16Array; +import org.graalvm.wasm.array.WasmInt32Array; +import org.graalvm.wasm.array.WasmInt64Array; +import org.graalvm.wasm.array.WasmInt8Array; +import org.graalvm.wasm.array.WasmRefArray; +import org.graalvm.wasm.array.WasmVec128Array; import org.graalvm.wasm.constants.Bytecode; import org.graalvm.wasm.constants.BytecodeBitEncoding; import org.graalvm.wasm.constants.ExceptionHandlerType; @@ -90,6 +99,9 @@ import org.graalvm.wasm.exception.WasmRuntimeException; import org.graalvm.wasm.memory.WasmMemory; import org.graalvm.wasm.memory.WasmMemoryLibrary; +import org.graalvm.wasm.struct.WasmStruct; +import org.graalvm.wasm.struct.WasmStructAccess; +import org.graalvm.wasm.types.DefinedType; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; @@ -106,6 +118,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.staticobject.StaticProperty; /** * This node represents the function body of a WebAssembly function. It executes the instruction @@ -553,8 +566,6 @@ public Object executeBodyFromOffset(WasmInstance instance, VirtualFrame frame, i case Bytecode.CALL_INDIRECT_I32: case Bytecode.CALL_REF_U8: case Bytecode.CALL_REF_I32: { - final SymbolTable symtab = module.symbolTable(); - final int callNodeIndex; final int expectedFunctionTypeIndex; final int tableIndex; @@ -614,16 +625,10 @@ public Object executeBodyFromOffset(WasmInstance instance, VirtualFrame frame, i if (opcode == Bytecode.CALL_INDIRECT_U8 || opcode == Bytecode.CALL_INDIRECT_I32) { // Validate that the target function type matches the expected type of - // the indirect call. We first try if the types are equivalent using the - // equivalence classes... - if (symtab.equivalenceClass(expectedFunctionTypeIndex) != function.typeEquivalenceClass()) { - codeEntry.subtypingBranch(); - // If they are not equivalent, we run the full subtype matching - // procedure. - if (!symtab.closedTypeAt(expectedFunctionTypeIndex).isSupertypeOf(function.closedType())) { - enterErrorBranch(); - failFunctionTypeCheck(function, expectedFunctionTypeIndex); - } + // the indirect call. + if (!runTimeConcreteTypeCheck(expectedFunctionTypeIndex, functionInstance)) { + enterErrorBranch(); + failFunctionTypeCheck(function, expectedFunctionTypeIndex); } } @@ -1539,6 +1544,45 @@ public Object executeBodyFromOffset(WasmInstance instance, VirtualFrame frame, i offset += 4; break; } + case Bytecode.AGGREGATE: { + final int aggregateOpcode = rawPeekU8(bytecode, offset); + offset++; + CompilerAsserts.partialEvaluationConstant(aggregateOpcode); + switch (aggregateOpcode) { + case Bytecode.STRUCT_NEW: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final WasmStructAccess structAccess = module.structTypeAccess(structTypeIdx); + final int numFields = module.structTypeFieldCount(structTypeIdx); + + WasmStruct struct = structAccess.shape().getFactory().create(module.closedTypeAt(structTypeIdx)); + popStructFields(frame, structTypeIdx, struct, structAccess, numFields, stackPointer); + stackPointer -= numFields; + WasmFrame.pushReference(frame, stackPointer++, struct); + break; + } + case Bytecode.ARRAY_NEW_FIXED: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + final int length = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final DefinedType arrayType = module.closedTypeAt(arrayTypeIdx); + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + + WasmArray array = popArrayElements(frame, arrayType, elemType, length, stackPointer); + stackPointer -= length; + WasmFrame.pushReference(frame, stackPointer++, array); + break; + } + default: { + offset = executeAggregate(instance, frame, offset, stackPointer, aggregateOpcode); + stackPointer += StackEffects.getAggregateOpStackEffect(aggregateOpcode); + break; + } + } + break; + } case Bytecode.MISC: { final int miscOpcode = rawPeekU8(bytecode, offset); offset++; @@ -1570,8 +1614,7 @@ public Object executeBodyFromOffset(WasmInstance instance, VirtualFrame frame, i if (profileCondition(bytecode, offset + 1, reference == WasmConstant.NULL)) { final int offsetDelta = rawPeekU8(bytecode, offset); // BR_ON_NULL_U8 encodes the back jump value as a positive byte - // value. BR_ON_NULL_U8 - // can never perform a forward jump. + // value. BR_ON_NULL_U8 can never perform a forward jump. offset -= offsetDelta; } else { offset += 3; @@ -2121,6 +2164,527 @@ private void executeMemoryFill(WasmInstance instance, VirtualFrame frame, int st memory_fill(instance, n, val, dst, memoryIndex); } + private int executeAggregate(WasmInstance instance, VirtualFrame frame, int startingOffset, int startingStackPointer, int aggregateOpcode) { + int offset = startingOffset; + int stackPointer = startingStackPointer; + + CompilerAsserts.partialEvaluationConstant(aggregateOpcode); + switch (aggregateOpcode) { + case Bytecode.STRUCT_NEW_DEFAULT: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + WasmStruct struct = module.structTypeAccess(structTypeIdx).shape().getFactory().create(module.closedTypeAt(structTypeIdx)); + WasmFrame.pushReference(frame, stackPointer, struct); + stackPointer += 1; + break; + } + case Bytecode.STRUCT_GET: + case Bytecode.STRUCT_GET_S: + case Bytecode.STRUCT_GET_U: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + final int fieldIdx = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final StaticProperty property = module.structTypeAccess(structTypeIdx).properties()[fieldIdx]; + final int fieldType = module.structTypeFieldTypeAt(structTypeIdx, fieldIdx); + CompilerAsserts.partialEvaluationConstant(fieldType); + + Object struct = WasmFrame.popReference(frame, stackPointer - 1); + if (struct == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_STRUCTURE_REFERENCE, this); + } + switch (fieldType) { + case WasmType.I8_TYPE -> + WasmFrame.pushInt(frame, stackPointer - 1, aggregateOpcode == Bytecode.STRUCT_GET_S ? property.getByte(struct) : Byte.toUnsignedInt(property.getByte(struct))); + case WasmType.I16_TYPE -> + WasmFrame.pushInt(frame, stackPointer - 1, aggregateOpcode == Bytecode.STRUCT_GET_S ? property.getShort(struct) : Short.toUnsignedInt(property.getShort(struct))); + case WasmType.I32_TYPE -> WasmFrame.pushInt(frame, stackPointer - 1, property.getInt(struct)); + case WasmType.I64_TYPE -> WasmFrame.pushLong(frame, stackPointer - 1, property.getLong(struct)); + case WasmType.F32_TYPE -> WasmFrame.pushFloat(frame, stackPointer - 1, property.getFloat(struct)); + case WasmType.F64_TYPE -> WasmFrame.pushDouble(frame, stackPointer - 1, property.getDouble(struct)); + case WasmType.V128_TYPE -> WasmFrame.pushVector128(frame, stackPointer - 1, vector128Ops().fromVector128((Vector128) property.getObject(struct))); + default -> { + assert WasmType.isReferenceType(fieldType); + WasmFrame.pushReference(frame, stackPointer - 1, property.getObject(struct)); + } + } + break; + } + case Bytecode.STRUCT_SET: { + final int structTypeIdx = rawPeekI32(bytecode, offset); + final int fieldIdx = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final StaticProperty property = module.structTypeAccess(structTypeIdx).properties()[fieldIdx]; + final int fieldType = module.structTypeFieldTypeAt(structTypeIdx, fieldIdx); + CompilerAsserts.partialEvaluationConstant(fieldType); + + Object struct = WasmFrame.popReference(frame, stackPointer - 2); + if (struct == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_STRUCTURE_REFERENCE, this); + } + switch (fieldType) { + case WasmType.I8_TYPE -> property.setByte(struct, (byte) WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I16_TYPE -> property.setShort(struct, (short) WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I32_TYPE -> property.setInt(struct, WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I64_TYPE -> property.setLong(struct, WasmFrame.popLong(frame, stackPointer - 1)); + case WasmType.F32_TYPE -> property.setFloat(struct, WasmFrame.popFloat(frame, stackPointer - 1)); + case WasmType.F64_TYPE -> property.setDouble(struct, WasmFrame.popDouble(frame, stackPointer - 1)); + case WasmType.V128_TYPE -> property.setObject(struct, vector128Ops().toVector128(WasmFrame.popVector128(frame, stackPointer - 1))); + default -> { + assert WasmType.isReferenceType(fieldType); + property.setObject(struct, WasmFrame.popReference(frame, stackPointer - 1)); + } + } + stackPointer -= 2; + break; + } + case Bytecode.ARRAY_NEW: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final DefinedType arrayType = module.closedTypeAt(arrayTypeIdx); + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> { + byte initialValue = (byte) WasmFrame.popInt(frame, stackPointer - 2); + yield new WasmInt8Array(arrayType, length, initialValue); + } + case WasmType.I16_TYPE -> { + short initialValue = (short) WasmFrame.popInt(frame, stackPointer - 2); + yield new WasmInt16Array(arrayType, length, initialValue); + } + case WasmType.I32_TYPE -> { + int initialValue = WasmFrame.popInt(frame, stackPointer - 2); + yield new WasmInt32Array(arrayType, length, initialValue); + } + case WasmType.I64_TYPE -> { + long initialValue = WasmFrame.popLong(frame, stackPointer - 2); + yield new WasmInt64Array(arrayType, length, initialValue); + } + case WasmType.F32_TYPE -> { + float initialValue = WasmFrame.popFloat(frame, stackPointer - 2); + yield new WasmFloat32Array(arrayType, length, initialValue); + } + case WasmType.F64_TYPE -> { + double initialValue = WasmFrame.popDouble(frame, stackPointer - 2); + yield new WasmFloat64Array(arrayType, length, initialValue); + } + case WasmType.V128_TYPE -> { + V128 initialValue = WasmFrame.popVector128(frame, stackPointer - 2); + yield new WasmVec128Array(arrayType, length, initialValue, vector128Ops()); + } + default -> { + Object initialValue = WasmFrame.popReference(frame, stackPointer - 2); + yield new WasmRefArray(arrayType, length, initialValue); + } + }; + WasmFrame.pushReference(frame, stackPointer - 2, array); + stackPointer -= 1; + break; + } + case Bytecode.ARRAY_NEW_DEFAULT: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final DefinedType arrayType = module.closedTypeAt(arrayTypeIdx); + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> new WasmInt8Array(arrayType, length); + case WasmType.I16_TYPE -> new WasmInt16Array(arrayType, length); + case WasmType.I32_TYPE -> new WasmInt32Array(arrayType, length); + case WasmType.I64_TYPE -> new WasmInt64Array(arrayType, length); + case WasmType.F32_TYPE -> new WasmFloat32Array(arrayType, length); + case WasmType.F64_TYPE -> new WasmFloat64Array(arrayType, length); + case WasmType.V128_TYPE -> new WasmVec128Array(arrayType, length); + default -> new WasmRefArray(arrayType, length); + }; + WasmFrame.pushReference(frame, stackPointer - 1, array); + break; + } + case Bytecode.ARRAY_NEW_DATA: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + final int dataIdx = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final DefinedType arrayType = module.closedTypeAt(arrayTypeIdx); + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + final int dataOffset = instance.dataInstanceOffset(dataIdx); + final int dataLength = instance.dataInstanceLength(dataIdx); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int source = WasmFrame.popInt(frame, stackPointer - 2); + if (checkOutOfBounds(source, length * WasmType.storageByteSize(elemType), dataLength)) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_MEMORY_ACCESS); + } + WasmArray array = switch (elemType) { + case WasmType.I8_TYPE -> new WasmInt8Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.I16_TYPE -> new WasmInt16Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.I32_TYPE -> new WasmInt32Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.I64_TYPE -> new WasmInt64Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.F32_TYPE -> new WasmFloat32Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.F64_TYPE -> new WasmFloat64Array(arrayType, length, bytecode, dataOffset + source); + case WasmType.V128_TYPE -> new WasmVec128Array(arrayType, length, bytecode, dataOffset + source); + default -> throw CompilerDirectives.shouldNotReachHere("array with ref element type used in array.new_data"); + }; + WasmFrame.pushReference(frame, stackPointer - 2, array); + stackPointer -= 1; + break; + } + case Bytecode.ARRAY_NEW_ELEM: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + final int elemIdx = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final DefinedType arrayType = module.closedTypeAt(arrayTypeIdx); + final Object[] elemInstance = instance.elemInstance(elemIdx); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int source = WasmFrame.popInt(frame, stackPointer - 2); + if (checkOutOfBounds(source, length, elemInstance == null ? 0 : elemInstance.length)) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_TABLE_ACCESS); + } + WasmRefArray array = length == 0 ? new WasmRefArray(arrayType, 0) : new WasmRefArray(arrayType, length, elemInstance, source); + WasmFrame.pushReference(frame, stackPointer - 2, array); + stackPointer -= 1; + break; + } + case Bytecode.ARRAY_GET: + case Bytecode.ARRAY_GET_S: + case Bytecode.ARRAY_GET_U: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int index = WasmFrame.popInt(frame, stackPointer - 1); + Object array = WasmFrame.popReference(frame, stackPointer - 2); + + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(index, ((WasmArray) array).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + switch (elemType) { + case WasmType.I8_TYPE -> { + byte packedValue = ((WasmInt8Array) array).get(index); + int unpackedValue = aggregateOpcode == Bytecode.ARRAY_GET_S ? packedValue : Byte.toUnsignedInt(packedValue); + WasmFrame.pushInt(frame, stackPointer - 2, unpackedValue); + } + case WasmType.I16_TYPE -> { + short packedValue = ((WasmInt16Array) array).get(index); + int unpackedValue = aggregateOpcode == Bytecode.ARRAY_GET_S ? packedValue : Short.toUnsignedInt(packedValue); + WasmFrame.pushInt(frame, stackPointer - 2, unpackedValue); + } + case WasmType.I32_TYPE -> WasmFrame.pushInt(frame, stackPointer - 2, ((WasmInt32Array) array).get(index)); + case WasmType.I64_TYPE -> WasmFrame.pushLong(frame, stackPointer - 2, ((WasmInt64Array) array).get(index)); + case WasmType.F32_TYPE -> WasmFrame.pushFloat(frame, stackPointer - 2, ((WasmFloat32Array) array).get(index)); + case WasmType.F64_TYPE -> WasmFrame.pushDouble(frame, stackPointer - 2, ((WasmFloat64Array) array).get(index)); + case WasmType.V128_TYPE -> WasmFrame.pushVector128(frame, stackPointer - 2, ((WasmVec128Array) array).get(index, vector128Ops())); + default -> WasmFrame.pushReference(frame, stackPointer - 2, ((WasmRefArray) array).get(index)); + } + stackPointer -= 1; + break; + } + case Bytecode.ARRAY_SET: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int index = WasmFrame.popInt(frame, stackPointer - 2); + Object array = WasmFrame.popReference(frame, stackPointer - 3); + + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(index, ((WasmArray) array).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + switch (elemType) { + case WasmType.I8_TYPE -> ((WasmInt8Array) array).set(index, (byte) WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I16_TYPE -> ((WasmInt16Array) array).set(index, (short) WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I32_TYPE -> ((WasmInt32Array) array).set(index, WasmFrame.popInt(frame, stackPointer - 1)); + case WasmType.I64_TYPE -> ((WasmInt64Array) array).set(index, WasmFrame.popLong(frame, stackPointer - 1)); + case WasmType.F32_TYPE -> ((WasmFloat32Array) array).set(index, WasmFrame.popFloat(frame, stackPointer - 1)); + case WasmType.F64_TYPE -> ((WasmFloat64Array) array).set(index, WasmFrame.popDouble(frame, stackPointer - 1)); + case WasmType.V128_TYPE -> ((WasmVec128Array) array).set(index, WasmFrame.popVector128(frame, stackPointer - 1), vector128Ops()); + default -> ((WasmRefArray) array).set(index, WasmFrame.popReference(frame, stackPointer - 1)); + } + stackPointer -= 3; + break; + } + case Bytecode.ARRAY_LEN: { + Object array = WasmFrame.popReference(frame, stackPointer - 1); + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + WasmFrame.pushInt(frame, stackPointer - 1, ((WasmArray) array).length()); + break; + } + case Bytecode.ARRAY_FILL: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int destination = WasmFrame.popInt(frame, stackPointer - 3); + Object array = WasmFrame.popReference(frame, stackPointer - 4); + + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(destination, length, ((WasmArray) array).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + switch (elemType) { + case WasmType.I8_TYPE -> { + byte fillValue = (byte) WasmFrame.popInt(frame, stackPointer - 2); + ((WasmInt8Array) array).fill(destination, length, fillValue); + } + case WasmType.I16_TYPE -> { + short fillValue = (short) WasmFrame.popInt(frame, stackPointer - 2); + ((WasmInt16Array) array).fill(destination, length, fillValue); + } + case WasmType.I32_TYPE -> { + int fillValue = WasmFrame.popInt(frame, stackPointer - 2); + ((WasmInt32Array) array).fill(destination, length, fillValue); + } + case WasmType.I64_TYPE -> { + long fillValue = WasmFrame.popLong(frame, stackPointer - 2); + ((WasmInt64Array) array).fill(destination, length, fillValue); + } + case WasmType.F32_TYPE -> { + float fillValue = WasmFrame.popFloat(frame, stackPointer - 2); + ((WasmFloat32Array) array).fill(destination, length, fillValue); + } + case WasmType.F64_TYPE -> { + double fillValue = WasmFrame.popDouble(frame, stackPointer - 2); + ((WasmFloat64Array) array).fill(destination, length, fillValue); + } + case WasmType.V128_TYPE -> { + V128 fillValue = WasmFrame.popVector128(frame, stackPointer - 2); + ((WasmVec128Array) array).fill(destination, length, fillValue, vector128Ops()); + } + default -> { + Object fillValue = WasmFrame.popReference(frame, stackPointer - 2); + ((WasmRefArray) array).fill(destination, length, fillValue); + } + } + stackPointer -= 4; + break; + } + case Bytecode.ARRAY_COPY: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int source = WasmFrame.popInt(frame, stackPointer - 2); + Object srcArray = WasmFrame.popReference(frame, stackPointer - 3); + int destination = WasmFrame.popInt(frame, stackPointer - 4); + Object dstArray = WasmFrame.popReference(frame, stackPointer - 5); + + if (srcArray == WasmConstant.NULL || dstArray == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(destination, length, ((WasmArray) dstArray).length()) || checkOutOfBounds(source, length, ((WasmArray) srcArray).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + switch (elemType) { + case WasmType.I8_TYPE -> ((WasmInt8Array) dstArray).copyFrom((WasmInt8Array) srcArray, source, destination, length); + case WasmType.I16_TYPE -> ((WasmInt16Array) dstArray).copyFrom((WasmInt16Array) srcArray, source, destination, length); + case WasmType.I32_TYPE -> ((WasmInt32Array) dstArray).copyFrom((WasmInt32Array) srcArray, source, destination, length); + case WasmType.I64_TYPE -> ((WasmInt64Array) dstArray).copyFrom((WasmInt64Array) srcArray, source, destination, length); + case WasmType.F32_TYPE -> ((WasmFloat32Array) dstArray).copyFrom((WasmFloat32Array) srcArray, source, destination, length); + case WasmType.F64_TYPE -> ((WasmFloat64Array) dstArray).copyFrom((WasmFloat64Array) srcArray, source, destination, length); + case WasmType.V128_TYPE -> ((WasmVec128Array) dstArray).copyFrom((WasmVec128Array) srcArray, source, destination, length); + default -> ((WasmRefArray) dstArray).copyFrom((WasmRefArray) srcArray, source, destination, length); + } + stackPointer -= 5; + break; + } + case Bytecode.ARRAY_INIT_DATA: { + final int arrayTypeIdx = rawPeekI32(bytecode, offset); + final int dataIdx = rawPeekI32(bytecode, offset + 4); + offset += 8; + + final int elemType = module.arrayTypeElemType(arrayTypeIdx); + CompilerAsserts.partialEvaluationConstant(elemType); + final int dataOffset = instance.dataInstanceOffset(dataIdx); + final int dataLength = instance.dataInstanceLength(dataIdx); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int source = WasmFrame.popInt(frame, stackPointer - 2); + int destination = WasmFrame.popInt(frame, stackPointer - 3); + Object array = WasmFrame.popReference(frame, stackPointer - 4); + + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(destination, length, ((WasmArray) array).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + if (checkOutOfBounds(source, length * WasmType.storageByteSize(elemType), dataLength)) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_MEMORY_ACCESS); + } + switch (elemType) { + case WasmType.I8_TYPE -> ((WasmInt8Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.I16_TYPE -> ((WasmInt16Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.I32_TYPE -> ((WasmInt32Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.I64_TYPE -> ((WasmInt64Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.F32_TYPE -> ((WasmFloat32Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.F64_TYPE -> ((WasmFloat64Array) array).initialize(bytecode, dataOffset + source, destination, length); + case WasmType.V128_TYPE -> ((WasmVec128Array) array).initialize(bytecode, dataOffset + source, destination, length); + default -> throw CompilerDirectives.shouldNotReachHere("array with ref element type used in array.init_data"); + } + stackPointer -= 4; + break; + } + case Bytecode.ARRAY_INIT_ELEM: { + final int elemIdx = rawPeekI32(bytecode, offset); + offset += 4; + + final Object[] elemInstance = instance.elemInstance(elemIdx); + + int length = WasmFrame.popInt(frame, stackPointer - 1); + int source = WasmFrame.popInt(frame, stackPointer - 2); + int destination = WasmFrame.popInt(frame, stackPointer - 3); + Object array = WasmFrame.popReference(frame, stackPointer - 4); + + if (array == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_ARRAY_REFERENCE, this); + } + if (checkOutOfBounds(destination, length, ((WasmArray) array).length())) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_ARRAY_ACCESS); + } + if (checkOutOfBounds(source, length, elemInstance == null ? 0 : elemInstance.length)) { + enterErrorBranch(); + throw WasmException.create(Failure.OUT_OF_BOUNDS_TABLE_ACCESS); + } + if (length > 0) { + ((WasmRefArray) array).initialize(elemInstance, source, destination, length); + } + stackPointer -= 4; + break; + } + case Bytecode.REF_TEST: { + final int expectedReferenceType = rawPeekI32(bytecode, offset); + offset += 4; + + Object ref = WasmFrame.popReference(frame, stackPointer - 1); + boolean match = runTimeTypeCheck(expectedReferenceType, ref); + WasmFrame.pushInt(frame, stackPointer - 1, match ? 1 : 0); + break; + } + case Bytecode.REF_CAST: { + final int expectedReferenceType = rawPeekI32(bytecode, offset); + offset += 4; + + Object ref = WasmFrame.popReference(frame, stackPointer - 1); + boolean match = runTimeTypeCheck(expectedReferenceType, ref); + if (!match) { + enterErrorBranch(); + throw WasmException.create(Failure.CAST_FAILURE, this); + } + WasmFrame.pushReference(frame, stackPointer - 1, ref); + break; + } + case Bytecode.BR_ON_CAST_U8: + case Bytecode.BR_ON_CAST_FAIL_U8: { + final int expectedReferenceType = rawPeekI32(bytecode, offset); + final boolean brOnCastFail = aggregateOpcode == Bytecode.BR_ON_CAST_FAIL_U8; + + // Peek at top of stack instead of popping and pushing the same value. + Object ref = frame.getObjectStatic(stackPointer - 1); + boolean match = runTimeTypeCheck(expectedReferenceType, ref); + if (profileCondition(bytecode, offset + 5, match != brOnCastFail)) { + final int offsetDelta = rawPeekU8(bytecode, offset + 4); + // BR_ON_CAST_U8 encodes the back jump value as a positive byte value. + // BR_ON_CAST_U8 can never perform a forward jump. + offset = offset + 4 - offsetDelta; + } else { + offset += 7; + } + break; + } + case Bytecode.BR_ON_CAST_I32: + case Bytecode.BR_ON_CAST_FAIL_I32: { + final int expectedReferenceType = rawPeekI32(bytecode, offset); + final boolean brOnCastFail = aggregateOpcode == Bytecode.BR_ON_CAST_FAIL_I32; + + // Peek at top of stack instead of popping and pushing the same value. + Object ref = frame.getObjectStatic(stackPointer - 1); + boolean match = runTimeTypeCheck(expectedReferenceType, ref); + if (profileCondition(bytecode, offset + 8, match != brOnCastFail)) { + final int offsetDelta = rawPeekI32(bytecode, offset + 4); + offset = offset + 4 + offsetDelta; + } else { + offset += 10; + } + break; + } + case Bytecode.REF_I31: { + int i32 = WasmFrame.popInt(frame, stackPointer - 1); + Integer i31 = i32 & ~(1 << 31); + WasmFrame.pushReference(frame, stackPointer - 1, i31); + break; + } + case Bytecode.I31_GET_S: + case Bytecode.I31_GET_U: { + Object i31 = WasmFrame.popReference(frame, stackPointer - 1); + if (i31 == WasmConstant.NULL) { + enterErrorBranch(); + throw WasmException.create(Failure.NULL_I31_REFERENCE, this); + } + int i32 = (int) i31; + if (aggregateOpcode == Bytecode.I31_GET_S) { + i32 = (i32 << 1) >> 1; + } + WasmFrame.pushInt(frame, stackPointer - 1, i32); + break; + } + default: + throw CompilerDirectives.shouldNotReachHere(); + } + + assert stackPointer - startingStackPointer == StackEffects.getAggregateOpStackEffect(aggregateOpcode); + return offset; + } + private int executeMisc(WasmInstance instance, VirtualFrame frame, int startingOffset, int startingStackPointer, int miscOpcode) { int offset = startingOffset; int stackPointer = startingStackPointer; @@ -2285,6 +2849,14 @@ private int executeMisc(WasmInstance instance, VirtualFrame frame, int startingO pushReference(frame, stackPointer - 1, reference); break; } + case Bytecode.REF_EQ: { + Object ref1 = popReference(frame, stackPointer - 1); + Object ref2 = popReference(frame, stackPointer - 2); + boolean equal = ref1 == ref2 || ref1 instanceof Integer int1 && ref2 instanceof Integer int2 && int1.intValue() == int2.intValue(); + pushInt(frame, stackPointer - 2, equal ? 1 : 0); + stackPointer -= 1; + break; + } default: throw CompilerDirectives.shouldNotReachHere(); } @@ -4640,6 +5212,10 @@ private void memory_copy(WasmInstance instance, long length, long source, long d // Checkstyle: resume method name check + private static boolean checkOutOfBounds(int offset, int size) { + return offset < 0 || offset >= size; + } + private static boolean checkOutOfBounds(int offset, int length, int size) { return offset < 0 || length < 0 || offset + length < 0 || offset + length > size; } @@ -4726,6 +5302,138 @@ private void pushExceptionFields(VirtualFrame frame, WasmRuntimeException e, int } } + @ExplodeLoop + private WasmArray popArrayElements(VirtualFrame frame, DefinedType arrayType, int elemType, int length, int stackPointerOffset) { + CompilerAsserts.partialEvaluationConstant(elemType); + CompilerAsserts.partialEvaluationConstant(length); + CompilerAsserts.partialEvaluationConstant(stackPointerOffset); + int stackPointer = stackPointerOffset; + switch (elemType) { + case WasmType.I8_TYPE: { + byte[] fixedArray = new byte[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (byte) WasmFrame.popInt(frame, --stackPointer); + } + return new WasmInt8Array(arrayType, fixedArray); + } + case WasmType.I16_TYPE: { + short[] fixedArray = new short[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = (short) WasmFrame.popInt(frame, --stackPointer); + } + return new WasmInt16Array(arrayType, fixedArray); + } + case WasmType.I32_TYPE: { + int[] fixedArray = new int[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = WasmFrame.popInt(frame, --stackPointer); + } + return new WasmInt32Array(arrayType, fixedArray); + } + case WasmType.I64_TYPE: { + long[] fixedArray = new long[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = WasmFrame.popLong(frame, --stackPointer); + } + return new WasmInt64Array(arrayType, fixedArray); + } + case WasmType.F32_TYPE: { + float[] fixedArray = new float[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = WasmFrame.popFloat(frame, --stackPointer); + } + return new WasmFloat32Array(arrayType, fixedArray); + } + case WasmType.F64_TYPE: { + double[] fixedArray = new double[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = WasmFrame.popDouble(frame, --stackPointer); + } + return new WasmFloat64Array(arrayType, fixedArray); + } + case WasmType.V128_TYPE: { + byte[] fixedArray = new byte[length << 4]; + for (int i = length - 1; i >= 0; i--) { + vector128Ops().intoArray(WasmFrame.popVector128(frame, --stackPointer), fixedArray, i << 4); + } + return new WasmVec128Array(arrayType, fixedArray); + } + default: { + Object[] fixedArray = new Object[length]; + for (int i = length - 1; i >= 0; i--) { + fixedArray[i] = WasmFrame.popReference(frame, --stackPointer); + } + return new WasmRefArray(arrayType, fixedArray); + } + } + } + + @ExplodeLoop + private void popStructFields(VirtualFrame frame, int structTypeIndex, WasmStruct struct, WasmStructAccess structAccess, int numFields, int stackPointerOffset) { + CompilerAsserts.partialEvaluationConstant(structTypeIndex); + CompilerAsserts.partialEvaluationConstant(structAccess); + CompilerAsserts.partialEvaluationConstant(numFields); + CompilerAsserts.partialEvaluationConstant(stackPointerOffset); + int stackPointer = stackPointerOffset; + for (int fieldIndex = numFields - 1; fieldIndex >= 0; fieldIndex--) { + int fieldType = module.structTypeFieldTypeAt(structTypeIndex, fieldIndex); + CompilerAsserts.partialEvaluationConstant(fieldType); + StaticProperty property = structAccess.properties()[fieldIndex]; + switch (fieldType) { + case WasmType.I8_TYPE -> property.setByte(struct, (byte) WasmFrame.popInt(frame, --stackPointer)); + case WasmType.I16_TYPE -> property.setShort(struct, (short) WasmFrame.popInt(frame, --stackPointer)); + case WasmType.I32_TYPE -> property.setInt(struct, WasmFrame.popInt(frame, --stackPointer)); + case WasmType.I64_TYPE -> property.setLong(struct, WasmFrame.popLong(frame, --stackPointer)); + case WasmType.F32_TYPE -> property.setFloat(struct, WasmFrame.popFloat(frame, --stackPointer)); + case WasmType.F64_TYPE -> property.setDouble(struct, WasmFrame.popDouble(frame, --stackPointer)); + case WasmType.V128_TYPE -> property.setObject(struct, vector128Ops().toVector128(WasmFrame.popVector128(frame, --stackPointer))); + default -> { + assert WasmType.isReferenceType(fieldType); + property.setObject(struct, WasmFrame.popReference(frame, --stackPointer)); + } + } + } + } + + private boolean runTimeTypeCheck(int expectedReferenceType, Object object) { + boolean isNullable = WasmType.isNullable(expectedReferenceType); + CompilerAsserts.partialEvaluationConstant(isNullable); + if (isNullable && object == WasmConstant.NULL) { + return true; + } + boolean isConcreteReferenceType = WasmType.isConcreteReferenceType(expectedReferenceType); + CompilerAsserts.partialEvaluationConstant(isConcreteReferenceType); + if (isConcreteReferenceType) { + int expectedTypeIndex = WasmType.getTypeIndex(expectedReferenceType); + CompilerAsserts.partialEvaluationConstant(expectedTypeIndex); + return object instanceof WasmTypedHeapObject heapObject && runTimeConcreteTypeCheck(expectedTypeIndex, heapObject); + } else { + int expectedAbstractHeapType = WasmType.getAbstractHeapType(expectedReferenceType); + CompilerAsserts.partialEvaluationConstant(expectedAbstractHeapType); + return module.closedHeapTypeOf(expectedAbstractHeapType).matchesValue(object); + } + } + + /** + * This is an optimized version of {@link DefinedType#isSubtypeOf(DefinedType)} that makes use + * of the type equivalence classes that are constructed after linking. + */ + private boolean runTimeConcreteTypeCheck(int expectedTypeIndex, WasmTypedHeapObject heapObject) { + DefinedType expectedType = module.closedTypeAt(expectedTypeIndex); + DefinedType actualType = heapObject.type(); + if (actualType.typeEquivalenceClass() == expectedType.typeEquivalenceClass()) { + return true; + } else if (expectedType.isFinal()) { + return false; + } else { + codeEntry.subtypingBranch(); + do { + actualType = (DefinedType) actualType.superType(); + } while (actualType != null && actualType.typeEquivalenceClass() != expectedType.typeEquivalenceClass()); + return actualType != null; + } + } + /** * Populates the stack with the result values of the current block (the one we are escaping * from). Reset the stack pointer to the target block stack pointer. diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionRootNode.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionRootNode.java index 01000a040a76..5f80e658f391 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionRootNode.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmFunctionRootNode.java @@ -64,8 +64,8 @@ import org.graalvm.wasm.WasmLanguage; import org.graalvm.wasm.WasmModule; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.api.Vector128; -import org.graalvm.wasm.api.Vector128Ops; +import org.graalvm.wasm.vector.Vector128; +import org.graalvm.wasm.vector.Vector128Ops; import org.graalvm.wasm.debugging.data.DebugFunction; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeGen.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeGen.java index 1fbb535f331f..07eafc7ff70f 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeGen.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeGen.java @@ -41,7 +41,7 @@ package org.graalvm.wasm.parser.bytecode; -import org.graalvm.wasm.api.Vector128; +import org.graalvm.wasm.vector.Vector128; import org.graalvm.wasm.collection.ByteArrayList; /** @@ -216,6 +216,13 @@ public int location() { return data.size(); } + /** + * Undoes the writing of the last byte to the bytecode. + */ + public void retreat() { + data.dropLast(); + } + /** * @return A byte array representation of the bytecode. */ diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeParser.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeParser.java index 3845cc7e4d7a..f9447004557d 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeParser.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/BytecodeParser.java @@ -94,14 +94,20 @@ public static void resetGlobalState(WasmModule module, WasmInstance instance) { if (module.globalExternal(i)) { final WasmGlobal global = instance.externalGlobal(i); try { - InteropLibrary interop = InteropLibrary.getUncached(global); - if (!global.isMutable()) { - if (!Objects.equals(interop.readMember(global, WasmGlobal.VALUE_MEMBER), initValue)) { - throw CompilerDirectives.shouldNotReachHere("Value of immutable global is not equal to init value"); + switch (global.getClosedType().valueKind()) { + case Number, Vector -> { + InteropLibrary interop = InteropLibrary.getUncached(global); + if (!global.isMutable()) { + if (!Objects.equals(interop.readMember(global, WasmGlobal.VALUE_MEMBER), initValue)) { + throw CompilerDirectives.shouldNotReachHere("Value of immutable global is not equal to init value"); + } + continue; + } + interop.writeMember(global, WasmGlobal.VALUE_MEMBER, initValue); } - continue; + // reset structs and arrays, even if the global is immutable + case Reference -> global.storeReference(initValue); } - interop.writeMember(global, WasmGlobal.VALUE_MEMBER, initValue); } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException e) { throw CompilerDirectives.shouldNotReachHere(e); } @@ -793,6 +799,54 @@ private static List readCallNodes(byte[] bytecode, int startOffset, in } break; } + case Bytecode.AGGREGATE: + int aggregateOpcode = rawPeekU8(bytecode, offset); + offset++; + switch (aggregateOpcode) { + case Bytecode.ARRAY_LEN: { + break; + } + case Bytecode.STRUCT_NEW: + case Bytecode.STRUCT_NEW_DEFAULT: + case Bytecode.ARRAY_NEW: + case Bytecode.ARRAY_NEW_DEFAULT: + case Bytecode.ARRAY_GET: + case Bytecode.ARRAY_GET_S: + case Bytecode.ARRAY_GET_U: + case Bytecode.ARRAY_SET: + case Bytecode.ARRAY_FILL: + case Bytecode.REF_TEST: + case Bytecode.REF_CAST: + case Bytecode.ARRAY_COPY: + case Bytecode.ARRAY_INIT_ELEM: { + offset += 4; + break; + } + case Bytecode.BR_ON_CAST_U8: + case Bytecode.BR_ON_CAST_FAIL_U8: { + offset += 7; + break; + } + case Bytecode.STRUCT_GET: + case Bytecode.STRUCT_GET_S: + case Bytecode.STRUCT_GET_U: + case Bytecode.STRUCT_SET: + case Bytecode.ARRAY_NEW_FIXED: + case Bytecode.ARRAY_NEW_DATA: + case Bytecode.ARRAY_NEW_ELEM: + case Bytecode.ARRAY_INIT_DATA: { + offset += 8; + break; + } + case Bytecode.BR_ON_CAST_I32: + case Bytecode.BR_ON_CAST_FAIL_I32: { + offset += 10; + break; + } + default: + throw CompilerDirectives.shouldNotReachHere(); + } + break; case Bytecode.MISC: int miscOpcode = rawPeekU8(bytecode, offset); offset++; @@ -805,7 +859,8 @@ private static List readCallNodes(byte[] bytecode, int startOffset, in case Bytecode.I64_TRUNC_SAT_F32_U: case Bytecode.I64_TRUNC_SAT_F64_S: case Bytecode.I64_TRUNC_SAT_F64_U: - case Bytecode.THROW_REF: { + case Bytecode.THROW_REF: + case Bytecode.REF_EQ: { break; } case Bytecode.BR_ON_NULL_U8: diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/RuntimeBytecodeGen.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/RuntimeBytecodeGen.java index af5b59225302..139ce4cff1b8 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/RuntimeBytecodeGen.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/bytecode/RuntimeBytecodeGen.java @@ -42,7 +42,7 @@ package org.graalvm.wasm.parser.bytecode; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.api.Vector128; +import org.graalvm.wasm.vector.Vector128; import org.graalvm.wasm.constants.Bytecode; import org.graalvm.wasm.constants.BytecodeBitEncoding; import org.graalvm.wasm.constants.SegmentMode; @@ -370,43 +370,101 @@ public int addIfLocation() { return location; } - public enum BranchOp { - BR(op(Bytecode.BR_U8), op(Bytecode.BR_I32), false), - BR_IF(op(Bytecode.BR_IF_U8), op(Bytecode.BR_IF_I32), true), - BR_ON_NULL(miscOp(Bytecode.BR_ON_NULL_U8), miscOp(Bytecode.BR_ON_NULL_I32), true), - BR_ON_NON_NULL(miscOp(Bytecode.BR_ON_NON_NULL_U8), miscOp(Bytecode.BR_ON_NON_NULL_I32), true); + public abstract static class BranchOp { + + public static final BranchOp BR = new BranchOp(false) { + @Override + public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.BR_U8); + } + + @Override + public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.BR_I32); + } + }; + + public static final BranchOp BR_IF = new BranchOp(true) { + @Override + public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.BR_IF_U8); + } + + @Override + public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.BR_IF_I32); + } + }; + + public static final BranchOp BR_ON_NULL = new BranchOp(true) { + @Override + public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.MISC); + bytecode.add1(Bytecode.BR_ON_NULL_U8); + } + + @Override + public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.MISC); + bytecode.add1(Bytecode.BR_ON_NULL_I32); + } + }; + + public static final BranchOp BR_ON_NON_NULL = new BranchOp(true) { + @Override + public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.MISC); + bytecode.add1(Bytecode.BR_ON_NON_NULL_U8); + } + + @Override + public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { + bytecode.add1(Bytecode.MISC); + bytecode.add1(Bytecode.BR_ON_NON_NULL_I32); + } + }; + + public static class BrOnCast extends BranchOp { + + private final boolean brOnCastFail; + private final int expectedReferenceType; + + public BrOnCast(boolean brOnCastFail, int expectedReferenceType) { + super(true); + this.brOnCastFail = brOnCastFail; + this.expectedReferenceType = expectedReferenceType; + } + + @Override + public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { + // Bytecode.AGGREGATE already emitted by caller + bytecode.add1(brOnCastFail ? Bytecode.BR_ON_CAST_FAIL_U8 : Bytecode.BR_ON_CAST_U8); + bytecode.add4(expectedReferenceType); + } + + @Override + public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { + // Bytecode.AGGREGATE already emitted by caller + bytecode.add1(brOnCastFail ? Bytecode.BR_ON_CAST_FAIL_I32 : Bytecode.BR_ON_CAST_I32); + bytecode.add4(expectedReferenceType); + } + } - private final byte[] opcodesU8; - private final byte[] opcodesI32; private final boolean profiled; - BranchOp(byte[] opcodesU8, byte[] opcodesI32, boolean profiled) { - this.opcodesU8 = opcodesU8; - this.opcodesI32 = opcodesI32; + BranchOp(boolean profiled) { this.profiled = profiled; } - public void emitOpcodesU8(RuntimeBytecodeGen bytecode) { - bytecode.addBytes(opcodesU8, 0, opcodesU8.length); - } + public abstract void emitOpcodesU8(RuntimeBytecodeGen bytecode); - public void emitOpcodesI32(RuntimeBytecodeGen bytecode) { - bytecode.addBytes(opcodesI32, 0, opcodesI32.length); - } + public abstract void emitOpcodesI32(RuntimeBytecodeGen bytecode); public void emitProfile(RuntimeBytecodeGen bytecode) { if (profiled) { bytecode.addProfile(); } } - - private static byte[] op(int opcode) { - return new byte[]{(byte) opcode}; - } - - private static byte[] miscOp(int opcode) { - return new byte[]{(byte) Bytecode.MISC, (byte) opcode}; - } } /** @@ -809,20 +867,13 @@ public void addType(int type) { add4(type); } - /** - * Adds a null entry to the data of an elem segment. - */ - public void addElemNull() { - add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_FUNCTION_INDEX | BytecodeBitEncoding.ELEM_ITEM_NULL_FLAG); - } - /** * Adds a function index entry to the data of an elem segment. * * @param functionIndex The function index of the element in the elem segment */ public void addElemFunctionIndex(int functionIndex) { - if (functionIndex >= 0 && functionIndex <= 15) { + if (functionIndex >= 0 && functionIndex <= BytecodeBitEncoding.ELEM_ITEM_MAX_INLINE_VALUE) { add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_FUNCTION_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_INLINE | functionIndex); } else if (fitsIntoUnsignedByte(functionIndex)) { add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_FUNCTION_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_U8); @@ -837,23 +888,25 @@ public void addElemFunctionIndex(int functionIndex) { } /** - * Adds a global index entry to the data of an elem segment. + * Adds a bytecode entry to the data of an elem segment. * - * @param globalIndex The global index of the element in the elem segment - */ - public void addElemGlobalIndex(int globalIndex) { - if (globalIndex >= 0 && globalIndex <= 15) { - add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_GLOBAL_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_INLINE | globalIndex); - } else if (fitsIntoUnsignedByte(globalIndex)) { - add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_GLOBAL_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_U8); - add1(globalIndex); - } else if (fitsIntoUnsignedShort(globalIndex)) { - add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_GLOBAL_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_U16); - add2(globalIndex); + * @param elementBytecode The bytecode of the element expression in the elem segment + */ + public void addElemBytecode(byte[] elementBytecode) { + int elementBytecodeLength = elementBytecode.length; + if (elementBytecodeLength >= 0 && elementBytecodeLength <= BytecodeBitEncoding.ELEM_ITEM_MAX_INLINE_VALUE) { + add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE | BytecodeBitEncoding.ELEM_ITEM_LENGTH_INLINE | elementBytecodeLength); + } else if (fitsIntoUnsignedByte(elementBytecodeLength)) { + add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE | BytecodeBitEncoding.ELEM_ITEM_LENGTH_U8); + add1(elementBytecodeLength); + } else if (fitsIntoUnsignedShort(elementBytecodeLength)) { + add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE | BytecodeBitEncoding.ELEM_ITEM_LENGTH_U16); + add2(elementBytecodeLength); } else { - add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_GLOBAL_INDEX | BytecodeBitEncoding.ELEM_ITEM_LENGTH_I32); - add4(globalIndex); + add1(BytecodeBitEncoding.ELEM_ITEM_TYPE_BYTECODE | BytecodeBitEncoding.ELEM_ITEM_LENGTH_I32); + add4(elementBytecodeLength); } + addBytes(elementBytecode, 0, elementBytecodeLength); } /** diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java index 63d783cf62c5..d00420921120 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ParserState.java @@ -48,12 +48,13 @@ import org.graalvm.wasm.Assert; import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.api.Vector128; +import org.graalvm.wasm.vector.Vector128; import org.graalvm.wasm.collection.IntArrayList; import org.graalvm.wasm.constants.Bytecode; import org.graalvm.wasm.exception.Failure; import org.graalvm.wasm.exception.WasmException; import org.graalvm.wasm.parser.bytecode.RuntimeBytecodeGen; +import org.graalvm.wasm.parser.bytecode.RuntimeBytecodeGen.BranchOp; /** * Represents the values and stack frames of a Wasm code section during validation. Stores @@ -83,7 +84,7 @@ public ParserState(RuntimeBytecodeGen bytecode, SymbolTable symbolTable) { * Pops a value from the stack if possible. Throws an error on stack underflow. * * @param expectedValueType The expectedValueType used for error generation. - * @return The top of the stack or -1. + * @return The top of the stack or {@link WasmType#BOT}. */ private int popInternal(int expectedValueType) { if (availableStackSize() == 0) { @@ -100,6 +101,23 @@ private int popInternal(int expectedValueType) { return valueStack.popBack(); } + /** + * Pops a value from the stack if possible. Throws an error on stack underflow. Expects the + * value type to be any reference type. + * + * @return The top of the stack or {@link WasmType#BOT}. + */ + private int popInternalExpectReference() { + if (availableStackSize() == 0) { + if (isCurrentStackUnreachable()) { + return WasmType.BOT; + } else { + throw ValidationErrors.createExpectedReferenceTypeOnEmptyStack(); + } + } + return valueStack.popBack(); + } + /** * Pops the maximum available values from the current stack frame. If the number of values on * the stack is smaller than the number of expectedValueTypes, only the remaining stack values @@ -211,16 +229,11 @@ public int popChecked(int expectedValueType) { * @return The reference type on top of the stack. */ public int popReferenceTypeChecked() { - if (availableStackSize() != 0) { - final int value = valueStack.popBack(); - if (WasmType.isReferenceType(value)) { - return value; - } - // Push value back onto the stack and perform a checked pop to get the correct error - // message - valueStack.push(value); + final int actualValueType = popInternalExpectReference(); + if (!WasmType.isReferenceType(actualValueType)) { + throw ValidationErrors.createExpectedReferenceTypeMismatch(actualValueType); } - return popChecked(WasmType.FUNCREF_TYPE); + return actualValueType; } /** @@ -401,7 +414,7 @@ public void addConditionalBranch(int branchLabel) { final int[] labelTypes = frame.labelTypes(); popAll(labelTypes); pushAll(labelTypes); - frame.addBranch(bytecode, RuntimeBytecodeGen.BranchOp.BR_IF); + frame.addBranch(bytecode, BranchOp.BR_IF); } /** @@ -415,7 +428,7 @@ public void addUnconditionalBranch(int branchLabel) { ControlFrame frame = getFrame(branchLabel); final int[] labelTypes = frame.labelTypes(); popAll(labelTypes); - frame.addBranch(bytecode, RuntimeBytecodeGen.BranchOp.BR); + frame.addBranch(bytecode, BranchOp.BR); } public void addBranchOnNull(int branchLabel) { @@ -424,7 +437,7 @@ public void addBranchOnNull(int branchLabel) { final int[] labelTypes = frame.labelTypes(); popAll(labelTypes); pushAll(labelTypes); - frame.addBranch(bytecode, RuntimeBytecodeGen.BranchOp.BR_ON_NULL); + frame.addBranch(bytecode, BranchOp.BR_ON_NULL); } public void addBranchOnNonNull(int branchLabel, int referenceType) { @@ -443,7 +456,29 @@ public void addBranchOnNonNull(int branchLabel, int referenceType) { for (int i = 0; i < labelTypes.length - 1; i++) { push(labelTypes[i]); } - frame.addBranch(bytecode, RuntimeBytecodeGen.BranchOp.BR_ON_NON_NULL); + frame.addBranch(bytecode, BranchOp.BR_ON_NON_NULL); + } + + public void addBranchOnCast(int branchLabel, int topReferenceType, int jumpReferenceType, int noJumpReferenceType, BranchOp branchOp) { + checkLabelExists(branchLabel); + ControlFrame frame = getFrame(branchLabel); + final int[] labelTypes = frame.labelTypes(); + + if (labelTypes.length < 1) { + throw ValidationErrors.createLabelTypesMismatch(labelTypes, new int[]{jumpReferenceType}); + } + if (!symbolTable.matchesType(labelTypes[labelTypes.length - 1], jumpReferenceType)) { + throw ValidationErrors.createTypeMismatch(labelTypes[labelTypes.length - 1], jumpReferenceType); + } + popChecked(topReferenceType); + for (int i = labelTypes.length - 2; i >= 0; i--) { + popChecked(labelTypes[i]); + } + for (int i = 0; i < labelTypes.length - 2; i++) { + push(labelTypes[i]); + } + push(noJumpReferenceType); + frame.addBranch(bytecode, branchOp); } /** @@ -525,6 +560,13 @@ public void addCall(int nodeIndex, int functionIndex) { bytecode.addCall(nodeIndex, functionIndex); } + /** + * Adds the aggregate flag to the bytecode. + */ + public void addAggregateFlag() { + bytecode.addOp(Bytecode.AGGREGATE); + } + /** * Adds the mics flag to the bytecode. */ @@ -676,6 +718,13 @@ public void addVectorLaneInstruction(int instruction, byte laneIndex) { bytecode.add(laneIndex); } + /** + * Undoes the writing of the last byte to the bytecode. + */ + public void retreat() { + bytecode.retreat(); + } + /** * Finishes the current control frame and removes it from the control frame stack. * diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ValidationErrors.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ValidationErrors.java index 26f9e8d926d5..e83b80e2ae93 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ValidationErrors.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/parser/validation/ValidationErrors.java @@ -69,6 +69,12 @@ public static WasmException createTypeMismatch(int expectedType, int actualType) return create("Expected type [%s], but got [%s].", expectedTypeString, actualTypeString); } + @TruffleBoundary + public static WasmException createExpectedReferenceTypeMismatch(int actualType) { + String actualTypeString = WasmType.toString(actualType); + return create("Expected type [%s], but got [%s].", "", actualTypeString); + } + @TruffleBoundary public static WasmException createResultTypesMismatch(int[] expectedTypes, int[] actualTypes) { String expectedTypesString = getValueTypesString(expectedTypes); @@ -91,18 +97,33 @@ public static WasmException createParamTypesMismatch(int[] expectedTypes, int[] } @TruffleBoundary - public static WasmException createMissingLabel(int expected, int max) { - return WasmException.format(Failure.UNKNOWN_LABEL, "Unknown branch label %d (max %d).", expected, max); + public static WasmException createMissingLabel(int label, int max) { + return WasmException.format(Failure.UNKNOWN_LABEL, "Unknown branch label %d (max %d).", label, max); + } + + @TruffleBoundary + public static WasmException createMissingType(int typeIndex) { + return WasmException.format(Failure.UNKNOWN_TYPE, "Type variable %d out of range.", typeIndex); + } + + @TruffleBoundary + public static WasmException createMissingType(int typeIndex, int max) { + return WasmException.format(Failure.UNKNOWN_TYPE, "Type variable %d out of range. (max %d)", typeIndex, max); + } + + @TruffleBoundary + public static WasmException createExpectedArrayType(int typeIndex) { + return WasmException.format(Failure.TYPE_MISMATCH, "Type %d is not an array type", typeIndex); } @TruffleBoundary - public static WasmException createMissingFunctionType(int expected) { - return WasmException.format(Failure.UNKNOWN_TYPE, "Function type variable %d out of range.", expected); + public static WasmException createExpectedStructType(int typeIndex) { + return WasmException.format(Failure.TYPE_MISMATCH, "Type %d is not a struct type", typeIndex); } @TruffleBoundary - public static WasmException createMissingFunctionType(int expected, int max) { - return WasmException.format(Failure.UNKNOWN_TYPE, "Function type variable %d out of range. (max %d)", expected, max); + public static WasmException createExpectedFunctionType(int typeIndex) { + return WasmException.format(Failure.TYPE_MISMATCH, "Type %d is not a function type", typeIndex); } @TruffleBoundary @@ -115,4 +136,9 @@ public static WasmException createExpectedTypeOnEmptyStack(int expectedType) { String expectedTypeString = WasmType.toString(expectedType); return create("Expected type [%s], but got [].", expectedTypeString, ""); } + + @TruffleBoundary + public static WasmException createExpectedReferenceTypeOnEmptyStack() { + return create("Expected type [%s], but got [].", "", ""); + } } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/BuiltinModule.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/BuiltinModule.java index b9afdc82bd81..2dccadc79fff 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/BuiltinModule.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/BuiltinModule.java @@ -103,7 +103,7 @@ protected WasmFunction defineFunction(WasmContext context, WasmModule module, St // We could check if the same function type had already been allocated, // but this is just an optimization, and probably not very important, // since predefined modules have a relatively small size. - final int typeIdx = module.symbolTable().allocateFunctionType(paramTypes, retTypes, context.getContextOptions().supportMultiValue()); + final int typeIdx = module.symbolTable().allocateFunctionType(paramTypes, retTypes, context.getContextOptions().supportMultiValue(), context.language()); final WasmFunction function = module.symbolTable().declareExportedFunction(typeIdx, name); function.setTarget(rootNode.getCallTarget()); return function; @@ -140,7 +140,7 @@ protected void defineMemory(WasmContext context, WasmModule module, String memor } protected void importFunction(WasmContext context, WasmModule module, String importModuleName, String importFunctionName, int[] paramTypes, int[] retTypes, String exportName) { - final int typeIdx = module.symbolTable().allocateFunctionType(paramTypes, retTypes, context.getContextOptions().supportMultiValue()); + final int typeIdx = module.symbolTable().allocateFunctionType(paramTypes, retTypes, context.getContextOptions().supportMultiValue(), context.language()); final WasmFunction function = module.symbolTable().importFunction(importModuleName, importFunctionName, typeIdx); module.symbolTable().exportFunction(function.index(), exportName); } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/emscripten/EmscriptenModule.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/emscripten/EmscriptenModule.java index 652cafcebe58..933c36950cbf 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/emscripten/EmscriptenModule.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/emscripten/EmscriptenModule.java @@ -47,7 +47,7 @@ import org.graalvm.wasm.WasmLanguage; import org.graalvm.wasm.WasmModule; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.predefined.BuiltinModule; import org.graalvm.wasm.predefined.wasi.WasiFdWriteNode; @@ -76,10 +76,10 @@ protected WasmModule createModule(WasmLanguage language, WasmContext context, St defineFunction(context, module, "__syscall54", types(I32_TYPE, I32_TYPE), types(I32_TYPE), new UnimplementedNode("__syscall54", language, module)); defineFunction(context, module, "__syscall6", types(I32_TYPE, I32_TYPE), types(I32_TYPE), new UnimplementedNode("__syscall6", language, module)); defineFunction(context, module, "setTempRet0", types(I32_TYPE), types(), new UnimplementedNode("setTempRet0", language, module)); - defineGlobal(module, "_table_base", I32_TYPE, GlobalModifier.CONSTANT, 0); - defineGlobal(module, "_memory_base", I32_TYPE, GlobalModifier.CONSTANT, 0); - defineGlobal(module, "DYNAMICTOP_PTR", I32_TYPE, GlobalModifier.CONSTANT, 0); - defineGlobal(module, "DYNAMIC_BASE", I32_TYPE, GlobalModifier.CONSTANT, 0); + defineGlobal(module, "_table_base", I32_TYPE, Mutability.CONSTANT, 0); + defineGlobal(module, "_memory_base", I32_TYPE, Mutability.CONSTANT, 0); + defineGlobal(module, "DYNAMICTOP_PTR", I32_TYPE, Mutability.CONSTANT, 0); + defineGlobal(module, "DYNAMIC_BASE", I32_TYPE, Mutability.CONSTANT, 0); defineTable(context, module, "table", 0, -1, WasmType.FUNCREF_TYPE); return module; } diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/spectest/SpectestModule.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/spectest/SpectestModule.java index 60559fcd36c2..ff16f22ee57c 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/spectest/SpectestModule.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/predefined/spectest/SpectestModule.java @@ -50,7 +50,7 @@ import org.graalvm.wasm.WasmLanguage; import org.graalvm.wasm.WasmModule; import org.graalvm.wasm.WasmType; -import org.graalvm.wasm.constants.GlobalModifier; +import org.graalvm.wasm.constants.Mutability; import org.graalvm.wasm.predefined.BuiltinModule; public class SpectestModule extends BuiltinModule { @@ -65,10 +65,10 @@ protected WasmModule createModule(WasmLanguage language, WasmContext context, St defineFunction(context, module, "print_f64", types(F64_TYPE), types(), new PrintNode(language, module)); defineFunction(context, module, "print_i32_f32", types(I32_TYPE, F32_TYPE), types(), new PrintNode(language, module)); defineFunction(context, module, "print_f64_f64", types(F64_TYPE, F64_TYPE), types(), new PrintNode(language, module)); - defineGlobal(module, "global_i32", I32_TYPE, GlobalModifier.CONSTANT, 666); - defineGlobal(module, "global_i64", I64_TYPE, GlobalModifier.CONSTANT, 666L); - defineGlobal(module, "global_f32", F32_TYPE, GlobalModifier.CONSTANT, 666.6f); - defineGlobal(module, "global_f64", F64_TYPE, GlobalModifier.CONSTANT, 666.6); + defineGlobal(module, "global_i32", I32_TYPE, Mutability.CONSTANT, 666); + defineGlobal(module, "global_i64", I64_TYPE, Mutability.CONSTANT, 666L); + defineGlobal(module, "global_f32", F32_TYPE, Mutability.CONSTANT, 666.6f); + defineGlobal(module, "global_f64", F64_TYPE, Mutability.CONSTANT, 666.6); defineTable(context, module, "table", 10, 20, WasmType.FUNCREF_TYPE); defineMemory(context, module, "memory", 1, 2, false, false); if (context.getContextOptions().supportThreads() && context.getContextOptions().useUnsafeMemory()) { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStruct.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStruct.java new file mode 100644 index 000000000000..60b447761d4a --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStruct.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.struct; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.staticobject.StaticProperty; +import org.graalvm.wasm.WasmTypedHeapObject; +import org.graalvm.wasm.api.InteropArray; +import org.graalvm.wasm.constants.Mutability; +import org.graalvm.wasm.types.DefinedType; +import org.graalvm.wasm.types.NumberType; +import org.graalvm.wasm.types.PackedType; +import org.graalvm.wasm.types.StorageType; +import org.graalvm.wasm.types.ValueType; + +@ExportLibrary(InteropLibrary.class) +public class WasmStruct extends WasmTypedHeapObject { + + public WasmStruct(DefinedType type) { + super(type); + assert type.isStructType(); + } + + @ExportMessage + protected static boolean hasMembers(@SuppressWarnings("unused") WasmStruct receiver) { + return true; + } + + @ExportMessage + protected InteropArray getMembers(@SuppressWarnings("unused") boolean includeInternal) { + Object[] members = new Object[type().asStructType().fieldTypes().length]; + for (int i = 0; i < members.length; i++) { + members[i] = integerToString(i); + } + return InteropArray.create(members); + } + + @ExportMessage + protected boolean isMemberReadable(String member) { + try { + return parseInt(member) < type().asStructType().fieldTypes().length; + } catch (NumberFormatException e) { + return false; + } + } + + @ExportMessage + protected boolean isMemberModifiable(String member) { + try { + int fieldIndex = parseInt(member); + if (fieldIndex >= type().asStructType().fieldTypes().length) { + return false; + } + if (type().asStructType().fieldTypes()[fieldIndex].mutability() == Mutability.CONSTANT) { + return false; + } + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @ExportMessage + protected Object readMember(String member) throws UnknownIdentifierException { + assert isMemberReadable(member); + try { + int fieldIndex = parseInt(member); + if (fieldIndex >= type().asStructType().fieldTypes().length) { + throw UnknownIdentifierException.create(member); + } + return readField(fieldIndex); + } catch (NumberFormatException e) { + throw UnknownIdentifierException.create(member); + } + } + + private Object readField(int fieldIndex) { + StorageType fieldType = type().asStructType().fieldTypes()[fieldIndex].storageType(); + StaticProperty property = type().structAccess().properties()[fieldIndex]; + return switch (fieldType.storageKind()) { + case Packed -> switch ((PackedType) fieldType) { + case I8 -> property.getByte(this); + case I16 -> property.getShort(this); + }; + case Value -> switch (((ValueType) fieldType).valueKind()) { + case Number -> switch ((NumberType) fieldType) { + case I32 -> property.getInt(this); + case I64 -> property.getLong(this); + case F32 -> property.getFloat(this); + case F64 -> property.getDouble(this); + }; + case Vector, Reference -> property.getObject(this); + }; + }; + } + + @ExportMessage + protected void writeMember(String member, Object value) throws UnknownIdentifierException, UnsupportedTypeException { + assert isMemberModifiable(member); + try { + int fieldIndex = parseInt(member); + if (fieldIndex >= type().asStructType().fieldTypes().length) { + throw UnknownIdentifierException.create(member); + } + if (type().asStructType().fieldTypes()[fieldIndex].mutability() == Mutability.CONSTANT) { + throw UnknownIdentifierException.create(member); + } + writeField(fieldIndex, value); + } catch (NumberFormatException e) { + throw UnknownIdentifierException.create(member); + } + } + + private void writeField(int fieldIndex, Object value) throws UnsupportedTypeException { + StorageType fieldType = type().asStructType().fieldTypes()[fieldIndex].storageType(); + StaticProperty property = type().structAccess().properties()[fieldIndex]; + if (!fieldType.javaClass().isInstance(value)) { + throw UnsupportedTypeException.create(new Object[]{value}); + } + switch (fieldType.storageKind()) { + case Packed -> { + switch ((PackedType) fieldType) { + case I8 -> property.setByte(this, (byte) value); + case I16 -> property.setShort(this, (short) value); + } + } + case Value -> { + switch (((ValueType) fieldType).valueKind()) { + case Number -> { + switch ((NumberType) fieldType) { + case I32 -> property.setInt(this, (int) value); + case I64 -> property.setLong(this, (long) value); + case F32 -> property.setFloat(this, (float) value); + case F64 -> property.setDouble(this, (double) value); + } + } + case Vector, Reference -> property.setObject(this, value); + } + } + } + } + + @ExportMessage + @SuppressWarnings("unused") + protected static boolean isMemberInsertable(WasmStruct receiver, String member) { + return false; + } + + @Override + @TruffleBoundary + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("wasm-struct:<"); + int numFields = type().asStructType().fieldTypes().length; + for (int fieldIndex = 0; fieldIndex < numFields; fieldIndex++) { + sb.append(readField(fieldIndex)); + if (fieldIndex < numFields - 1) { + sb.append(", "); + } + } + sb.append(">"); + return sb.toString(); + } + + @ExportMessage + protected String toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) { + return toString(); + } + + @TruffleBoundary + private static int parseInt(String member) { + return Integer.parseInt(member); + } + + @TruffleBoundary + private static String integerToString(int fieldIndex) { + return Integer.toString(fieldIndex); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructAccess.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructAccess.java new file mode 100644 index 000000000000..573979bef996 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructAccess.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.struct; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.staticobject.StaticProperty; +import com.oracle.truffle.api.staticobject.StaticShape; + +public record WasmStructAccess(StaticShape shape, + @CompilerDirectives.CompilationFinal(dimensions = 1) StaticProperty[] properties) { +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructFactory.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructFactory.java new file mode 100644 index 000000000000..ec10a5c064a4 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/struct/WasmStructFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.struct; + +import org.graalvm.wasm.types.DefinedType; + +public interface WasmStructFactory { + + WasmStruct create(DefinedType type); +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/AbstractHeapType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/AbstractHeapType.java new file mode 100644 index 000000000000..ce077670aa94 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/AbstractHeapType.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import org.graalvm.wasm.WasmConstant; +import org.graalvm.wasm.WasmFunctionInstance; +import org.graalvm.wasm.WasmType; +import org.graalvm.wasm.array.WasmArray; +import org.graalvm.wasm.exception.WasmRuntimeException; +import org.graalvm.wasm.struct.WasmStruct; + +public enum AbstractHeapType implements HeapType { + + NOEXN(WasmType.NOEXN_HEAPTYPE), + NOFUNC(WasmType.NOFUNC_HEAPTYPE), + NOEXTERN(WasmType.NOEXTERN_HEAPTYPE), + NONE(WasmType.NONE_HEAPTYPE), + FUNC(WasmType.FUNC_HEAPTYPE), + EXTERN(WasmType.EXTERN_HEAPTYPE), + ANY(WasmType.ANY_HEAPTYPE), + EQ(WasmType.EQ_HEAPTYPE), + I31(WasmType.I31_HEAPTYPE), + STRUCT(WasmType.STRUCT_HEAPTYPE), + ARRAY(WasmType.ARRAY_HEAPTYPE), + EXN(WasmType.EXN_HEAPTYPE); + + private final int value; + + AbstractHeapType(int value) { + this.value = value; + } + + public int value() { + return value; + } + + @Override + public HeapKind heapKind() { + return HeapKind.Abstract; + } + + @Override + public boolean isSubtypeOf(HeapType that) { + return switch (this) { + case NOEXN -> that == NOEXN || that == EXN; + case NOFUNC -> that.isFunctionType(); + case NOEXTERN -> that == NOEXTERN || that == EXTERN; + case NONE -> that.isStructType() || that.isArrayType() || that == I31 || that == EQ || that == ANY; + case FUNC -> that == FUNC; + case EXTERN -> that == EXTERN; + case ANY -> that == ANY; + case EQ -> that == EQ || that == ANY; + case I31 -> that == I31 || that == EQ || that == ANY; + case STRUCT -> that == STRUCT || that == EQ || that == ANY; + case ARRAY -> that == ARRAY || that == EQ || that == ANY; + case EXN -> that == EXN; + }; + } + + @Override + public boolean isArrayType() { + return this == ARRAY || this == NONE; + } + + @Override + public boolean isStructType() { + return this == STRUCT || this == NONE; + } + + @Override + public boolean isFunctionType() { + return this == FUNC || this == NOFUNC; + } + + @Override + public boolean matchesValue(Object val) { + return switch (this) { + case NOEXN, NOFUNC, NOEXTERN, NONE -> false; + case FUNC -> val instanceof WasmFunctionInstance; + case EXTERN, ANY -> val != WasmConstant.NULL; + case EQ -> val instanceof WasmArray || val instanceof WasmStruct || (val instanceof Integer integer && integer >= 0); + case I31 -> val instanceof Integer integer && integer >= 0; + case STRUCT -> val instanceof WasmStruct; + case ARRAY -> val instanceof WasmArray; + case EXN -> val instanceof WasmRuntimeException; + }; + } + + @Override + public String toString() { + return switch (this) { + case NOEXN -> "noexn"; + case NOFUNC -> "nofunc"; + case NOEXTERN -> "noextern"; + case NONE -> "none"; + case FUNC -> "func"; + case EXTERN -> "extern"; + case ANY -> "any"; + case EQ -> "eq"; + case I31 -> "i31"; + case STRUCT -> "struct"; + case ARRAY -> "array"; + case EXN -> "exn"; + }; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ArrayType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ArrayType.java new file mode 100644 index 000000000000..e12c452e4ba7 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ArrayType.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; + +public record ArrayType(FieldType fieldType) implements CompositeType { + + @Override + public CompositeKind compositeKind() { + return CompositeKind.Array; + } + + @Override + public boolean isSubtypeOf(HeapType that) { + return that == AbstractHeapType.ARRAY || that == AbstractHeapType.EQ || that == AbstractHeapType.ANY || + that instanceof DefinedType definedSuperType && definedSuperType.expand() instanceof ArrayType arraySuperType && this.fieldType.isSubtypeOf(arraySuperType.fieldType); + } + + @Override + public void unroll(RecursiveTypes recursiveTypes) { + fieldType.unroll(recursiveTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + return "(array (field " + fieldType + "))"; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/CompositeType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/CompositeType.java new file mode 100644 index 000000000000..7ab968fb10a4 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/CompositeType.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +public sealed interface CompositeType permits ArrayType, StructType, FunctionType { + + // This is a workaround until we can use pattern matching in JDK 21+. + enum CompositeKind { + Array, + Struct, + Function + } + + CompositeKind compositeKind(); + + boolean isSubtypeOf(HeapType that); + + void unroll(RecursiveTypes recursiveTypes); +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/DefinedType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/DefinedType.java new file mode 100644 index 000000000000..13cd9689dbd4 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/DefinedType.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import org.graalvm.wasm.WasmTypedHeapObject; +import org.graalvm.wasm.struct.WasmStructAccess; + +import java.util.Objects; + +/** + * This represents a user-defined type in a WebAssembly module, and it corresponds to the + * {@code deftype} abstract syntax category in the WebAssembly specification. These are represented + * as projections from a list of mutually recursive types (with {@link RecursiveTypes} representing + * the list of recursive types and {@link SubType} representing an element of that list). + *

+ * These types can be recursive and in our representation, all type indices are expanded and any + * recursion is unrolled by creating cyclic graphs. Whenever a group of mutually recursive types has + * finished parsing, its types are unrolled via {@link DefinedType#unroll(RecursiveTypes)}. However, + * for certain operations which would try to traverse the entire AST, we treat the recursive + * references as not unrolled by first checking {@link #recursiveReference} and stopping further + * traversal. This is notably the case for {@link #equals(Object)} and {@link #hashCode()}, where + * this yields the iso-recursive type equality predicate that is required by the spec. + */ +public final class DefinedType implements HeapType { + + @CompilerDirectives.CompilationFinal private RecursiveTypes recursiveTypes; + private final int subTypeIndex; + + private final boolean recursiveReference; + + @CompilerDirectives.CompilationFinal private int typeEquivalenceClass; + @CompilerDirectives.CompilationFinal private WasmStructAccess structAccess; + + private DefinedType(int subTypeIndex, boolean recursiveReference) { + this.subTypeIndex = subTypeIndex; + this.recursiveReference = recursiveReference; + } + + public static DefinedType makeTopLevelType(RecursiveTypes recursiveTypes, int subTypeIndex) { + DefinedType toplevelType = new DefinedType(subTypeIndex, false); + toplevelType.recursiveTypes = recursiveTypes; + return toplevelType; + } + + public static DefinedType makeRecursiveReference(int subTypeIndex) { + return new DefinedType(subTypeIndex, true); + } + + public void setTypeEquivalenceClass(int typeEquivalenceClass) { + this.typeEquivalenceClass = typeEquivalenceClass; + } + + public int typeEquivalenceClass() { + return typeEquivalenceClass; + } + + public void setStructAccess(WasmStructAccess structAccess) { + this.structAccess = structAccess; + } + + public WasmStructAccess structAccess() { + return structAccess; + } + + public boolean isFinal() { + return recursiveTypes.subTypes()[subTypeIndex].isFinal(); + } + + public HeapType superType() { + return recursiveTypes.subTypes()[subTypeIndex].superType(); + } + + public CompositeType expand() { + return recursiveTypes.subTypes()[subTypeIndex].compositeType(); + } + + public ArrayType asArrayType() { + assert isArrayType(); + return (ArrayType) expand(); + } + + public StructType asStructType() { + assert isStructType(); + return (StructType) expand(); + } + + public FunctionType asFunctionType() { + assert isFunctionType(); + return (FunctionType) expand(); + } + + @Override + public HeapKind heapKind() { + return HeapKind.DefinedType; + } + + @Override + public boolean isSubtypeOf(HeapType thatHeapType) { + if (thatHeapType instanceof DefinedType that) { + return isSubtypeOf(that); + } else { + return expand().isSubtypeOf(thatHeapType); + } + } + + @TruffleBoundary + public boolean isSubtypeOf(DefinedType that) { + if (this.recursiveTypes == that.recursiveTypes && this.subTypeIndex == that.subTypeIndex) { + return true; + } + if (this.recursiveTypes.equals(that.recursiveTypes) && this.subTypeIndex == that.subTypeIndex) { + return true; + } + return this.recursiveTypes.subTypes()[subTypeIndex].superType() != null && this.recursiveTypes.subTypes()[subTypeIndex].superType().isSubtypeOf(that); + } + + @Override + public boolean isArrayType() { + return expand() instanceof ArrayType; + } + + @Override + public boolean isStructType() { + return expand() instanceof StructType; + } + + @Override + public boolean isFunctionType() { + return expand() instanceof FunctionType; + } + + @Override + public boolean matchesValue(Object value) { + return value instanceof WasmTypedHeapObject heapValue && heapValue.type().isSubtypeOf(this); + } + + @Override + public void unroll(@SuppressWarnings("hiding") RecursiveTypes recursiveTypes) { + if (recursiveReference) { + this.recursiveTypes = recursiveTypes; + } + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DefinedType that && this.recursiveReference == that.recursiveReference && this.subTypeIndex == that.subTypeIndex && + (recursiveReference || this.recursiveTypes.equals(that.recursiveTypes)); + } + + @Override + public int hashCode() { + return recursiveReference ? subTypeIndex : Objects.hash(subTypeIndex, recursiveTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + if (recursiveReference) { + return "$" + subTypeIndex; + } else if (recursiveTypes.subTypes().length == 1 && recursiveTypes.subTypes()[0].isFinal()) { + return recursiveTypes.subTypes()[0].compositeType().toString(); + } else { + return recursiveTypes + "." + subTypeIndex; + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FieldType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FieldType.java new file mode 100644 index 000000000000..6e6473e8d7dc --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FieldType.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; +import org.graalvm.wasm.constants.Mutability; + +import java.util.Objects; + +public record FieldType(StorageType storageType, byte mutability) { + + public boolean isSubtypeOf(FieldType that) { + if (this.mutability != that.mutability) { + return false; + } + if (this.mutability == Mutability.MUTABLE) { + return this.storageType.equals(that.storageType); + } else { + return this.storageType.isSubtypeOf(that.storageType); + } + } + + public void unroll(RecursiveTypes recursiveTypes) { + storageType.unroll(recursiveTypes); + } + + public Class javaClass() { + return storageType.javaClass(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof FieldType that && this.storageType.equals(that.storageType) && this.mutability == that.mutability; + } + + @Override + public int hashCode() { + return Objects.hash(storageType, mutability); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + return mutability == Mutability.MUTABLE ? "(mut " + storageType + ")" : storageType.toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FunctionType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FunctionType.java new file mode 100644 index 000000000000..45f6b510be5e --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/FunctionType.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; + +import java.util.Arrays; + +public record FunctionType(@CompilerDirectives.CompilationFinal(dimensions = 1) ValueType[] paramTypes, + @CompilerDirectives.CompilationFinal(dimensions = 1) ValueType[] resultTypes) implements CompositeType { + + @Override + public CompositeKind compositeKind() { + return CompositeKind.Function; + } + + @Override + public boolean isSubtypeOf(HeapType thatHeapType) { + if (thatHeapType == AbstractHeapType.FUNC) { + return true; + } + if (!(thatHeapType instanceof DefinedType thatDefinedType && thatDefinedType.expand() instanceof FunctionType that)) { + return false; + } + if (this.paramTypes.length != that.paramTypes.length) { + return false; + } + for (int i = 0; i < this.paramTypes.length; i++) { + CompilerAsserts.partialEvaluationConstant(this.paramTypes[i]); + if (!that.paramTypes[i].isSubtypeOf(this.paramTypes[i])) { + return false; + } + } + if (this.resultTypes.length != that.resultTypes.length) { + return false; + } + for (int i = 0; i < this.resultTypes.length; i++) { + CompilerAsserts.partialEvaluationConstant(this.resultTypes[i]); + if (!this.resultTypes[i].isSubtypeOf(that.resultTypes[i])) { + return false; + } + } + return true; + } + + @Override + public void unroll(RecursiveTypes recursiveTypes) { + for (ValueType paramType : paramTypes) { + paramType.unroll(recursiveTypes); + } + for (ValueType resultType : resultTypes) { + resultType.unroll(recursiveTypes); + } + } + + @Override + public boolean equals(Object obj) { + return obj instanceof FunctionType that && Arrays.equals(this.paramTypes, that.paramTypes) && Arrays.equals(this.resultTypes, that.resultTypes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(paramTypes) ^ Arrays.hashCode(resultTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + StringBuilder sb = new StringBuilder(); + sb.append("(func"); + for (int i = 0; i < paramTypes.length; i++) { + sb.append(" (param "); + sb.append(paramTypes[i]); + sb.append(")"); + } + for (int i = 0; i < resultTypes.length; i++) { + sb.append(" (result "); + sb.append(resultTypes[i]); + sb.append(")"); + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/HeapType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/HeapType.java new file mode 100644 index 000000000000..9566c2e31e73 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/HeapType.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +public sealed interface HeapType permits AbstractHeapType, DefinedType { + + // This is a workaround until we can use pattern matching in JDK 21+. + enum HeapKind { + Abstract, + DefinedType + } + + HeapKind heapKind(); + + boolean isSubtypeOf(HeapType that); + + boolean isArrayType(); + + boolean isStructType(); + + boolean isFunctionType(); + + boolean matchesValue(Object value); + + default void unroll(@SuppressWarnings("unused") RecursiveTypes recursiveTypes) { + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/NumberType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/NumberType.java new file mode 100644 index 000000000000..8bca158a3407 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/NumberType.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import org.graalvm.wasm.WasmType; + +public enum NumberType implements ValueType { + + I32(WasmType.I32_TYPE), + I64(WasmType.I64_TYPE), + F32(WasmType.F32_TYPE), + F64(WasmType.F64_TYPE); + + private final int value; + + NumberType(int value) { + this.value = value; + } + + public int value() { + return value; + } + + @Override + public ValueKind valueKind() { + return ValueKind.Number; + } + + @Override + public boolean isSubtypeOf(ValueType that) { + return this == that; + } + + @Override + public boolean isSubtypeOf(StorageType that) { + return this == that; + } + + @Override + public Class javaClass() { + return switch (this) { + case I32 -> int.class; + case I64 -> long.class; + case F32 -> float.class; + case F64 -> double.class; + }; + } + + @Override + public boolean matchesValue(Object val) { + return switch (this) { + case I32 -> val instanceof Integer; + case I64 -> val instanceof Long; + case F32 -> val instanceof Float; + case F64 -> val instanceof Double; + }; + } + + @Override + public String toString() { + return switch (this) { + case I32 -> "i32"; + case I64 -> "i64"; + case F32 -> "f32"; + case F64 -> "f64"; + }; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/PackedType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/PackedType.java new file mode 100644 index 000000000000..68739cdbe972 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/PackedType.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +public enum PackedType implements StorageType { + + I8, + I16; + + @Override + public StorageKind storageKind() { + return StorageKind.Packed; + } + + @Override + public boolean isSubtypeOf(StorageType that) { + return this == that; + } + + @Override + public Class javaClass() { + return switch (this) { + case I8 -> byte.class; + case I16 -> short.class; + }; + } + + @Override + public String toString() { + return switch (this) { + case I8 -> "i8"; + case I16 -> "i16"; + }; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/RecursiveTypes.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/RecursiveTypes.java new file mode 100644 index 000000000000..526d74d15a57 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/RecursiveTypes.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; + +import java.util.Arrays; + +public record RecursiveTypes(SubType[] subTypes) { + + @Override + public boolean equals(Object obj) { + return obj instanceof RecursiveTypes that && Arrays.equals(this.subTypes, that.subTypes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(subTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + StringBuilder sb = new StringBuilder(); + sb.append("(rec"); + for (int i = 0; i < subTypes.length; i++) { + sb.append(" "); + sb.append(subTypes[i]); + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ReferenceType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ReferenceType.java new file mode 100644 index 000000000000..584f7385e0f3 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ReferenceType.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; +import org.graalvm.wasm.WasmConstant; + +public record ReferenceType(boolean nullable, HeapType heapType) implements ValueType { + + public static ReferenceType FUNCREF = new ReferenceType(true, AbstractHeapType.FUNC); + public static ReferenceType EXTERNREF = new ReferenceType(true, AbstractHeapType.EXTERN); + public static ReferenceType EXNREF = new ReferenceType(true, AbstractHeapType.EXN); + + @Override + public boolean isSubtypeOf(ValueType thatValueType) { + return thatValueType instanceof ReferenceType that && (!this.nullable || that.nullable) && this.heapType.isSubtypeOf(that.heapType); + } + + @Override + public boolean isSubtypeOf(StorageType thatStorageType) { + return thatStorageType instanceof ReferenceType thatReferenceType && isSubtypeOf(thatReferenceType); + } + + @Override + public Class javaClass() { + return Object.class; + } + + @Override + public boolean matchesValue(Object value) { + return nullable() && value == WasmConstant.NULL || heapType().matchesValue(value); + } + + @Override + public ValueKind valueKind() { + return ValueKind.Reference; + } + + @Override + public void unroll(RecursiveTypes recursiveTypes) { + heapType.unroll(recursiveTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + StringBuilder buf = new StringBuilder(); + buf.append("(ref "); + if (nullable) { + buf.append("null "); + } + buf.append(heapType.toString()); + buf.append(")"); + return buf.toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StorageType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StorageType.java new file mode 100644 index 000000000000..e205630b748e --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StorageType.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +public sealed interface StorageType permits ValueType, PackedType { + + // This is a workaround until we can use pattern matching in JDK 21+. + enum StorageKind { + Value, + Packed + } + + StorageKind storageKind(); + + boolean isSubtypeOf(StorageType that); + + Class javaClass(); + + default void unroll(@SuppressWarnings("unused") RecursiveTypes recursiveTypes) { + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StructType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StructType.java new file mode 100644 index 000000000000..2a6f1873c633 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/StructType.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; + +import java.util.Arrays; + +public record StructType( + @CompilerDirectives.CompilationFinal(dimensions = 1) FieldType[] fieldTypes) implements CompositeType { + + @Override + public CompositeKind compositeKind() { + return CompositeKind.Struct; + } + + @Override + public boolean isSubtypeOf(HeapType thatHeapType) { + if (thatHeapType == AbstractHeapType.STRUCT || thatHeapType == AbstractHeapType.EQ || thatHeapType == AbstractHeapType.ANY) { + return true; + } + if (!(thatHeapType instanceof DefinedType thatDefinedType && thatDefinedType.expand() instanceof StructType that)) { + return false; + } + if (this.fieldTypes.length < that.fieldTypes.length) { + return false; + } + for (int i = 0; i < that.fieldTypes.length; i++) { + if (!this.fieldTypes[i].isSubtypeOf(that.fieldTypes[i])) { + return false; + } + } + return true; + } + + @Override + public void unroll(RecursiveTypes recursiveTypes) { + for (FieldType fieldType : fieldTypes) { + fieldType.unroll(recursiveTypes); + } + } + + @Override + public boolean equals(Object obj) { + return obj instanceof StructType that && Arrays.equals(this.fieldTypes, that.fieldTypes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(fieldTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + StringBuilder sb = new StringBuilder(); + sb.append("(struct"); + for (int i = 0; i < fieldTypes.length; i++) { + sb.append(" (field "); + sb.append(fieldTypes[i]); + sb.append(")"); + } + sb.append(")"); + return sb.toString(); + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/SubType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/SubType.java new file mode 100644 index 000000000000..4515898f750e --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/SubType.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import com.oracle.truffle.api.CompilerAsserts; + +public record SubType(boolean isFinal, DefinedType superType, CompositeType compositeType) { + + public void unroll(RecursiveTypes recursiveTypes) { + if (superType != null) { + superType.unroll(recursiveTypes); + } + compositeType.unroll(recursiveTypes); + } + + @Override + public String toString() { + CompilerAsserts.neverPartOfCompilation(); + if (isFinal && superType == null) { + return compositeType.toString(); + } else { + StringBuilder sb = new StringBuilder(); + sb.append("(sub"); + if (superType != null) { + sb.append(" (super "); + sb.append(superType); + sb.append(")"); + } + sb.append(" "); + sb.append(compositeType); + sb.append(")"); + return sb.toString(); + } + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ValueType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ValueType.java new file mode 100644 index 000000000000..b3a6d3763367 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/ValueType.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +/** + * Represents a WebAssembly value type in its closed form, with all type indices replaced with their + * definitions. You can query the subtyping relation on types using the predicate + * {@link #isSubtypeOf(ValueType)}. + *

+ * If you need to check whether two types are equivalent, instead of checking + * {@code A.isSubtypeOf(B) && B.isSubtypeOf(A)}, you can use {@code A.equals(B)}, since, in the + * WebAssembly type system, type equivalence corresponds to structural equality. + *

+ */ +public sealed interface ValueType extends StorageType permits NumberType, VectorType, ReferenceType { + + // This is a workaround until we can use pattern matching in JDK 21+. + enum ValueKind { + Number, + Vector, + Reference + } + + ValueKind valueKind(); + + @Override + default StorageKind storageKind() { + return StorageKind.Value; + } + + boolean isSubtypeOf(ValueType that); + + boolean matchesValue(Object value); +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/VectorType.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/VectorType.java new file mode 100644 index 000000000000..ae587e794125 --- /dev/null +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/types/VectorType.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.wasm.types; + +import org.graalvm.wasm.WasmType; +import org.graalvm.wasm.vector.Vector128; + +public enum VectorType implements ValueType { + + V128(WasmType.V128_TYPE); + + private final int value; + + VectorType(int value) { + this.value = value; + } + + public int value() { + return value; + } + + @Override + public ValueKind valueKind() { + return ValueKind.Vector; + } + + @Override + public boolean isSubtypeOf(ValueType that) { + return this == that; + } + + @Override + public boolean isSubtypeOf(StorageType that) { + return this == that; + } + + @Override + public Class javaClass() { + return Vector128.class; + } + + @Override + public boolean matchesValue(Object val) { + return val instanceof Vector128; + } + + @Override + public String toString() { + return "v128"; + } +} diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128.java similarity index 99% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128.java index c210754bf128..69bbc87b8f6e 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128.java @@ -38,7 +38,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Ops.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Ops.java similarity index 99% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Ops.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Ops.java index 7e790647e10a..336850267dac 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Ops.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Ops.java @@ -39,7 +39,7 @@ * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; public interface Vector128Ops { diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsFallback.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsFallback.java similarity index 99% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsFallback.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsFallback.java index 8007deca7a63..535d4733ecb4 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsFallback.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsFallback.java @@ -39,7 +39,7 @@ * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.ExactMath; @@ -49,13 +49,13 @@ import java.util.Arrays; -import static org.graalvm.wasm.api.Vector128.BYTES; -import static org.graalvm.wasm.api.Vector128.BYTE_LENGTH; -import static org.graalvm.wasm.api.Vector128.DOUBLE_LENGTH; -import static org.graalvm.wasm.api.Vector128.FLOAT_LENGTH; -import static org.graalvm.wasm.api.Vector128.INT_LENGTH; -import static org.graalvm.wasm.api.Vector128.LONG_LENGTH; -import static org.graalvm.wasm.api.Vector128.SHORT_LENGTH; +import static org.graalvm.wasm.vector.Vector128.BYTES; +import static org.graalvm.wasm.vector.Vector128.BYTE_LENGTH; +import static org.graalvm.wasm.vector.Vector128.DOUBLE_LENGTH; +import static org.graalvm.wasm.vector.Vector128.FLOAT_LENGTH; +import static org.graalvm.wasm.vector.Vector128.INT_LENGTH; +import static org.graalvm.wasm.vector.Vector128.LONG_LENGTH; +import static org.graalvm.wasm.vector.Vector128.SHORT_LENGTH; /** * This is an implementation of the GraalWasm SIMD proposal. It uses {@code byte[]}s as a diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java similarity index 98% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java index fb988a023849..827831a6c7db 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128OpsVectorAPI.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128OpsVectorAPI.java @@ -39,7 +39,7 @@ * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; /** * On JDK 25+, this class holds an implementation of the GraalWasm SIMD proposal that uses the diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Shape.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Shape.java similarity index 99% rename from wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Shape.java rename to wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Shape.java index e9ff2fe3c71d..62ea54adc7a6 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/Vector128Shape.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/vector/Vector128Shape.java @@ -39,7 +39,7 @@ * SOFTWARE. */ -package org.graalvm.wasm.api; +package org.graalvm.wasm.vector; import org.graalvm.wasm.WasmType; import org.graalvm.wasm.constants.Instructions;