From 8c87d188d2d45db0447b302c2a4b3e449aedc006 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Fri, 7 Nov 2025 15:34:35 +0100 Subject: [PATCH 1/3] Crema: fix invokedynamic patching the wrong cpi --- .../interpreter/metadata/BytecodeStream.java | 31 +++++-------------- .../interpreter/BuildTimeConstantPool.java | 2 +- .../oracle/svm/interpreter/Interpreter.java | 5 +-- .../svm/interpreter/classfile/ClassFile.java | 2 +- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/BytecodeStream.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/BytecodeStream.java index fea18e449505..3c94065cf4fc 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/BytecodeStream.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/BytecodeStream.java @@ -260,16 +260,6 @@ public static char readCPI2(byte[] code, int curBCI) { return (char) ByteUtils.beU2(code, curBCI + 1); } - /** - * Reads a 2-byte constant pool index for the current instruction, reading each byte with - * volatile semantics. Note that this does not read the 2 bytes atomically. - * - * @return the constant pool index - */ - public static char readCPI2Volatile(byte[] code, int curBCI) { - return (char) (((ByteUtils.volatileBeU1(code, curBCI + 1) & 0xff) << 8) | (ByteUtils.volatileBeU1(code, curBCI + 1 + 1) & 0xff)); - } - /** * Reads a constant pool index for the current instruction. * @@ -371,25 +361,20 @@ private static String unknownVariableLengthBytecodeMessage(int opcode) { return "unknown variable-length bytecode: " + opcode; } - @Platforms(Platform.HOSTED_ONLY.class) - public static void patchAppendixCPI(byte[] code, int curBCI, int appendixCPI) { - int opcode = opcode(code, curBCI); - switch (opcode) { - case INVOKEDYNAMIC: - code[curBCI + 3] = (byte) ((appendixCPI >> 8) & 0xFF); - code[curBCI + 4] = (byte) (appendixCPI & 0xFF); - break; - default: - throw VMError.shouldNotReachHereAtRuntime(); - } + /** + * Reads the 2-byte extra CPI for the current invokedynamic instruction, reading each byte with + * volatile semantics. Note that this does not read the 2 bytes atomically. + */ + public static char readIndyExtraCPIVolatile(byte[] code, int curBCI) { + return (char) ((ByteUtils.volatileBeU1(code, curBCI + 3) << 8) | ByteUtils.volatileBeU1(code, curBCI + 4)); } public static void patchIndyExtraCPI(byte[] code, int curBCI, int extraCPI) { int opcode = opcode(code, curBCI); switch (opcode) { case INVOKEDYNAMIC: - code[curBCI + 1] = (byte) ((extraCPI >> 8) & 0xFF); - code[curBCI + 2] = (byte) (extraCPI & 0xFF); + code[curBCI + 3] = (byte) ((extraCPI >> 8) & 0xFF); + code[curBCI + 4] = (byte) (extraCPI & 0xFF); break; default: throw VMError.shouldNotReachHereAtRuntime(); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java index b4363a2bba98..649bc1c46886 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java @@ -448,7 +448,7 @@ public void hydrate(InterpreterResolvedObjectType type) { // in the CP. newAppendixCPI = appendixConstant(JavaConstant.NULL_POINTER); } - BytecodeStream.patchAppendixCPI(code, bci, newAppendixCPI); + BytecodeStream.patchIndyExtraCPI(code, bci, newAppendixCPI); } BytecodeStream.patchCPI(code, bci, newCPI); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index deb8f2a8eeea..8b2e9bedfe0c 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -1401,7 +1401,8 @@ private static int invoke(InterpreterFrame callerFrame, InterpreterResolvedJavaM throw SemanticJavaException.raise(e); } BytecodeStream.patchIndyExtraCPI(code, curBCI, extraCPI); - assert BytecodeStream.readCPI2Volatile(code, curBCI) == extraCPI; + assert BytecodeStream.readIndyExtraCPIVolatile(code, curBCI) == extraCPI; + assert BytecodeStream.readCPI2(code, curBCI) == indyCPI; } CallSiteLink link = invokeDynamicConstant.getCallSiteLink(extraCPI); while (!link.matchesCallSite(method, curBCI)) { @@ -1411,7 +1412,7 @@ private static int invoke(InterpreterFrame callerFrame, InterpreterResolvedJavaM * still safe to use in `getCallSiteLink`. `matchesCallSite` ensures we have the * full extraCPI. */ - extraCPI = BytecodeStream.readCPI2Volatile(code, curBCI); + extraCPI = BytecodeStream.readIndyExtraCPIVolatile(code, curBCI); link = invokeDynamicConstant.getCallSiteLink(extraCPI); } if (link instanceof SuccessfulCallSiteLink successfulCallSiteLink) { diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/classfile/ClassFile.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/classfile/ClassFile.java index dedc5fd5f600..f4b4b482bb0a 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/classfile/ClassFile.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/classfile/ClassFile.java @@ -1000,7 +1000,7 @@ private byte[] recomputeConstantPoolIndices(ResolvedJavaMethod method) { // The VM already provides the resolved bootstrap method and appendix. // Investigate how to persist the appendix (arbitrary object) on the constant pool. BytecodeStream.patchCPI(code, bci, 0); - BytecodeStream.patchAppendixCPI(code, bci, 0); + BytecodeStream.patchIndyExtraCPI(code, bci, 0); break; } // @formatter:on From f044619531114d5b4e749fce0e04fb10f6fa43eb Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 6 Nov 2025 18:13:59 +0100 Subject: [PATCH 2/3] Crema: un/rebasic for compiled `linkTo` calls Signature polymorphic susbsitutions use stack kinds. --- .../svm/interpreter/CremaSupportImpl.java | 30 ++++++++++++++----- .../oracle/svm/interpreter/Interpreter.java | 9 ++++-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java index 3f8a3bf465c6..78dad696f6d9 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java @@ -42,6 +42,7 @@ import static com.oracle.svm.espresso.classfile.Constants.ACC_SUPER; import static com.oracle.svm.espresso.classfile.Constants.JVM_ACC_WRITTEN_FLAGS; import static com.oracle.svm.espresso.shared.meta.SignaturePolymorphicIntrinsic.InvokeGeneric; +import static com.oracle.svm.interpreter.Interpreter.unbasic; import static com.oracle.svm.interpreter.InterpreterStubSection.getCremaStubForVTableIndex; import java.lang.invoke.MethodType; @@ -122,6 +123,7 @@ import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaMethod; import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaType; import com.oracle.svm.interpreter.metadata.InterpreterResolvedObjectType; +import com.oracle.svm.interpreter.metadata.InterpreterUnresolvedSignature; import jdk.graal.compiler.word.Word; import jdk.vm.ci.meta.ResolvedJavaField; @@ -1312,12 +1314,15 @@ public Object invokeBasic(Target_java_lang_invoke_MemberName memberName, Object @Override public Object linkToVirtual(Object[] args) { // This is AOT-compiled code calling MethodHandle.linkToVirtual + // See also PolymorphicSignatureWrapperMethod.buildGraph Target_java_lang_invoke_MemberName mnTarget = (Target_java_lang_invoke_MemberName) args[args.length - 1]; InterpreterResolvedJavaMethod target = InterpreterResolvedJavaMethod.fromMemberName(mnTarget); - Object[] basicArgs = Arrays.copyOf(args, args.length - 1); + InterpreterUnresolvedSignature signature = target.getSignature(); + Object[] basicArgs = unbasic(args, signature, true); logIntrinsic("[from compiled] linkToVirtual ", target, basicArgs); try { - return InterpreterToVM.dispatchInvocation(target, basicArgs, true, false, false, false, true); + Object result = InterpreterToVM.dispatchInvocation(target, basicArgs, true, false, false, false, true); + return Interpreter.rebasic(result, signature.getReturnKind()); } catch (SemanticJavaException e) { throw uncheckedThrow(e.getCause()); } @@ -1326,12 +1331,15 @@ public Object linkToVirtual(Object[] args) { @Override public Object linkToStatic(Object[] args) { // This is AOT-compiled code calling MethodHandle.linkToStatic + // See also PolymorphicSignatureWrapperMethod.buildGraph Target_java_lang_invoke_MemberName mnTarget = (Target_java_lang_invoke_MemberName) args[args.length - 1]; InterpreterResolvedJavaMethod target = InterpreterResolvedJavaMethod.fromMemberName(mnTarget); - Object[] basicArgs = Arrays.copyOf(args, args.length - 1); + InterpreterUnresolvedSignature signature = target.getSignature(); + Object[] basicArgs = unbasic(args, signature, false); logIntrinsic("[from compiled] linkToStatic ", target, basicArgs); try { - return InterpreterToVM.dispatchInvocation(target, basicArgs, false, false, false, false, true); + Object result = InterpreterToVM.dispatchInvocation(target, basicArgs, false, false, false, false, true); + return Interpreter.rebasic(result, signature.getReturnKind()); } catch (SemanticJavaException e) { throw uncheckedThrow(e.getCause()); } @@ -1340,12 +1348,15 @@ public Object linkToStatic(Object[] args) { @Override public Object linkToSpecial(Object[] args) { // This is AOT-compiled code calling MethodHandle.linkToSpecial + // See also PolymorphicSignatureWrapperMethod.buildGraph Target_java_lang_invoke_MemberName mnTarget = (Target_java_lang_invoke_MemberName) args[args.length - 1]; InterpreterResolvedJavaMethod target = InterpreterResolvedJavaMethod.fromMemberName(mnTarget); - Object[] basicArgs = Arrays.copyOf(args, args.length - 1); + InterpreterUnresolvedSignature signature = target.getSignature(); + Object[] basicArgs = unbasic(args, signature, true); logIntrinsic("[from compiled] linkToSpecial ", target, basicArgs); try { - return InterpreterToVM.dispatchInvocation(target, basicArgs, false, false, false, false, true); + Object result = InterpreterToVM.dispatchInvocation(target, basicArgs, false, false, false, false, true); + return Interpreter.rebasic(result, signature.getReturnKind()); } catch (SemanticJavaException e) { throw uncheckedThrow(e.getCause()); } @@ -1354,12 +1365,15 @@ public Object linkToSpecial(Object[] args) { @Override public Object linkToInterface(Object[] args) { // This is AOT-compiled code calling MethodHandle.linkToInterface + // See also PolymorphicSignatureWrapperMethod.buildGraph Target_java_lang_invoke_MemberName mnTarget = (Target_java_lang_invoke_MemberName) args[args.length - 1]; InterpreterResolvedJavaMethod target = InterpreterResolvedJavaMethod.fromMemberName(mnTarget); - Object[] basicArgs = Arrays.copyOf(args, args.length - 1); + InterpreterUnresolvedSignature signature = target.getSignature(); + Object[] basicArgs = unbasic(args, signature, true); logIntrinsic("[from compiled] linkToInterface ", target, basicArgs); try { - return InterpreterToVM.dispatchInvocation(target, basicArgs, true, false, false, true, true); + Object result = InterpreterToVM.dispatchInvocation(target, basicArgs, true, false, false, true, true); + return Interpreter.rebasic(result, signature.getReturnKind()); } catch (SemanticJavaException e) { throw uncheckedThrow(e.getCause()); } diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index 8b2e9bedfe0c..432b019dc76c 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -573,7 +573,10 @@ private static InterpreterResolvedJavaMethod getLinkToTarget(InterpreterFrame fr } private static Object[] unbasic(InterpreterFrame frame, InterpreterUnresolvedSignature targetSig, boolean inclReceiver) { - Object[] arguments = frame.getArguments(); + return unbasic(frame.getArguments(), targetSig, inclReceiver); + } + + static Object[] unbasic(Object[] arguments, InterpreterUnresolvedSignature targetSig, boolean inclReceiver) { int parameterCount = targetSig.getParameterCount(inclReceiver); Object[] res = new Object[parameterCount]; int start = 0; @@ -588,7 +591,7 @@ private static Object[] unbasic(InterpreterFrame frame, InterpreterUnresolvedSig } // Transforms ints to sub-words - public static Object unbasic(Object arg, JavaKind kind) { + private static Object unbasic(Object arg, JavaKind kind) { return switch (kind) { case Boolean -> (int) arg != 0; case Byte -> (byte) (int) arg; @@ -598,7 +601,7 @@ public static Object unbasic(Object arg, JavaKind kind) { }; } - private static Object rebasic(Object value, JavaKind returnType) { + static Object rebasic(Object value, JavaKind returnType) { // @formatter:off return switch (returnType) { case Boolean -> stackIntToBoolean((int) value); From d7eda898e8b85cc869cb4689c35c20e1e6670aa9 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Fri, 7 Nov 2025 15:39:00 +0100 Subject: [PATCH 3/3] Crema: fix `linkToStatic` intrinsic --- .../src/com/oracle/svm/interpreter/Interpreter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index 432b019dc76c..1cd77fe5a19f 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -548,7 +548,8 @@ public static Object execute(InterpreterFrame frame, InterpreterResolvedJavaMeth case LinkToStatic, LinkToSpecial, LinkToVirtual, LinkToInterface -> { InterpreterResolvedJavaMethod resolutionSeed = getLinkToTarget(frame); InterpreterUnresolvedSignature signature = resolutionSeed.getSignature(); - Object[] basicArgs = unbasic(frame, signature, false); + boolean hasReceiver = intrinsic != SignaturePolymorphicIntrinsic.LinkToStatic; + Object[] basicArgs = unbasic(frame, signature, hasReceiver); // This should integrate with the debugger GR-70801 boolean preferStayInInterpreter = forceStayInInterpreter; traceLinkTo(resolutionSeed, intrinsic, indent);