From 56e0bf4a86f7454aac0ffd2d6192bf609fc1a6a9 Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Mon, 29 Apr 2024 15:37:28 +0200 Subject: [PATCH 01/21] Fix error message in bounds check --- wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java index ffeadacc62b5..b0b116ca5a0b 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java @@ -70,7 +70,7 @@ public static void assertIntEqual(int actual, int expected, String message, Fail public static void assertIntGreaterOrEqual(int n1, int n2, Failure failure) throws WasmException { if (n1 < n2) { - fail(failure, format("%s: %d should be > %d", failure.name, n1, n2)); + fail(failure, format("%s: %d should be >= %d", failure.name, n1, n2)); } } From 34b5b0b9f2775fb4582626855d575e15500f1e08 Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Mon, 29 Apr 2024 15:37:54 +0200 Subject: [PATCH 02/21] Parse vector opcodes using LEB This is forward-compatible with SIMD extensions and provides better error reporting when encountering such unsupported extensions. --- .../src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 426907f5b32e..81585f82eff0 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 @@ -1802,7 +1802,7 @@ private void readNumericInstructions(ParserState state, int opcode) { break; case Instructions.VECTOR: checkSIMDSupport(); - int vectorOpcode = read1() & 0xFF; + int vectorOpcode = readUnsignedInt32(); state.addVectorFlag(); switch (vectorOpcode) { case Instructions.VECTOR_V128_LOAD: From a86d2939270350cdc5b018628a1fca3504e54327 Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Thu, 2 May 2024 11:53:20 +0200 Subject: [PATCH 03/21] Store JS memory callbacks per context, not per memory This fixes cases when such callbacks are needed for memory instances which didn't go through the import/export boundary to/from JS. --- .../src/org/graalvm/wasm/WasmContext.java | 43 +++++++++ .../src/org/graalvm/wasm/api/WebAssembly.java | 87 ++++++++++++------- .../wasm/memory/ByteArrayWasmMemory.java | 6 +- .../graalvm/wasm/memory/NativeWasmMemory.java | 6 +- .../graalvm/wasm/memory/UnsafeWasmMemory.java | 6 +- .../org/graalvm/wasm/memory/WasmMemory.java | 47 +--------- 6 files changed, 114 insertions(+), 81 deletions(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContext.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContext.java index fca78769a116..36f51b2ab44f 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContext.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/WasmContext.java @@ -67,6 +67,19 @@ public final class WasmContext { private final FdManager filesManager; private final WasmContextOptions contextOptions; + /** + * Optional grow callback to notify the embedder. + */ + private Object memGrowCallback; + /** + * JS callback to implement part of memory.atomic.notify. + */ + private Object memNotifyCallback; + /** + * JS callback to implement part of memory.atomic.waitN. + */ + private Object memWaitCallback; + public WasmContext(Env env, WasmLanguage language) { this.env = env; this.language = language; @@ -222,4 +235,34 @@ public WasmContextOptions getContextOptions() { public static WasmContext get(Node node) { return REFERENCE.get(node); } + + public void setMemGrowCallback(Object callback) { + this.memGrowCallback = callback; + } + + public Object getMemGrowCallback() { + return memGrowCallback; + } + + public void setMemNotifyCallback(Object callback) { + this.memNotifyCallback = callback; + } + + public Object getMemNotifyCallback() { + return memNotifyCallback; + } + + public void setMemWaitCallback(Object callback) { + this.memWaitCallback = callback; + } + + public Object getMemWaitCallback() { + return memWaitCallback; + } + + public void inheritCallbacksFromParentContext(WasmContext parent) { + setMemGrowCallback(parent.getMemGrowCallback()); + setMemNotifyCallback(parent.getMemNotifyCallback()); + setMemWaitCallback(parent.getMemWaitCallback()); + } } 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 46c76d6b9858..a7d791c269ab 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 @@ -81,6 +81,7 @@ import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; public class WebAssembly extends Dictionary { private final WasmContext currentContext; @@ -142,6 +143,7 @@ public WasmInstance moduleInstantiate(WasmModule module, Object importObject) { final Object prev = innerTruffleContext.enter(null); try { final WasmContext instanceContext = WasmContext.get(null); + instanceContext.inheritCallbacksFromParentContext(currentContext); WasmInstance instance = instantiateModule(module, importObject, instanceContext, innerTruffleContext); instanceContext.linker().tryLink(instance); return instance; @@ -705,24 +707,33 @@ public static long memGrow(WasmMemory memory, int delta) { } private static Object memSetGrowCallback(Object[] args) { + checkArgumentCount(args, 1); InteropLibrary lib = InteropLibrary.getUncached(); - if (!(args[0] instanceof WasmMemory)) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be wasm memory"); + if (args.length > 1) { + // TODO: drop this branch after JS adopts the single-argument version + if (!(args[0] instanceof WasmMemory)) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be executable"); + } + if (!lib.isExecutable(args[1])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + } + return memSetGrowCallback(args[1]); } - if (!lib.isExecutable(args[1])) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + if (!lib.isExecutable(args[0])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Argument must be executable"); } - WasmMemory memory = (WasmMemory) args[0]; - return memSetGrowCallback(memory, args[1]); + return memSetGrowCallback(args[0]); } - private static Object memSetGrowCallback(WasmMemory memory, Object callback) { - memory.setGrowCallback(callback); + private static Object memSetGrowCallback(Object callback) { + WasmContext context = WasmContext.get(null); + context.setMemGrowCallback(callback); return WasmConstant.VOID; } public static void invokeMemGrowCallback(WasmMemory memory) { - Object callback = memory.getGrowCallback(); + WasmContext context = WasmContext.get(null); + Object callback = context.getMemGrowCallback(); if (callback != null) { InteropLibrary lib = InteropLibrary.getUncached(); try { @@ -734,24 +745,33 @@ public static void invokeMemGrowCallback(WasmMemory memory) { } private static Object memSetNotifyCallback(Object[] args) { + checkArgumentCount(args, 1); InteropLibrary lib = InteropLibrary.getUncached(); - if (!(args[0] instanceof WasmMemory)) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be wasm memory"); + if (args.length > 1) { + // TODO: drop this branch after JS adopts the single-argument version + if (!(args[0] instanceof WasmMemory)) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be executable"); + } + if (!lib.isExecutable(args[1])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + } + return memSetNotifyCallback(args[1]); } - if (!lib.isExecutable(args[1])) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + if (!lib.isExecutable(args[0])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Argument must be executable"); } - WasmMemory memory = (WasmMemory) args[0]; - return memSetNotifyCallback(memory, args[1]); + return memSetNotifyCallback(args[0]); } - private static Object memSetNotifyCallback(WasmMemory memory, Object callback) { - memory.setNotifyCallback(callback); + private static Object memSetNotifyCallback(Object callback) { + WasmContext context = WasmContext.get(null); + context.setMemNotifyCallback(callback); return WasmConstant.VOID; } - public static int invokeMemNotifyCallback(WasmMemory memory, long address, int count) { - Object callback = memory.getNotifyCallback(); + public static int invokeMemNotifyCallback(Node node, WasmMemory memory, long address, int count) { + WasmContext context = WasmContext.get(node); + Object callback = context.getMemNotifyCallback(); if (callback != null) { InteropLibrary lib = InteropLibrary.getUncached(); try { @@ -764,24 +784,33 @@ public static int invokeMemNotifyCallback(WasmMemory memory, long address, int c } private static Object memSetWaitCallback(Object[] args) { + checkArgumentCount(args, 1); InteropLibrary lib = InteropLibrary.getUncached(); - if (!(args[0] instanceof WasmMemory)) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be wasm memory"); + if (args.length > 1) { + // TODO: drop this branch after JS adopts the single-argument version + if (!(args[0] instanceof WasmMemory)) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "First argument must be executable"); + } + if (!lib.isExecutable(args[1])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + } + return memSetWaitCallback(args[1]); } - if (!lib.isExecutable(args[1])) { - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Second argument must be executable"); + if (!lib.isExecutable(args[0])) { + throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Argument must be executable"); } - WasmMemory memory = (WasmMemory) args[0]; - return memSetWaitCallback(memory, args[1]); + return memSetWaitCallback(args[0]); } - private static Object memSetWaitCallback(WasmMemory memory, Object callback) { - memory.setWaitCallback(callback); + private static Object memSetWaitCallback(Object callback) { + WasmContext context = WasmContext.get(null); + context.setMemWaitCallback(callback); return WasmConstant.VOID; } - public static int invokeMemWaitCallback(WasmMemory memory, long address, long expected, long timeout, boolean is64) { - Object callback = memory.getWaitCallback(); + public static int invokeMemWaitCallback(Node node, WasmMemory memory, long address, long expected, long timeout, boolean is64) { + WasmContext context = WasmContext.get(node); + Object callback = context.getMemWaitCallback(); if (callback != null) { InteropLibrary lib = InteropLibrary.getUncached(); try { 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 799ff53f1139..22f3c6500c04 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 @@ -983,7 +983,7 @@ public int atomic_notify(Node node, long address, int count) { if (!this.isShared()) { return 0; } - return invokeNotifyCallback(address, count); + return invokeNotifyCallback(node, address, count); } @Override @@ -996,7 +996,7 @@ public int atomic_wait32(Node node, long address, int expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, false); + return invokeWaitCallback(node, address, expected, timeout, false); } @Override @@ -1009,7 +1009,7 @@ public int atomic_wait64(Node node, long address, long expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, true); + return invokeWaitCallback(node, address, expected, timeout, true); } @Override 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 a195fd338226..28934acfa2a0 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 @@ -899,7 +899,7 @@ public int atomic_notify(Node node, long address, int count) { if (!this.isShared()) { return 0; } - return invokeNotifyCallback(address, count); + return invokeNotifyCallback(node, address, count); } @Override @@ -910,7 +910,7 @@ public int atomic_wait32(Node node, long address, int expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, false); + return invokeWaitCallback(node, address, expected, timeout, false); } @Override @@ -921,7 +921,7 @@ public int atomic_wait64(Node node, long address, long expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, true); + return invokeWaitCallback(node, address, expected, timeout, true); } @Override 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 10ce0f7c815c..538860ad52d4 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 @@ -901,7 +901,7 @@ public int atomic_notify(Node node, long address, int count) { if (!this.isShared()) { return 0; } - return invokeNotifyCallback(address, count); + return invokeNotifyCallback(node, address, count); } @Override @@ -912,7 +912,7 @@ public int atomic_wait32(Node node, long address, int expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, false); + return invokeWaitCallback(node, address, expected, timeout, false); } @Override @@ -923,7 +923,7 @@ public int atomic_wait64(Node node, long address, long expected, long timeout) { if (!this.isShared()) { throw trapUnsharedMemory(node); } - return invokeWaitCallback(address, expected, timeout, true); + return invokeWaitCallback(node, address, expected, timeout, true); } @Override 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 5af7dbbdc23a..75beffad1143 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 @@ -109,21 +109,6 @@ public abstract class WasmMemory extends EmbedderDataHolder implements TruffleOb */ protected final long maxAllowedSize; - /** - * Optional grow callback to notify the embedder. - */ - private Object growCallback; - - /** - * JS callback to implement part of memory.atomic.notify. - */ - private Object notifyCallback; - - /** - * JS callback to implement part of memory.atomic.waitN. - */ - private Object waitCallback; - /** * @see #hasIndexType64() */ @@ -820,40 +805,16 @@ public void writeArrayElement(long address, Object value, store_i32_8(null, address, rawValue); } - public void setGrowCallback(Object growCallback) { - this.growCallback = growCallback; - } - - public Object getGrowCallback() { - return growCallback; - } - protected void invokeGrowCallback() { WebAssembly.invokeMemGrowCallback(this); } - public void setNotifyCallback(Object notifyCallback) { - this.notifyCallback = notifyCallback; - } - - public Object getNotifyCallback() { - return notifyCallback; - } - - protected int invokeNotifyCallback(long address, int count) { - return WebAssembly.invokeMemNotifyCallback(this, address, count); - } - - public void setWaitCallback(Object waitCallback) { - this.waitCallback = waitCallback; - } - - public Object getWaitCallback() { - return waitCallback; + protected int invokeNotifyCallback(Node node, long address, int count) { + return WebAssembly.invokeMemNotifyCallback(node, this, address, count); } - protected int invokeWaitCallback(long address, long expected, long timeout, boolean is64) { - return WebAssembly.invokeMemWaitCallback(this, address, expected, timeout, is64); + protected int invokeWaitCallback(Node node, long address, long expected, long timeout, boolean is64) { + return WebAssembly.invokeMemWaitCallback(node, this, address, expected, timeout, is64); } public abstract void close(); From 631f29d5068a326c7badce5067c7e840ac480e6d Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Mon, 6 May 2024 12:10:19 +0200 Subject: [PATCH 04/21] Parse atomic opcodes using LEB --- .../src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 81585f82eff0..faf2f52c47de 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 @@ -1525,7 +1525,7 @@ private void readNumericInstructions(ParserState state, int opcode) { break; case Instructions.ATOMIC: checkThreadsSupport(opcode); - int atomicOpcode = read1() & 0xFF; + int atomicOpcode = readUnsignedInt32(); state.addAtomicFlag(); switch (atomicOpcode) { case Instructions.ATOMIC_NOTIFY: From 140160152f6adddc6d1827c3d0a0bde2b67a62fb Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Mon, 6 May 2024 17:20:07 +0200 Subject: [PATCH 05/21] Differentiate between JS's null and undefined values in wasm JS already takes care to pass in WebAssembly's ref.null when it wants to express null. Other times, it might need to pass in undefined and have it be preserved as undefined. The existing handling of interop null values was redundant. --- .../src/org/graalvm/wasm/api/WebAssembly.java | 26 +++---------------- 1 file changed, 4 insertions(+), 22 deletions(-) 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 a7d791c269ab..4a77cf084ff2 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 @@ -513,9 +513,6 @@ private static Object tableGrow(Object[] args) { WasmTable table = (WasmTable) args[0]; int delta = (Integer) args[1]; if (args.length > 2) { - if (InteropLibrary.getUncached().isNull(args[2])) { - return tableGrow(table, delta, WasmConstant.NULL); - } return tableGrow(table, delta, args[2]); } return tableGrow(table, delta, WasmConstant.NULL); @@ -868,23 +865,14 @@ public WasmGlobal globalAlloc(ValueType valueType, boolean mutable, Object value case f64: return new DefaultWasmGlobal(valueType, mutable, Double.doubleToRawLongBits(valueInterop.asDouble(value))); case anyfunc: - if (!refTypes) { + if (!refTypes || !(value == WasmConstant.NULL || value instanceof WasmFunctionInstance)) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type"); } - if (valueInterop.isNull(value)) { - return new DefaultWasmGlobal(valueType, mutable, WasmConstant.NULL); - } else if (value instanceof WasmFunctionInstance) { - return new DefaultWasmGlobal(valueType, mutable, value); - } - throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type"); - + return new DefaultWasmGlobal(valueType, mutable, value); case externref: if (!refTypes) { throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type"); } - if (valueInterop.isNull(value)) { - return new DefaultWasmGlobal(valueType, mutable, WasmConstant.NULL); - } return new DefaultWasmGlobal(valueType, mutable, value); default: throw new WasmJsApiException(WasmJsApiException.Kind.TypeError, "Invalid value type"); @@ -964,9 +952,7 @@ public Object globalWrite(WasmGlobal global, Object value) { if (!refTypes) { throw WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Reference types are not enabled"); } - if (InteropLibrary.getUncached(value).isNull(value)) { - global.storeObject(WasmConstant.NULL); - } else if (!(value instanceof WasmFunctionInstance)) { + if (!(value == WasmConstant.NULL || value instanceof WasmFunctionInstance)) { throw WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Global type %s, value: %s", valueType, value); } else { global.storeObject(value); @@ -976,11 +962,7 @@ public Object globalWrite(WasmGlobal global, Object value) { if (!refTypes) { throw WasmJsApiException.format(WasmJsApiException.Kind.TypeError, "Invalid value type. Reference types are not enabled"); } - if (InteropLibrary.getUncached(value).isNull(value)) { - global.storeObject(WasmConstant.NULL); - } else { - global.storeObject(value); - } + global.storeObject(value); break; } return WasmConstant.VOID; From 7a700a8aa605fa3cdb12e6fffcbc9333e786e62d Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Mon, 6 May 2024 17:54:49 +0200 Subject: [PATCH 06/21] Check types when importing functions from JS which were exported to JS --- .../src/org/graalvm/wasm/api/ImportModule.java | 4 ++-- .../src/org/graalvm/wasm/predefined/BuiltinModule.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ImportModule.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ImportModule.java index 3a789bfe24d2..d9b9f3d3e7c7 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ImportModule.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/api/ImportModule.java @@ -86,8 +86,8 @@ protected WasmInstance createInstance(WasmLanguage language, WasmContext context final WasmFunction function = info.getLeft(); final Object executable = info.getRight(); final SymbolTable.FunctionType type = function.type(); - if (executable instanceof WasmFunctionInstance) { - defineExportedFunction(instance, functionName, type.paramTypes(), type.resultTypes(), (WasmFunctionInstance) executable); + if (executable instanceof WasmFunctionInstance functionInstance) { + defineExportedFunction(instance, functionName, functionInstance); } else { var executableWrapper = new ExecuteInParentContextNode(context.language(), module, executable, function.resultCount()); WasmFunction exported = defineFunction(context, module, functionName, type.paramTypes(), type.resultTypes(), executableWrapper); 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 6b4e1a83cd1f..ce59231abc9c 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 @@ -43,6 +43,7 @@ import java.util.HashMap; import java.util.Map; +import org.graalvm.wasm.SymbolTable; import org.graalvm.wasm.WasmContext; import org.graalvm.wasm.WasmFunction; import org.graalvm.wasm.WasmFunctionInstance; @@ -109,8 +110,9 @@ protected WasmInstance createInstance(WasmLanguage language, WasmContext context return instance; } - protected void defineExportedFunction(WasmInstance instance, String name, byte[] paramType, byte[] retTypes, WasmFunctionInstance functionInstance) { - final int typeIdx = instance.symbolTable().allocateFunctionType(paramType, retTypes, instance.context().getContextOptions().supportMultiValue()); + protected void defineExportedFunction(WasmInstance instance, String name, WasmFunctionInstance functionInstance) { + SymbolTable.FunctionType type = functionInstance.function().type(); + final int typeIdx = instance.symbolTable().allocateFunctionType(type.paramTypes(), type.resultTypes(), instance.context().getContextOptions().supportMultiValue()); final WasmFunction function = instance.symbolTable().declareExportedFunction(typeIdx, name); instance.setFunctionInstance(function.index(), functionInstance); instance.setTarget(function.index(), functionInstance.target()); From a52770e88a8a686582fe7a291924cd6b7e877f04 Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Tue, 7 May 2024 11:40:12 +0200 Subject: [PATCH 07/21] Check out-of-bounds access in memory.waitN instructions --- .../src/org/graalvm/wasm/memory/NativeWasmMemory.java | 4 ++-- .../src/org/graalvm/wasm/memory/UnsafeWasmMemory.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 28934acfa2a0..5100f82a59e4 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 @@ -905,7 +905,7 @@ public int atomic_notify(Node node, long address, int count) { @Override @TruffleBoundary public int atomic_wait32(Node node, long address, int expected, long timeout) { - validateAtomicAddress(node, address, 4); + validateAddress(node, address, 4); validateAtomicAddress(node, address, 4); if (!this.isShared()) { throw trapUnsharedMemory(node); @@ -916,7 +916,7 @@ public int atomic_wait32(Node node, long address, int expected, long timeout) { @Override @TruffleBoundary public int atomic_wait64(Node node, long address, long expected, long timeout) { - validateAtomicAddress(node, address, 8); + validateAddress(node, address, 8); validateAtomicAddress(node, address, 8); if (!this.isShared()) { throw trapUnsharedMemory(node); 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 538860ad52d4..5277fb15f785 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 @@ -907,7 +907,7 @@ public int atomic_notify(Node node, long address, int count) { @Override @TruffleBoundary public int atomic_wait32(Node node, long address, int expected, long timeout) { - validateAtomicAddress(node, address, 4); + validateAddress(node, address, 4); validateAtomicAddress(node, address, 4); if (!this.isShared()) { throw trapUnsharedMemory(node); @@ -918,7 +918,7 @@ public int atomic_wait32(Node node, long address, int expected, long timeout) { @Override @TruffleBoundary public int atomic_wait64(Node node, long address, long expected, long timeout) { - validateAtomicAddress(node, address, 8); + validateAddress(node, address, 8); validateAtomicAddress(node, address, 8); if (!this.isShared()) { throw trapUnsharedMemory(node); From 7b27f4add44fa841c5a03003f53ce348e5f5de7f Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Tue, 7 May 2024 13:47:01 +0200 Subject: [PATCH 08/21] Switch TruffleContext when calling foreign start function --- .../src/org/graalvm/wasm/Linker.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) 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 e524f1e4c610..0d9c13566c08 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 @@ -103,6 +103,7 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleContext; public class Linker { public enum LinkState { @@ -248,8 +249,25 @@ private static void runStartFunctions(Map instances, Array static void runStartFunction(WasmInstance instance) { final WasmFunction start = instance.symbolTable().startFunction(); if (start != null) { - WasmInstance targetInstance = !start.isImported() ? instance : instance.functionInstance(start.index()).moduleInstance(); - instance.target(start.index()).call(WasmArguments.create(targetInstance)); + if (start.isImported()) { + final WasmFunctionInstance functionInstance = instance.functionInstance(start.index()); + final WasmContext currentContext = WasmContext.get(null); + final WasmContext functionInstanceContext = functionInstance.context(); + if (functionInstanceContext == currentContext) { + instance.target(start.index()).call(WasmArguments.create(functionInstance.moduleInstance())); + } else { + // Enter function's context when it is not from the current one + TruffleContext truffleContext = functionInstance.getTruffleContext(); + Object prev = truffleContext.enter(null); + try { + instance.target(start.index()).call(WasmArguments.create(functionInstance.moduleInstance())); + } finally { + truffleContext.leave(null, prev); + } + } + } else { + instance.target(start.index()).call(WasmArguments.create(instance)); + } } } From fd549178150474cca12a6bfbdd4017f7e95ee8d7 Mon Sep 17 00:00:00 2001 From: Jirka Marsik Date: Thu, 16 May 2024 13:03:58 +0200 Subject: [PATCH 09/21] Fix error message in Assert class --- wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java index b0b116ca5a0b..1f92ef7c9f2d 100644 --- a/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java +++ b/wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Assert.java @@ -86,7 +86,7 @@ public static void assertIntLessOrEqual(int n1, int n2, Failure failure) throws public static void assertIntLess(int n1, int n2, Failure failure) throws WasmException { if (n1 >= n2) { - fail(failure, format("%s: %d should be <= %d", failure.name, n1, n2)); + fail(failure, format("%s: %d should be < %d", failure.name, n1, n2)); } } From 18fd852c51fe9c48e448cd3ac5c63bcade68ea4e Mon Sep 17 00:00:00 2001 From: ol-automation_ww Date: Fri, 24 May 2024 01:12:12 +0000 Subject: [PATCH 10/21] update JVMCI to 23+24-jvmci-b01 --- common.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common.json b/common.json index 957635169e80..c6126eb4ddfb 100644 --- a/common.json +++ b/common.json @@ -46,12 +46,12 @@ "graalvm-ee-21": {"name": "graalvm-java21", "version": "23.1.3", "platformspecific": true }, "oraclejdk-latest": {"name": "jpg-jdk", "version": "23", "build_id": "jdk-23+23", "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-23+23-jvmci-b01", "platformspecific": true }, - "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-23+23-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-23+23-jvmci-b01-sulong", "platformspecific": true }, - "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-23+23-jvmci-b01", "platformspecific": true }, - "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-23+23-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-23+23-jvmci-b01-sulong", "platformspecific": true } + "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958", "platformspecific": true }, + "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958-debug", "platformspecific": true }, + "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958-sulong", "platformspecific": true }, + "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91", "platformspecific": true }, + "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91-debug", "platformspecific": true }, + "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91-sulong", "platformspecific": true } }, "eclipse": { From 12ffb5b543f91bd4ba43f9afa7e303a8bac61b3b Mon Sep 17 00:00:00 2001 From: Marouane El Hallaoui Date: Fri, 24 May 2024 12:01:31 +0100 Subject: [PATCH 11/21] deploy labsjdk snapshots --- common.json | 14 +++++++------- .../graal/compiler/hotspot/JVMCIVersionCheck.java | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common.json b/common.json index c6126eb4ddfb..10bbe13fa87f 100644 --- a/common.json +++ b/common.json @@ -45,13 +45,13 @@ "labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21.0.2+13-jvmci-23.1-b33-sulong", "platformspecific": true }, "graalvm-ee-21": {"name": "graalvm-java21", "version": "23.1.3", "platformspecific": true }, - "oraclejdk-latest": {"name": "jpg-jdk", "version": "23", "build_id": "jdk-23+23", "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958", "platformspecific": true }, - "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958-debug", "platformspecific": true }, - "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-20240523181335-9fed068958-sulong", "platformspecific": true }, - "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91", "platformspecific": true }, - "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91-debug", "platformspecific": true }, - "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-20240523181335-9fed068958+9f83deba91-sulong", "platformspecific": true } + "oraclejdk-latest": {"name": "jpg-jdk", "version": "23", "build_id": "jdk-23+24", "platformspecific": true, "extrabundles": ["static-libs"]}, + "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01", "platformspecific": true }, + "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-23+24-jvmci-b01-sulong", "platformspecific": true }, + "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01", "platformspecific": true }, + "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-23+24-jvmci-b01-sulong", "platformspecific": true } }, "eclipse": { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java index 201f9503e533..cb662be9ea80 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/JVMCIVersionCheck.java @@ -55,8 +55,8 @@ public final class JVMCIVersionCheck { private static final Map> JVMCI_MIN_VERSIONS = Map.of( "21", Map.of(DEFAULT_VENDOR_ENTRY, createLegacyVersion(23, 1, 33)), "23", Map.of( - "Oracle Corporation", createLabsJDKVersion("23+23", 1), - DEFAULT_VENDOR_ENTRY, createLabsJDKVersion("23+23", 1))); + "Oracle Corporation", createLabsJDKVersion("23+24", 1), + DEFAULT_VENDOR_ENTRY, createLabsJDKVersion("23+24", 1))); private static final int NA = 0; /** * Minimum Java release supported by Graal. From fe3de1fc8cc83a64cb28d167455d8dddc10a28b0 Mon Sep 17 00:00:00 2001 From: ol-automation_ww Date: Fri, 24 May 2024 21:10:43 +0000 Subject: [PATCH 12/21] Update truffleruby import. --- vm/mx.vm/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index 0cce399b7b68..de05f74a1fac 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -49,7 +49,7 @@ }, { "name": "truffleruby", - "version": "d96438b54692b913ef5ec31424cc631f590dc130", + "version": "1adc2121259f121273e810e1f6577a54b810ad56", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/truffleruby.git", "kind": "git"}, From c2df1e368312044de9119332d9d57e2fb226f44b Mon Sep 17 00:00:00 2001 From: Francois Farquet Date: Sat, 25 May 2024 21:03:51 +0200 Subject: [PATCH 13/21] Delete leftover file from java-benchmarks suite --- .../mx.java-benchmarks/mx_java_benchmarks.py | 1981 ----------------- 1 file changed, 1981 deletions(-) delete mode 100644 java-benchmarks/mx.java-benchmarks/mx_java_benchmarks.py diff --git a/java-benchmarks/mx.java-benchmarks/mx_java_benchmarks.py b/java-benchmarks/mx.java-benchmarks/mx_java_benchmarks.py deleted file mode 100644 index bfba48dff5a4..000000000000 --- a/java-benchmarks/mx.java-benchmarks/mx_java_benchmarks.py +++ /dev/null @@ -1,1981 +0,0 @@ -# -# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -import sys -import re -import argparse -import os -from os.path import join, exists -import json -import shutil -from shutil import rmtree -from tempfile import mkdtemp, mkstemp - -import mx -import mx_benchmark -from mx_benchmark import ParserEntry, DataPoints -import mx_sdk_benchmark -from mx_sdk_benchmark import NativeImageBundleBasedBenchmarkMixin -import mx_sdk_vm_impl - -_suite = mx.suite('java-benchmarks') - - -from configparser import ConfigParser -from io import StringIO - - -def _configparser_read_file(configp, fp): - configp.read_file(fp) - - -# Short-hand commands used to quickly run common benchmarks. -mx.update_commands(_suite, { - 'dacapo': [ - lambda args: createBenchmarkShortcut("dacapo", args), - '[|*] [-- [VM options] [-- [DaCapo options]]]' - ], - 'scaladacapo': [ - lambda args: createBenchmarkShortcut("scala-dacapo", args), - '[|*] [-- [VM options] [-- [Scala DaCapo options]]]' - ], - 'specjvm2008': [ - lambda args: createBenchmarkShortcut("specjvm2008", args), - '[|*] [-- [VM options] [-- [SPECjvm2008 options]]]' - ], - 'specjbb2015': [ - lambda args: mx_benchmark.benchmark(["specjbb2015"] + args), - '[-- [VM options] [-- [SPECjbb2015 options]]]' - ], - 'renaissance': [ - lambda args: createBenchmarkShortcut("renaissance", args), - '[|*] [-- [VM options] [-- [Renaissance options]]]' - ], - 'shopcart': [ - lambda args: createBenchmarkShortcut("shopcart", args), - '[-- [VM options] [-- [ShopCart options]]]' - ], - 'awfy': [ - lambda args: createBenchmarkShortcut("awfy", args), - '[|*] [-- [VM options] ] [-- [AWFY options] ]]' - ], -}) - - -def createBenchmarkShortcut(benchSuite, args): - if not args: - benchname = "*" - remaining_args = [] - elif args[0] == "--": - # not a benchmark name - benchname = "*" - remaining_args = args - else: - benchname = args[0] - remaining_args = args[1:] - return mx_benchmark.benchmark([benchSuite + ":" + benchname] + remaining_args) - - -# Adds a java VM from JAVA_HOME without any assumption about it -mx_benchmark.add_java_vm(mx_benchmark.DefaultJavaVm('java-home', 'default'), _suite, 1) - - -def java_home_jdk(): - return mx.get_jdk() - - -class BaseMicroserviceBenchmarkSuite(mx_benchmark.BenchmarkSuite): - def group(self): - return "Graal" - - def subgroup(self): - return "graal-compiler" - - def validateReturnCode(self, retcode): - return retcode == 143 - - def applicationDist(self): - raise NotImplementedError() - - def applicationPath(self): - raise NotImplementedError() - - def applicationStartupRule(self, benchSuiteName, benchmark): - return [ - # Example of Micronaut startup log: - # "[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 328ms. Server Running: " - mx_benchmark.StdOutRule( - self.get_application_startup_regex(), - { - "benchmark": benchmark, - "bench-suite": benchSuiteName, - "metric.name": "app-startup", - "metric.value": ("", float), - "metric.unit": self.get_application_startup_units(), - "metric.better": "lower", - } - ) - ] - - def get_application_startup_regex(self): - raise NotImplementedError() - - def get_application_startup_units(self): - raise NotImplementedError - - def skip_agent_assertions(self, benchmark, args): - # This method overrides NativeImageMixin.skip_agent_assertions - user_args = super(BaseMicroserviceBenchmarkSuite, self).skip_agent_assertions(benchmark, args) - if user_args is not None: - return user_args - else: - return [] - - def default_stages(self): - # This method is used by NativeImageMixin.stages - raise NotImplementedError() - - -class BaseSpringBenchmarkSuite(BaseMicroserviceBenchmarkSuite, NativeImageBundleBasedBenchmarkMixin): - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - return self.create_bundle_command_line_args(benchmarks, bmSuiteArgs) - - def get_application_startup_regex(self): - # Example of SpringBoot 3 startup log: - # 2023-05-16T14:08:54.033+02:00 INFO 24381 --- [ main] o.s.s.petclinic.PetClinicApplication : Started PetClinicApplication in 3.774 seconds (process running for 4.1) - return r"Started [^ ]+ in (?P\d*[.,]?\d*) seconds \(process running for (?P\d*[.,]?\d*)\)$" - - def get_application_startup_units(self): - return 's' - - def get_image_env(self): - # Disable experimental option checking. - return {**os.environ, "NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL": "false"} - - def default_stages(self): - return ['instrument-image', 'instrument-run', 'image', 'run'] - - def uses_bundles(self): - return True - - -class BasePetClinicBenchmarkSuite(BaseSpringBenchmarkSuite): - def version(self): - return "3.0.1" - - def applicationDist(self): - return mx.library("PETCLINIC_" + self.version(), True).get_path(True) - - -class PetClinicWrkBenchmarkSuite(BasePetClinicBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - """PetClinic benchmark suite that measures throughput using Wrk.""" - - def name(self): - return "petclinic-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["mixed-tiny", "mixed-small", "mixed-medium", "mixed-large", "mixed-huge"] - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(PetClinicWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - -mx_benchmark.add_bm_suite(PetClinicWrkBenchmarkSuite()) - - -class BaseSpringHelloWorldBenchmarkSuite(BaseSpringBenchmarkSuite): - def version(self): - return "3.0.6" - - def applicationDist(self): - return mx.library("SPRING_HW_" + self.version(), True).get_path(True) - - -class SpringHelloWorldWrkBenchmarkSuite(BaseSpringHelloWorldBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - def name(self): - return "spring-helloworld-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["helloworld"] - - def serviceEndpoint(self): - return 'hello' - - def defaultWorkloadPath(self, benchmark): - return os.path.join(self.applicationDist(), "workloads", benchmark + ".wrk") - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(SpringHelloWorldWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - - def getScriptPath(self, config): - return os.path.join(self.applicationDist(), "workloads", config["script"]) - - -mx_benchmark.add_bm_suite(SpringHelloWorldWrkBenchmarkSuite()) - - -class BaseQuarkusBenchmarkSuite(BaseMicroserviceBenchmarkSuite): - def get_application_startup_regex(self): - # Example of Quarkus startup log: - # "2021-03-17 20:03:33,893 INFO [io.quarkus] (main) tika-quickstart 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.12.1.Final) started in 1.210s. Listening on: " - return r"started in (?P\d*[.,]?\d*)s." - - def get_application_startup_units(self): - return 's' - - def get_image_env(self): - # Disable experimental option checking. - return {**os.environ, "NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL": "false"} - - def default_stages(self): - return ['instrument-image', 'instrument-run', 'image', 'run'] - - -class BaseQuarkusBundleBenchmarkSuite(BaseQuarkusBenchmarkSuite, NativeImageBundleBasedBenchmarkMixin): - def uses_bundles(self): - return True - - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - return self.create_bundle_command_line_args(benchmarks, bmSuiteArgs) - - -class BaseTikaBenchmarkSuite(BaseQuarkusBundleBenchmarkSuite): - def version(self): - return "1.0.11" - - def applicationDist(self): - return mx.library("TIKA_" + self.version(), True).get_path(True) - - def applicationPath(self): - return os.path.join(self.applicationDist(), "tika-quickstart-" + self.version() + "-runner.jar") - - def serviceEndpoint(self): - return 'parse' - - def extra_image_build_argument(self, benchmark, args): - # Older JDK versions would need -H:NativeLinkerOption=libharfbuzz as an extra build argument. - expectedJdkVersion = mx.VersionSpec("11.0.13") - if mx.get_jdk().version < expectedJdkVersion: - mx.abort(benchmark + " needs at least JDK version " + str(expectedJdkVersion)) - return [ - # Workaround for wrong class initialization configuration in Quarkus Tika - '--initialize-at-build-time=org.apache.pdfbox.rendering.ImageType,org.apache.pdfbox.rendering.ImageType$1,org.apache.pdfbox.rendering.ImageType$2,org.apache.pdfbox.rendering.ImageType$3,org.apache.pdfbox.rendering.ImageType$4,org.apache.xmlbeans.XmlObject,org.apache.xmlbeans.metadata.system.sXMLCONFIG.TypeSystemHolder,org.apache.xmlbeans.metadata.system.sXMLLANG.TypeSystemHolder,org.apache.xmlbeans.metadata.system.sXMLSCHEMA.TypeSystemHolder', - ] + super(BaseTikaBenchmarkSuite, self).extra_image_build_argument(benchmark, args) - - -class TikaWrkBenchmarkSuite(BaseTikaBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - """Tika benchmark suite that measures throughput using Wrk.""" - - def name(self): - return "tika-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["odt-tiny", "odt-small", "odt-medium", "odt-large", "odt-huge", "pdf-tiny", "pdf-small", "pdf-medium", "pdf-large", "pdf-huge"] - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(TikaWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - -mx_benchmark.add_bm_suite(TikaWrkBenchmarkSuite()) - - -class BaseQuarkusHelloWorldBenchmarkSuite(BaseQuarkusBundleBenchmarkSuite): - def version(self): - return "1.0.6" - - def applicationDist(self): - return mx.library("QUARKUS_HW_" + self.version(), True).get_path(True) - - def applicationPath(self): - return os.path.join(self.applicationDist(), "quarkus-hello-world-" + self.version() + "-runner.jar") - - def serviceEndpoint(self): - return 'hello' - - -class QuarkusHelloWorldWrkBenchmarkSuite(BaseQuarkusHelloWorldBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - """Quarkus benchmark suite that measures latency using Wrk2.""" - - def name(self): - return "quarkus-helloworld-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["helloworld"] - - def defaultWorkloadPath(self, benchmark): - return os.path.join(self.applicationDist(), "workloads", benchmark + ".wrk") - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(QuarkusHelloWorldWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - - def getScriptPath(self, config): - return os.path.join(self.applicationDist(), "workloads", config["script"]) - - -mx_benchmark.add_bm_suite(QuarkusHelloWorldWrkBenchmarkSuite()) - - -class BaseMicronautBenchmarkSuite(BaseMicroserviceBenchmarkSuite): - def get_application_startup_regex(self): - # Example of Micronaut startup log (there can be some formatting in between): - # "[main] INFO io.micronaut.runtime.Micronaut - Startup completed in 328ms. Server Running: " - return r"^.*\[main\].*INFO.*io.micronaut.runtime.Micronaut.*- Startup completed in (?P\d+)ms." - - def get_application_startup_units(self): - return 'ms' - - def get_image_env(self): - # Disable experimental option checking. - return {**os.environ, "NATIVE_IMAGE_EXPERIMENTAL_OPTIONS_ARE_FATAL": "false"} - - def build_assertions(self, benchmark, is_gate): - # This method overrides NativeImageMixin.build_assertions - return [] # We are skipping build assertions due to some failed asserts while building Micronaut apps. - - def default_stages(self): - return ['instrument-image', 'instrument-run', 'image', 'run'] - - -class BaseMicronautBundleBenchmarkSuite(BaseMicronautBenchmarkSuite, NativeImageBundleBasedBenchmarkMixin): - def uses_bundles(self): - return True - - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - return self.create_bundle_command_line_args(benchmarks, bmSuiteArgs) - - -class BaseQuarkusRegistryBenchmark(BaseQuarkusBenchmarkSuite, mx_sdk_benchmark.BaseMicroserviceBenchmarkSuite): - """ - This benchmark is used to measure the precision and performance of the static analysis in Native Image, - so there is no runtime load, that's why the default stage is just image. - """ - - def version(self): - return "0.0.2" - - def name(self): - return "quarkus" - - def benchmarkList(self, bmSuiteArgs): - return ["registry"] - - def default_stages(self): - return ['image'] - - def run(self, benchmarks, bmSuiteArgs): - return self.intercept_run(super(), benchmarks, bmSuiteArgs) - - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - if benchmarks is None: - mx.abort("Suite can only run a single benchmark per VM instance.") - elif len(benchmarks) != 1: - mx.abort("Must specify exactly one benchmark.") - else: - benchmark = benchmarks[0] - return self.vmArgs(bmSuiteArgs) + ["-jar", os.path.join(self.applicationDist(), benchmark + ".jar")] - - def applicationDist(self): - return mx.library("QUARKUS_REGISTRY_" + self.version(), True).get_path(True) - - def extra_image_build_argument(self, benchmark, args): - return ['-J-Dlogging.initial-configurator.min-level=500', - '-J-Dio.quarkus.caffeine.graalvm.recordStats=true', - '-J-Djava.util.logging.manager=org.jboss.logmanager.LogManager', - '-J-Dsun.nio.ch.maxUpdateArraySize=100', - '-J-DCoordinatorEnvironmentBean.transactionStatusManagerEnable=false', - '-J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory', - '-J-Dvertx.disableDnsResolver=true', - '-J-Dio.netty.leakDetection.level=DISABLED', - '-J-Dio.netty.allocator.maxOrder=3', - '-J-Duser.language=en', - '-J-Duser.country=GB', - '-J-Dfile.encoding=UTF-8', - '--features=io.quarkus.jdbc.postgresql.runtime.graal.SQLXMLFeature,org.hibernate.graalvm.internal.GraalVMStaticFeature,io.quarkus.hibernate.validator.runtime.DisableLoggingFeature,io.quarkus.hibernate.orm.runtime.graal.DisableLoggingFeature,io.quarkus.runner.Feature,io.quarkus.runtime.graal.DisableLoggingFeature,io.quarkus.caffeine.runtime.graal.CacheConstructorsFeature', - '-J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED', - '-J--add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=ALL-UNNAMED', - '-J--add-opens=java.base/java.text=ALL-UNNAMED', - '-J--add-opens=java.base/java.io=ALL-UNNAMED', - '-J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED', - '-J--add-opens=java.base/java.util=ALL-UNNAMED', - '-H:+AllowFoldMethods', - '-J-Djava.awt.headless=true', - '--no-fallback', - '--link-at-build-time', - '-H:+ReportExceptionStackTraces', - '-H:-AddAllCharsets', - '--enable-url-protocols=http,https', - '-H:-UseServiceLoaderFeature', - '--exclude-config', - 'io\.netty\.netty-codec', - '/META-INF/native-image/io\.netty/netty-codec/generated/handlers/reflect-config\.json', - '--exclude-config', - 'io\.netty\.netty-handler', - '/META-INF/native-image/io\.netty/netty-handler/generated/handlers/reflect-config\.json', - ] + super(BaseQuarkusBenchmarkSuite,self).extra_image_build_argument(benchmark, args) - -mx_benchmark.add_bm_suite(BaseQuarkusRegistryBenchmark()) - -_mushopConfig = { - 'order': ['--initialize-at-build-time=io.netty.handler.codec.http.cookie.ServerCookieEncoder,java.sql.DriverInfo,kotlin.coroutines.intrinsics.CoroutineSingletons'], - 'user': ['--initialize-at-build-time=io.netty.handler.codec.http.cookie.ServerCookieEncoder,java.sql.DriverInfo'], - 'payment': ['--initialize-at-build-time=io.netty.handler.codec.http.cookie.ServerCookieEncoder'] -} - -class BaseMicronautMuShopBenchmark(BaseMicronautBenchmarkSuite, mx_sdk_benchmark.BaseMicroserviceBenchmarkSuite): - """ - This benchmark suite is used to measure the precision and performance of the static analysis in Native Image, - so there is no runtime load, that's why the default stage is just image. - """ - - def version(self): - return "0.0.2" - - def name(self): - return "mushop" - - def benchmarkList(self, bmSuiteArgs): - return ["user", "order", "payment"] - - def default_stages(self): - return ['image'] - - def run(self, benchmarks, bmSuiteArgs): - return self.intercept_run(super(), benchmarks, bmSuiteArgs) - - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - if benchmarks is None: - mx.abort("Suite can only run a single benchmark per VM instance.") - elif len(benchmarks) != 1: - mx.abort("Must specify exactly one benchmark.") - else: - benchmark = benchmarks[0] - return self.vmArgs(bmSuiteArgs) + ["-jar", os.path.join(self.applicationDist(), benchmark + ".jar")] - - def applicationDist(self): - return mx.library("MICRONAUT_MUSHOP_" + self.version(), True).get_path(True) - - def extra_image_build_argument(self, benchmark, args): - return ([ - '--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk=ALL-UNNAMED', - '--add-exports=org.graalvm.nativeimage.builder/com.oracle.svm.core.configure=ALL-UNNAMED', - '--add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=ALL-UNNAMED'] - + _mushopConfig[benchmark] + super(BaseMicronautBenchmarkSuite,self).extra_image_build_argument(benchmark, args)) - -mx_benchmark.add_bm_suite(BaseMicronautMuShopBenchmark()) - - -class BaseShopCartBenchmarkSuite(BaseMicronautBundleBenchmarkSuite): - def version(self): - return "0.3.10" - - def applicationDist(self): - return mx.library("SHOPCART_" + self.version(), True).get_path(True) - - def applicationPath(self): - return os.path.join(self.applicationDist(), "shopcart-" + self.version() + ".jar") - - def serviceEndpoint(self): - return 'clients' - - -class ShopCartWrkBenchmarkSuite(BaseShopCartBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - """ShopCart benchmark suite that measures throughput using Wrk.""" - - def name(self): - return "shopcart-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["mixed-tiny", "mixed-small", "mixed-medium", "mixed-large", "mixed-huge"] - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(ShopCartWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - -mx_benchmark.add_bm_suite(ShopCartWrkBenchmarkSuite()) - - -class BaseMicronautHelloWorldBenchmarkSuite(BaseMicronautBundleBenchmarkSuite): - def version(self): - return "1.0.7" - - def applicationDist(self): - return mx.library("MICRONAUT_HW_" + self.version(), True).get_path(True) - - def applicationPath(self): - return os.path.join(self.applicationDist(), "micronaut-hello-world-" + self.version() + ".jar") - - def serviceEndpoint(self): - return 'hello' - - -class MicronautHelloWorldWrkBenchmarkSuite(BaseMicronautHelloWorldBenchmarkSuite, mx_sdk_benchmark.BaseWrkBenchmarkSuite): - def name(self): - return "micronaut-helloworld-wrk" - - def benchmarkList(self, bmSuiteArgs): - return ["helloworld"] - - def defaultWorkloadPath(self, benchmark): - return os.path.join(self.applicationDist(), "workloads", benchmark + ".wrk") - - def rules(self, out, benchmarks, bmSuiteArgs): - return self.applicationStartupRule(self.benchSuiteName(), benchmarks[0]) + super(MicronautHelloWorldWrkBenchmarkSuite, self).rules(out, benchmarks, bmSuiteArgs) - - def getScriptPath(self, config): - return os.path.join(self.applicationDist(), "workloads", config["script"]) - - -mx_benchmark.add_bm_suite(MicronautHelloWorldWrkBenchmarkSuite()) - - -class BaseDaCapoBenchmarkSuite(mx_benchmark.JavaBenchmarkSuite, mx_benchmark.AveragingBenchmarkMixin, mx_benchmark.TemporaryWorkdirMixin): - """Base benchmark suite for DaCapo-based benchmarks. - - This suite can only run a single benchmark in one VM invocation. - """ - def group(self): - return "Graal" - - def subgroup(self): - return "graal-compiler" - - def name(self): - raise NotImplementedError() - - def daCapoClasspathEnvVarName(self): - raise NotImplementedError() - - def daCapoLibraryName(self): - raise NotImplementedError() - - def daCapoPath(self): - dacapo = mx.get_env(self.daCapoClasspathEnvVarName()) - if dacapo: - return dacapo - lib = mx.library(self.daCapoLibraryName(), False) - if lib: - return lib.get_path(True) - return None - - def daCapoIterations(self): - raise NotImplementedError() - - def daCapoSizes(self): - raise NotImplementedError() - - def completeBenchmarkList(self, bmSuiteArgs): - return sorted([bench for bench in self.daCapoIterations().keys() if self.workloadSize() in self.daCapoSizes().get(bench, [])]) - - def existingSizes(self): - return list(dict.fromkeys([s for bench, sizes in self.daCapoSizes().items() for s in sizes])) - - def workloadSize(self): - raise NotImplementedError() - - def validateEnvironment(self): - if not self.daCapoPath(): - raise RuntimeError( - "Neither " + self.daCapoClasspathEnvVarName() + " variable nor " + - self.daCapoLibraryName() + " library specified.") - - def validateReturnCode(self, retcode): - return retcode == 0 - - def postprocessRunArgs(self, benchname, runArgs): - parser = argparse.ArgumentParser(add_help=False) - parser.add_argument("-n", "--iterations", default=None) - parser.add_argument("-sf", default=1, type=float, help="The total number of iterations is equivalent to the value selected by the '-n' flag scaled by this factor.") - parser.add_argument("-s", "--size", default=None) - args, remaining = parser.parse_known_args(runArgs) - - if args.size: - if args.size not in self.existingSizes(): - mx.abort("Unknown workload size '{}'. " - "Existing benchmark sizes are: {}".format(args.size, ','.join(self.existingSizes()))) - - if args.size != self.workloadSize(): - mx.abort("Mismatch between suite-defined workload size ('{}') " - "and user-provided one ('{}')!".format(self.workloadSize(), args.size)) - - otherArgs = ["-s", self.workloadSize()] + remaining - - if args.iterations: - if args.iterations.isdigit(): - return ["-n", str(int(args.sf * int(args.iterations)))] + otherArgs - if args.iterations == "-1": - return None - else: - iterations = self.daCapoIterations()[benchname] - if iterations == -1: - return None - else: - iterations = iterations + int(self.getExtraIterationCount(iterations) * args.sf) - return ["-n", str(iterations)] + otherArgs - - def createCommandLineArgs(self, benchmarks, bmSuiteArgs): - if benchmarks is None: - raise RuntimeError( - "Suite runs only a single benchmark.") - if len(benchmarks) != 1: - raise RuntimeError( - "Suite runs only a single benchmark, got: {0}".format(benchmarks)) - runArgs = self.postprocessRunArgs(benchmarks[0], self.runArgs(bmSuiteArgs)) - if runArgs is None: - return None - return ( - self.vmArgs(bmSuiteArgs) + ["-jar"] + [self.daCapoPath()] + - [benchmarks[0]] + runArgs) - - def benchmarkList(self, bmSuiteArgs): - missing_sizes = set(self.daCapoIterations().keys()).difference(set(self.daCapoSizes().keys())) - if len(missing_sizes) > 0: - mx.abort("Missing size definitions for benchmark(s): {}".format(missing_sizes)) - return [b for b, it in self.daCapoIterations().items() - if self.workloadSize() in self.daCapoSizes().get(b, []) and it != -1] - - def successPatterns(self): - return [ - # Due to the non-determinism of DaCapo version printing, we only match the name. - re.compile( - r"^===== DaCapo (?P[^\n]+) ([a-zA-Z0-9_]+) PASSED in ([0-9]+) msec =====", # pylint: disable=line-too-long - re.MULTILINE) - ] - - def failurePatterns(self): - return [ - # Due to the non-determinism of DaCapo version printing, we only match the name. - re.compile( - r"^===== DaCapo (?P[^\n]+) ([a-zA-Z0-9_]+) FAILED (warmup|) =====", # pylint: disable=line-too-long - re.MULTILINE), - re.compile( - r"^\[\[\[Graal compilation failure\]\]\]", # pylint: disable=line-too-long - re.MULTILINE) - ] - - def shorten_vm_flags(self, args): - return mx_benchmark.Rule.crop_back("...")(' '.join(args)) - - def rules(self, out, benchmarks, bmSuiteArgs): - runArgs = self.postprocessRunArgs(benchmarks[0], self.runArgs(bmSuiteArgs)) - if runArgs is None: - return [] - totalIterations = int(runArgs[runArgs.index("-n") + 1]) - return [ - # Due to the non-determinism of DaCapo version printing, we only match the name. - mx_benchmark.StdOutRule( - r"===== DaCapo (?P[^\n]+) (?P[a-zA-Z0-9_]+) PASSED in (?P