diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 046211164acc..3d04c62ab2c9 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -197,7 +197,7 @@ def test(): fr"#4{spaces_pattern}{address_pattern} in com\.oracle\.svm\.core\.JavaMainWrapper::runCore{no_param_types_pattern} {no_arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", fr"#5{spaces_pattern}com\.oracle\.svm\.core\.JavaMainWrapper::doRun{param_types_pattern} {arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", fr"#6{spaces_pattern}({address_pattern} in )?com\.oracle\.svm\.core\.JavaMainWrapper::run{param_types_pattern} {arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", - fr"#7{spaces_pattern}com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_{varname_pattern}{param_types_pattern} {arg_values_pattern}" + fr"#7{spaces_pattern}({address_pattern} in )?com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_{varname_pattern}{param_types_pattern} {arg_values_pattern}" ] if musl: # musl has a different entry point - drop the last two frames @@ -408,7 +408,7 @@ def test(): fr"#5{spaces_pattern}{address_pattern} in com\.oracle\.svm\.core\.JavaMainWrapper::runCore{no_param_types_pattern} {no_arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", fr"#6{spaces_pattern}com\.oracle\.svm\.core\.JavaMainWrapper::doRun{param_types_pattern} {arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", fr"#7{spaces_pattern}({address_pattern} in )?com\.oracle\.svm\.core\.JavaMainWrapper::run{param_types_pattern} {arg_values_pattern} at {package_pattern}JavaMainWrapper\.java:[0-9]+", - fr"#8{spaces_pattern}com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_{varname_pattern}{param_types_pattern} {arg_values_pattern}" + fr"#8{spaces_pattern}({address_pattern} in )?com\.oracle\.svm\.core\.code\.IsolateEnterStub::JavaMainWrapper_run_{varname_pattern}{param_types_pattern} {arg_values_pattern}" ] if musl: # musl has a different entry point - drop the last two frames diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java index 92ac68ba0252..8d4148793aac 100644 --- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java +++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/UpcallStub.java @@ -218,9 +218,15 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos arguments.addFirst(returnBuffer); } - /* Transfers to the Java-side stub; note that exceptions should be handled there. */ + /* + * Transfers to the Java-side stub; note that exceptions should be handled there. We + * explicitly disable inline for this call to prevent that operations floating to a point + * where the base registers are not initialized yet. + */ arguments.addFirst(mh); InvokeWithExceptionNode returnValue = kit.createJavaCallWithException(CallTargetNode.InvokeKind.Static, highLevelStub, arguments.toArray(ValueNode.EMPTY_ARRAY)); + returnValue.setUseForInlining(false); + kit.exceptionPart(); kit.append(new DeadEndNode()); kit.endInvokeWithException(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java index ad4dfde9e0bd..0753d08e470e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/cenum/CEnumCallWrapperMethod.java @@ -101,7 +101,7 @@ private ValueNode createInvoke(AnalysisMethod method, HostedGraphKit kit, Analys if (method.getAnnotation(CEnumLookup.class) != null) { /* Call a method that converts the primitive value to a Java enum. */ EnumInfo enumInfo = (EnumInfo) nativeLibraries.findElementInfo(returnType); - return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, returnType, enumInfo, arg); + return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, returnType, enumInfo, arg, true); } else if (method.getAnnotation(CEnumValue.class) != null) { /* Call a method that converts a Java enum to a primitive value. */ ResolvedJavaType declaringType = method.getDeclaringClass(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java index 2f2aaa69f322..81e71ea698d4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CCallStubMethod.java @@ -143,7 +143,7 @@ private ValueNode adaptReturnValue(AnalysisMethod method, NativeLibraries native "Enum class %s needs a method that is annotated with @%s because it is used as the return type of a method annotated with @%s: %s.", declaredReturnType, CEnumLookup.class.getSimpleName(), getCorrespondingAnnotationName(), getOriginal()); - return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, declaredReturnType, enumInfo, value); + return CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, declaredReturnType, enumInfo, value, true); } private EnumInfo getEnumInfo(NativeLibraries nativeLibraries, AnalysisType type, boolean isReturnType) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java index 0cd79b174e31..93f69f710e67 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CEntryPointCallStubMethod.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.code; +import static com.oracle.svm.core.Uninterruptible.Utils.isUninterruptible; + import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Iterator; @@ -92,6 +94,40 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; +/** + * Builds a graph for each {@link CEntryPoint} method. Each graph looks roughly like the pseudocode + * below. Note that only the prologue may be inlined, while all other calls must never be inlined. + * This restriction is necessary because the prologue typically initializes well-known registers + * such as the heap base, so we need to make sure that nothing (not even Java heap constants) can + * float above the (potentially inlined) prologue logic. + *

+ * Besides that, we also need to prevent inlining to ensure that exception handling works correctly + * (see GR-24649). + * + * {@snippet : + * CEntryPointCallStub(args) { + * prologue(); + * if (errorInPrologue) { + * return prologueBailout(); + * } + * + * adaptedArgs = adaptArgumentValues(args); + * ensureClassInitialization(); + * try { + * result = targetMethod(adaptedArgs); + * } catch (Throwable e) { + * result = exceptionHandler(e); + * result = adaptReturnValue(result); + * epilogue(); + * return result; + * } + * + * result = adaptReturnValue(result); + * epilogue(); + * return result; + * } + * } + */ public final class CEntryPointCallStubMethod extends EntryPointCallStubMethod { static CEntryPointCallStubMethod create(BigBang bb, AnalysisMethod targetMethod, CEntryPointData entryPointData) { MetaAccessProvider originalMetaAccess = GraalAccess.getOriginalProviders().getMetaAccess(); @@ -170,36 +206,47 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos ImageSingletons.lookup(CInterfaceWrapper.class).tagCEntryPointPrologue(kit, method); } + /* Invoke the prologue. This call is the only one that may be inlined. */ InvokeWithExceptionNode invokePrologue = generatePrologue(kit, parameterLoadTypes, targetMethod.getParameterAnnotations(), args); if (invokePrologue != null) { ResolvedJavaMethod prologueMethod = invokePrologue.callTarget().targetMethod(); JavaKind prologueReturnKind = prologueMethod.getSignature().getReturnKind(); if (prologueReturnKind == JavaKind.Int) { kit.startIf(kit.unique(new IntegerEqualsNode(invokePrologue, ConstantNode.forInt(0, kit.getGraph()))), BranchProbabilityNode.VERY_FAST_PATH_PROFILE); + + /* Prologue returned zero, so continue execution normally. */ kit.thenPart(); + + /* Prologue returned non-zero, start the error handling. */ kit.elsePart(); Class bailoutCustomizer = entryPointData.getPrologueBailout(); JavaKind targetMethodReturnKind = targetMethod.getSignature().getReturnKind(); + + /* Generate the default bailout logic if no custom bailout logic was registered. */ boolean createdReturnNode = false; if (bailoutCustomizer == CEntryPointOptions.AutomaticPrologueBailout.class) { if (targetMethodReturnKind == JavaKind.Int) { + /* Directly return the error code that was returned from the prologue. */ kit.createReturn(invokePrologue, JavaKind.Int); createdReturnNode = true; } else if (targetMethodReturnKind == JavaKind.Void) { + /* Swallow the error code from the prologue. */ kit.createReturn(null, JavaKind.Void); createdReturnNode = true; } else { - VMError.shouldNotReachHere("@CEntryPointOptions on " + targetMethod + " must specify a custom prologue bailout as the method's return type is neither int nor void."); + throw VMError.shouldNotReachHere("@CEntryPointOptions on " + targetMethod + " must specify a custom prologue bailout as the method's return type is neither int nor void."); } } + /* Invoke the custom bailout logic if necessary. */ if (!createdReturnNode) { AnalysisMethod[] bailoutMethods = kit.getMetaAccess().lookupJavaType(bailoutCustomizer).getDeclaredMethods(false); UserError.guarantee(bailoutMethods.length == 1 && bailoutMethods[0].isStatic(), "Prologue bailout customization class must declare exactly one static method: %s -> %s", targetMethod, bailoutCustomizer); - InvokeWithExceptionNode invokeBailoutCustomizer = generatePrologueOrEpilogueInvoke(kit, bailoutMethods[0], invokePrologue); + InvokeWithExceptionNode invokeBailoutCustomizer = createInvokeStaticWithFatalExceptionHandler(kit, bailoutMethods[0], invokePrologue); + invokeBailoutCustomizer.setUseForInlining(false); VMError.guarantee(bailoutMethods[0].getSignature().getReturnKind() == method.getSignature().getReturnKind(), "Return type mismatch: %s is incompatible with %s", bailoutMethods[0], targetMethod); kit.createReturn(invokeBailoutCustomizer, targetMethod.getSignature().getReturnKind()); @@ -211,13 +258,18 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos } } + /* Convert primitive argument values to Java enums if needed. */ adaptArgumentValues(kit, parameterTypes, parameterEnumInfos, args); + /* Ensure that the target class is initialized. */ AnalysisMethod aTargetMethod = kit.getMetaAccess().getUniverse().lookup(targetMethod); kit.emitEnsureInitializedCall(aTargetMethod.getDeclaringClass()); + /* + * Invoke the target method that is annotated with @CEntryPoint. Also support non-static + * test methods by passing null as the receiver (they are not allowed to use the receiver). + */ int invokeBci = kit.bci(); - // Also support non-static test methods (they are not allowed to use the receiver) InvokeKind invokeKind = aTargetMethod.isStatic() ? InvokeKind.Static : InvokeKind.Special; ValueNode[] invokeArgs = args; if (invokeKind != InvokeKind.Static) { @@ -225,13 +277,17 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos invokeArgs[0] = kit.createObject(null); System.arraycopy(args, 0, invokeArgs, 1, args.length); } + InvokeWithExceptionNode invoke = kit.startInvokeWithException(aTargetMethod, invokeKind, kit.getFrameState(), invokeBci, invokeArgs); + invoke.setUseForInlining(false); patchNodeSourcePosition(invoke); + + /* Invoke the exception handler if an exception occurred in the target method. */ kit.exceptionPart(); - ExceptionObjectNode exception = kit.exceptionObject(); - generateExceptionHandler(method, kit, exception, invoke.getStackKind()); + generateExceptionHandler(method, kit, kit.exceptionObject(), invoke.getStackKind()); kit.endInvokeWithException(); + /* Invoke the epilogue and return the value that was returned from the target method. */ generateEpilogueAndReturn(method, kit, invoke); return kit.finalizeGraph(); } @@ -271,7 +327,8 @@ private StructuredGraph buildBuiltinGraph(DebugContext debug, AnalysisMethod met ExecutionContextParameters executionContext = findExecutionContextParameters(kit, aTargetMethod.toParameterList(), aTargetMethod.getParameterAnnotations()); - final CEntryPoint.Builtin builtin = entryPointData.getBuiltin(); + /* Find the target method for the built-in. */ + CEntryPoint.Builtin builtin = entryPointData.getBuiltin(); AnalysisMethod builtinCallee = null; for (AnalysisMethod candidate : kit.getMetaAccess().lookupJavaType(CEntryPointBuiltins.class).getDeclaredMethods(false)) { CEntryPointBuiltinImplementation annotation = candidate.getAnnotation(CEntryPointBuiltinImplementation.class); @@ -282,90 +339,91 @@ private StructuredGraph buildBuiltinGraph(DebugContext debug, AnalysisMethod met } VMError.guarantee(builtinCallee != null, "No candidate for @%s built-in %s", CEntryPoint.class.getSimpleName(), builtin); + /* Determine which Isolate/IsolateThread should be used as the context. */ AnalysisType isolateType = kit.getMetaAccess().lookupJavaType(Isolate.class); AnalysisType threadType = kit.getMetaAccess().lookupJavaType(IsolateThread.class); - int builtinIsolateIndex = -1; - int builtinThreadIndex = -1; + int isolateIndex = -1; + int threadIndex = -1; List builtinParamTypes = builtinCallee.toParameterList(); for (int i = 0; i < builtinParamTypes.size(); i++) { AnalysisType type = builtinParamTypes.get(i); if (isolateType.isAssignableFrom(type)) { - VMError.guarantee(builtinIsolateIndex == -1, "@%s built-in with more than one %s parameter: %s", + VMError.guarantee(isolateIndex == -1, "@%s built-in with more than one %s parameter: %s", CEntryPoint.class.getSimpleName(), Isolate.class.getSimpleName(), builtinCallee); - builtinIsolateIndex = i; + isolateIndex = i; } else if (threadType.isAssignableFrom(type)) { - VMError.guarantee(builtinThreadIndex == -1, "@%s built-in with more than one %s parameter: %s", + VMError.guarantee(threadIndex == -1, "@%s built-in with more than one %s parameter: %s", CEntryPoint.class.getSimpleName(), IsolateThread.class.getSimpleName(), builtinCallee); - builtinThreadIndex = i; + threadIndex = i; } else { - VMError.shouldNotReachHere("@%s built-in currently may have only %s or %s parameters: %s", + throw VMError.shouldNotReachHere("@%s built-in currently may have only %s or %s parameters: %s", CEntryPoint.class.getSimpleName(), Isolate.class.getSimpleName(), IsolateThread.class.getSimpleName(), builtinCallee); } } ValueNode[] args = kit.getInitialArguments().toArray(ValueNode.EMPTY_ARRAY); - ValueNode[] builtinArgs = new ValueNode[builtinParamTypes.size()]; - if (builtinIsolateIndex != -1) { + if (isolateIndex != -1) { VMError.guarantee(executionContext.designatedIsolateIndex != -1 || executionContext.isolateCount == 1, "@%s built-in %s needs exactly one %s parameter: %s", CEntryPoint.class.getSimpleName(), entryPointData.getBuiltin(), Isolate.class.getSimpleName(), builtinCallee); int index = (executionContext.designatedIsolateIndex != -1) ? executionContext.designatedIsolateIndex : executionContext.lastIsolateIndex; - builtinArgs[builtinIsolateIndex] = args[index]; + builtinArgs[isolateIndex] = args[index]; } - if (builtinThreadIndex != -1) { + if (threadIndex != -1) { VMError.guarantee(executionContext.designatedThreadIndex != -1 || executionContext.threadCount == 1, "@%s built-in %s needs exactly one %s parameter: %s", CEntryPoint.class.getSimpleName(), entryPointData.getBuiltin(), IsolateThread.class.getSimpleName(), builtinCallee); int index = (executionContext.designatedThreadIndex != -1) ? executionContext.designatedThreadIndex : executionContext.lastThreadIndex; - builtinArgs[builtinThreadIndex] = args[index]; + builtinArgs[threadIndex] = args[index]; } - int invokeBci = kit.bci(); - InvokeWithExceptionNode invoke = kit.startInvokeWithException(builtinCallee, InvokeKind.Static, kit.getFrameState(), invokeBci, builtinArgs); - kit.exceptionPart(); - ExceptionObjectNode exception = kit.exceptionObject(); + /* Invoke the target method. */ + InvokeWithExceptionNode invoke = kit.startInvokeWithException(builtinCallee, InvokeKind.Static, kit.getFrameState(), kit.bci(), builtinArgs); - generateExceptionHandler(method, kit, exception, invoke.getStackKind()); + /* Invoke the exception handler if an exception occurred in the target method. */ + kit.exceptionPart(); + generateExceptionHandler(method, kit, kit.exceptionObject(), invoke.getStackKind()); kit.endInvokeWithException(); if (ImageSingletons.contains(CInterfaceWrapper.class)) { ImageSingletons.lookup(CInterfaceWrapper.class).tagCEntryPointEpilogue(kit, method); } + /* Return the result. */ kit.createReturn(invoke, aTargetMethod.getSignature().getReturnKind()); - return kit.finalizeGraph(); } /** * The signature may contain Java object types. The only Java object types that we support at - * the moment are Java enums (annotated with @CEnum). This method replaces all Java enums with - * suitable primitive types. + * the moment are Java enums (annotated with {@link CEnum}). This method replaces all Java enums + * with suitable primitive types. */ private EnumInfo[] adaptParameterTypes(NativeLibraries nativeLibraries, HostedGraphKit kit, List parameterTypes, List parameterLoadTypes) { EnumInfo[] parameterEnumInfos = null; for (int i = 0; i < parameterTypes.size(); i++) { - if (!CInterfaceEnumTool.isPrimitiveOrWord(parameterTypes.get(i))) { - EnumInfo enumInfo = getEnumInfo(nativeLibraries, targetMethod, parameterTypes.get(i), false); - UserError.guarantee(enumInfo.hasCEnumLookupMethods(), - "Enum class %s needs a method that is annotated with @%s because it is used as a parameter of an entry point method: %s", - parameterTypes.get(i), CEnumLookup.class.getSimpleName(), targetMethod); - - if (parameterEnumInfos == null) { - parameterEnumInfos = new EnumInfo[parameterTypes.size()]; - } - parameterEnumInfos[i] = enumInfo; - - /* The argument is a @CEnum, so change the type to a primitive type. */ - AnalysisType paramType = CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess()); - parameterLoadTypes.set(i, paramType); - - final int parameterIndex = i; - FrameState initialState = kit.getGraph().start().stateAfter(); - Iterator matchingNodes = initialState.values().filter(node -> ((ParameterNode) node).index() == parameterIndex).iterator(); - ValueNode parameterNode = matchingNodes.next(); - assert !matchingNodes.hasNext() && parameterNode.usages().filter(n -> n != initialState).isEmpty(); - parameterNode.setStamp(StampFactory.forKind(paramType.getJavaKind())); + if (CInterfaceEnumTool.isPrimitiveOrWord(parameterTypes.get(i))) { + continue; + } + + EnumInfo enumInfo = getEnumInfo(nativeLibraries, targetMethod, parameterTypes.get(i), false); + UserError.guarantee(enumInfo.hasCEnumLookupMethods(), "Enum class %s needs a method that is annotated with @%s because it is used as a parameter of an entry point method: %s", + parameterTypes.get(i), CEnumLookup.class.getSimpleName(), targetMethod); + + if (parameterEnumInfos == null) { + parameterEnumInfos = new EnumInfo[parameterTypes.size()]; } + parameterEnumInfos[i] = enumInfo; + + /* The argument is a @CEnum, so change the type to a primitive type. */ + AnalysisType paramType = CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess()); + parameterLoadTypes.set(i, paramType); + + final int parameterIndex = i; + FrameState initialState = kit.getGraph().start().stateAfter(); + Iterator matchingNodes = initialState.values().filter(node -> ((ParameterNode) node).index() == parameterIndex).iterator(); + ValueNode parameterNode = matchingNodes.next(); + assert !matchingNodes.hasNext() && parameterNode.usages().filter(n -> n != initialState).isEmpty(); + parameterNode.setStamp(StampFactory.forKind(paramType.getJavaKind())); } return parameterEnumInfos; } @@ -384,13 +442,16 @@ private static EnumInfo getEnumInfo(NativeLibraries nativeLibraries, ResolvedJav CEnum.class.getSimpleName(), method, type); } + /** Converts primitive argument values to Java enums if necessary, see {@link CEnum}. */ private static void adaptArgumentValues(HostedGraphKit kit, List parameterTypes, EnumInfo[] parameterEnumInfos, ValueNode[] args) { - if (parameterEnumInfos != null) { - // These methods must be called after the prologue established a safe context - for (int i = 0; i < parameterEnumInfos.length; i++) { - if (parameterEnumInfos[i] != null) { - args[i] = CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, parameterTypes.get(i), parameterEnumInfos[i], args[i]); - } + if (parameterEnumInfos == null) { + /* No conversion necessary. */ + return; + } + + for (int i = 0; i < parameterEnumInfos.length; i++) { + if (parameterEnumInfos[i] != null) { + args[i] = CInterfaceEnumTool.singleton().createInvokeLookupEnum(kit, parameterTypes.get(i), parameterEnumInfos[i], args[i], false); } } } @@ -398,28 +459,21 @@ private static void adaptArgumentValues(HostedGraphKit kit, List p private InvokeWithExceptionNode generatePrologue(HostedGraphKit kit, List parameterTypes, Annotation[][] parameterAnnotations, ValueNode[] args) { Class prologueClass = entryPointData.getPrologue(); if (prologueClass == NoPrologue.class) { - UserError.guarantee(Uninterruptible.Utils.isUninterruptible(targetMethod), - "%s.%s is allowed only for methods annotated with @%s: %s", - CEntryPointOptions.class.getSimpleName(), - NoPrologue.class.getSimpleName(), - Uninterruptible.class.getSimpleName(), - targetMethod); + UserError.guarantee(isUninterruptible(targetMethod), "%s.%s is allowed only for methods annotated with @%s: %s", + CEntryPointOptions.class.getSimpleName(), NoPrologue.class.getSimpleName(), Uninterruptible.class.getSimpleName(), targetMethod); return null; } + + /* Generate a call to a custom prologue method, if one is registered. */ if (prologueClass != CEntryPointOptions.AutomaticPrologue.class) { AnalysisType prologue = kit.getMetaAccess().lookupJavaType(prologueClass); AnalysisMethod[] prologueMethods = prologue.getDeclaredMethods(false); - UserError.guarantee(prologueMethods.length == 1 && prologueMethods[0].isStatic(), - "Prologue class must declare exactly one static method: %s -> %s", - targetMethod, - prologue); - UserError.guarantee(Uninterruptible.Utils.isUninterruptible(prologueMethods[0]), - "Prologue method must be annotated with @%s: %s", Uninterruptible.class.getSimpleName(), prologueMethods[0]); + UserError.guarantee(prologueMethods.length == 1 && prologueMethods[0].isStatic(), "Prologue class must declare exactly one static method: %s -> %s", targetMethod, prologue); ValueNode[] prologueArgs = matchPrologueParameters(kit, parameterTypes, args, prologueMethods[0]); - return generatePrologueOrEpilogueInvoke(kit, prologueMethods[0], prologueArgs); + return createInvokeStaticWithFatalExceptionHandler(kit, prologueMethods[0], prologueArgs); } - // Automatically choose prologue from signature and annotations and call + /* Find the IsolateThread argument that should be used as the context. */ ExecutionContextParameters executionContext = findExecutionContextParameters(kit, parameterTypes, parameterAnnotations); int contextIndex = -1; if (executionContext.designatedThreadIndex != -1) { @@ -427,19 +481,24 @@ private InvokeWithExceptionNode generatePrologue(HostedGraphKit kit, List %s", - targetMethod, - prologueMethod); + UserError.guarantee(prologueType.isPrimitive() || kit.getWordTypes().isWord(prologueType), "Prologue method parameter types are restricted to primitive types and word types: %s -> %s", + targetMethod, prologueMethod); while (i < types.size() && !prologueType.isAssignableFrom(types.get(i))) { i++; } + if (i >= types.size()) { - throw UserError.abort("Unable to match signature of entry point method to that of prologue method: %s -> %s", - targetMethod, - prologueMethod); + throw UserError.abort("Unable to match signature of entry point method to that of prologue method: %s -> %s", targetMethod, prologueMethod); } prologueValues[p] = values[i]; i++; @@ -541,61 +587,73 @@ private ValueNode[] matchPrologueParameters(HostedGraphKit kit, List %s", targetMethod, handler); - UserError.guarantee(Uninterruptible.Utils.isUninterruptible(handlerMethods[0]), - "Exception handler method must be annotated with @%s: %s", Uninterruptible.class.getSimpleName(), handlerMethods[0]); - List handlerParameterTypes = handlerMethods[0].toParameterList(); - UserError.guarantee(handlerParameterTypes.size() == 1 && - handlerParameterTypes.get(0).isAssignableFrom(throwable), - "Exception handler method must have exactly one parameter of type Throwable: %s -> %s", targetMethod, handlerMethods[0]); - InvokeWithExceptionNode handlerInvoke = kit.startInvokeWithException(handlerMethods[0], InvokeKind.Static, kit.getFrameState(), kit.bci(), exception); - kit.noExceptionPart(); - ValueNode returnValue = handlerInvoke; - if (handlerInvoke.getStackKind() != returnKind) { - JavaKind fromKind = handlerInvoke.getStackKind(); - if (fromKind == JavaKind.Float && returnKind == JavaKind.Double) { - returnValue = kit.unique(new FloatConvertNode(FloatConvert.F2D, returnValue)); - } else if (fromKind.isUnsigned() && returnKind.isNumericInteger() && returnKind.getBitCount() > fromKind.getBitCount()) { - returnValue = kit.unique(new ZeroExtendNode(returnValue, returnKind.getBitCount())); - } else if (fromKind.isNumericInteger() && returnKind.isNumericInteger() && returnKind.getBitCount() > fromKind.getBitCount()) { - returnValue = kit.unique(new SignExtendNode(returnValue, returnKind.getBitCount())); - } else { - throw UserError.abort("Exception handler method return type must be assignable to entry point method return type: %s -> %s", - targetMethod, handlerMethods[0]); - } - } - - /* The exception is handled, we can continue with the normal epilogue. */ - generateEpilogueAndReturn(method, kit, returnValue); + return; + } - kit.exceptionPart(); // fail-safe for exceptions in exception handler - kit.append(new CEntryPointLeaveNode(LeaveAction.ExceptionAbort, kit.exceptionObject())); - kit.append(new LoweredDeadEndNode()); - kit.endInvokeWithException(); + /* Determine which method was registered as the exception handler. */ + AnalysisType throwable = kit.getMetaAccess().lookupJavaType(Throwable.class); + AnalysisType handler = kit.getMetaAccess().lookupJavaType(entryPointData.getExceptionHandler()); + AnalysisMethod[] handlerMethods = handler.getDeclaredMethods(false); + UserError.guarantee(handlerMethods.length == 1 && handlerMethods[0].isStatic(), "Exception handler class must declare exactly one static method: %s -> %s", targetMethod, handler); + UserError.guarantee(isUninterruptible(handlerMethods[0]), "Exception handler method must be annotated with @%s: %s", Uninterruptible.class.getSimpleName(), handlerMethods[0]); + + List handlerParameterTypes = handlerMethods[0].toParameterList(); + UserError.guarantee(handlerParameterTypes.size() == 1 && handlerParameterTypes.getFirst().isAssignableFrom(throwable), + "Exception handler method must have exactly one parameter of type Throwable: %s -> %s", targetMethod, handlerMethods[0]); + + /* Invoke the exception handler method. */ + InvokeWithExceptionNode handlerInvoke = kit.startInvokeWithException(handlerMethods[0], InvokeKind.Static, kit.getFrameState(), kit.bci(), exception); + handlerInvoke.setUseForInlining(false); + + /* + * The exception handler method finished successfully. Invoke the epilogue and return the + * value that was returned from the exception handler method. + */ + kit.noExceptionPart(); + ValueNode returnValue = handlerInvoke; + if (handlerInvoke.getStackKind() != returnKind) { + JavaKind fromKind = handlerInvoke.getStackKind(); + if (fromKind == JavaKind.Float && returnKind == JavaKind.Double) { + returnValue = kit.unique(new FloatConvertNode(FloatConvert.F2D, returnValue)); + } else if (fromKind.isUnsigned() && returnKind.isNumericInteger() && returnKind.getBitCount() > fromKind.getBitCount()) { + returnValue = kit.unique(new ZeroExtendNode(returnValue, returnKind.getBitCount())); + } else if (fromKind.isNumericInteger() && returnKind.isNumericInteger() && returnKind.getBitCount() > fromKind.getBitCount()) { + returnValue = kit.unique(new SignExtendNode(returnValue, returnKind.getBitCount())); + } else { + throw UserError.abort("Exception handler method return type must be assignable to entry point method return type: %s -> %s", + targetMethod, handlerMethods[0]); + } } + generateEpilogueAndReturn(method, kit, returnValue); + + /* The exception handler method threw an exception. This is a fatal error. */ + kit.exceptionPart(); + kit.append(new CEntryPointLeaveNode(LeaveAction.ExceptionAbort, kit.exceptionObject())); + kit.append(new LoweredDeadEndNode()); + kit.endInvokeWithException(); } + /** Converts a Java enum to a primitive value if necessary, see {@link CEnum}. */ private ValueNode adaptReturnValue(HostedGraphKit kit, ValueNode value) { if (value.getStackKind().isPrimitive()) { + /* No conversion necessary. */ return value; } - /* The method returns a Java enum, so we need to convert the enum to a primitive value. */ AnalysisType returnType = targetSignature.getReturnType(); NativeLibraries nativeLibraries = NativeLibraries.singleton(); - EnumInfo enumInfo = getEnumInfo(nativeLibraries, targetMethod, returnType, true); - ValueNode result = CInterfaceEnumTool.singleton().startInvokeWithExceptionEnumToValue(kit, enumInfo, CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess()), value); - result = kit.getGraph().unique(new ZeroExtendNode(result, kit.getWordTypes().getWordKind().getBitCount())); + /* Invoke the method that does the conversion from Java enum to primitive value. */ + InvokeWithExceptionNode invoke = CInterfaceEnumTool.singleton().startInvokeWithExceptionEnumToValue(kit, enumInfo, CInterfaceEnumTool.getCEnumValueType(enumInfo, kit.getMetaAccess()), value); + invoke.setUseForInlining(false); + ValueNode result = kit.getGraph().unique(new ZeroExtendNode(invoke, kit.getWordTypes().getWordKind().getBitCount())); + + /* Exceptions during the conversion are fatal. */ kit.exceptionPart(); kit.append(new CEntryPointLeaveNode(LeaveAction.ExceptionAbort, kit.exceptionObject())); kit.append(new LoweredDeadEndNode()); @@ -606,21 +664,18 @@ private ValueNode adaptReturnValue(HostedGraphKit kit, ValueNode value) { private void generateEpilogue(HostedGraphKit kit) { Class epilogueClass = entryPointData.getEpilogue(); if (epilogueClass == NoEpilogue.class) { - UserError.guarantee(Uninterruptible.Utils.isUninterruptible(targetMethod), - "%s.%s is allowed only for methods annotated with @%s: %s", - CEntryPointOptions.class.getSimpleName(), - NoEpilogue.class.getSimpleName(), - Uninterruptible.class.getSimpleName(), - targetMethod); + UserError.guarantee(isUninterruptible(targetMethod), "%s.%s is allowed only for methods annotated with @%s: %s", + CEntryPointOptions.class.getSimpleName(), NoEpilogue.class.getSimpleName(), Uninterruptible.class.getSimpleName(), targetMethod); return; } + AnalysisType epilogue = kit.getMetaAccess().lookupJavaType(epilogueClass); AnalysisMethod[] epilogueMethods = epilogue.getDeclaredMethods(false); UserError.guarantee(epilogueMethods.length == 1 && epilogueMethods[0].isStatic() && epilogueMethods[0].getSignature().getParameterCount(false) == 0, "Epilogue class must declare exactly one static method without parameters: %s -> %s", targetMethod, epilogue); - UserError.guarantee(Uninterruptible.Utils.isUninterruptible(epilogueMethods[0]), - "Epilogue method must be annotated with @%s: %s", Uninterruptible.class.getSimpleName(), epilogueMethods[0]); - generatePrologueOrEpilogueInvoke(kit, epilogueMethods[0]); + + InvokeWithExceptionNode invoke = createInvokeStaticWithFatalExceptionHandler(kit, epilogueMethods[0]); + invoke.setUseForInlining(false); } public boolean isNotPublished() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java index 426cceb807a0..0c03a95d798e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/CInterfaceEnumTool.java @@ -139,7 +139,7 @@ public ValueNode createInvokeEnumToValue(HostedGraphKit kit, EnumInfo enumInfo, return kit.createInvokeWithExceptionAndUnwind(callTarget, kit.getFrameState(), invokeBci); } - public ValueNode startInvokeWithExceptionEnumToValue(HostedGraphKit kit, EnumInfo enumInfo, AnalysisType returnType, ValueNode arg) { + public InvokeWithExceptionNode startInvokeWithExceptionEnumToValue(HostedGraphKit kit, EnumInfo enumInfo, AnalysisType returnType, ValueNode arg) { int invokeBci = kit.bci(); MethodCallTargetNode callTarget = createInvokeEnumToValue(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, returnType, arg); return kit.startInvokeWithException(callTarget, kit.getFrameState(), invokeBci); @@ -183,11 +183,14 @@ private AnalysisMethod getEnumToValueMethod(GraphBuilderTool b, AnalysisType ret }; } - public ValueNode createInvokeLookupEnum(HostedGraphKit kit, AnalysisType enumType, EnumInfo enumInfo, ValueNode arg) { - // Create the invocation of the actual target method: EnumRuntimeData.convertCToJava + public ValueNode createInvokeLookupEnum(HostedGraphKit kit, AnalysisType enumType, EnumInfo enumInfo, ValueNode arg, boolean allowInlining) { + // Invoke the conversion function (from long to enum) int invokeBci = kit.bci(); MethodCallTargetNode callTarget = createInvokeLongToEnum(kit, CallTargetFactory.from(kit), invokeBci, enumInfo, arg); InvokeWithExceptionNode invoke = kit.createInvokeWithExceptionAndUnwind(callTarget, kit.getFrameState(), invokeBci); + if (!allowInlining) { + invoke.setUseForInlining(false); + } // Create the instanceof guard to narrow the return type for the analysis LogicNode instanceOfNode = kit.append(InstanceOfNode.createAllowNull(TypeReference.createExactTrusted(enumType), invoke, null, null));