Skip to content

Commit c924a4e

Browse files
[GR-66563] [GR-66569] Better frame anchor verification and fully uninterruptible exception handling.
PullRequest: graal/21264
2 parents 670a86f + 1e26b5f commit c924a4e

File tree

6 files changed

+108
-84
lines changed

6 files changed

+108
-84
lines changed

substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
package com.oracle.svm.core.graal.llvm.runtime;
2626

2727
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
28+
import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION;
2829

2930
import java.util.function.BooleanSupplier;
3031

31-
import jdk.graal.compiler.core.common.NumUtil;
3232
import org.graalvm.nativeimage.CurrentIsolate;
3333
import org.graalvm.nativeimage.IsolateThread;
3434
import org.graalvm.nativeimage.c.CContext;
@@ -50,6 +50,7 @@
5050
import com.oracle.svm.core.stack.StackOverflowCheck;
5151
import com.oracle.svm.hosted.code.CEntryPointCallStubSupport;
5252

53+
import jdk.graal.compiler.core.common.NumUtil;
5354
import jdk.vm.ci.meta.MetaAccessProvider;
5455
import jdk.vm.ci.meta.ResolvedJavaMethod;
5556

@@ -142,6 +143,7 @@ public static ResolvedJavaMethod getRetrieveExceptionMethod(MetaAccessProvider m
142143
public static ExceptionUnwind createRaiseExceptionHandler() {
143144
return new ExceptionUnwind() {
144145
@Override
146+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
145147
protected void customUnwindException(Pointer callerSP) {
146148
_Unwind_Exception exceptionStructure = UnsafeStackValue.get(_Unwind_Exception.class);
147149
exceptionStructure.set_exception_class(CurrentIsolate.getCurrentThread());
@@ -198,7 +200,7 @@ private interface _Unwind_Exception extends PointerBase {
198200
private interface _Unwind_Context extends PointerBase {
199201
}
200202

201-
@CFunction(value = "_Unwind_RaiseException")
203+
@CFunction(value = "_Unwind_RaiseException", transition = NO_TRANSITION)
202204
public static native int raiseException(_Unwind_Exception exception);
203205

204206
@CFunction(value = "_Unwind_GetIP")

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424
*/
2525
package com.oracle.svm.core.deopt;
2626

27+
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
2729
import java.lang.ref.WeakReference;
2830

29-
import jdk.graal.compiler.word.Word;
3031
import org.graalvm.nativeimage.PinnedObject;
3132
import org.graalvm.nativeimage.c.function.CodePointer;
3233
import org.graalvm.word.Pointer;
3334

34-
import com.oracle.svm.core.NeverInline;
3535
import com.oracle.svm.core.Uninterruptible;
3636
import com.oracle.svm.core.code.CodeInfo;
3737
import com.oracle.svm.core.code.CodeInfoAccess;
@@ -42,17 +42,15 @@
4242
import com.oracle.svm.core.config.ConfigurationValues;
4343
import com.oracle.svm.core.deopt.Deoptimizer.TargetContent;
4444
import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue;
45-
import com.oracle.svm.core.heap.RestrictHeapAccess;
4645
import com.oracle.svm.core.log.StringBuilderLog;
4746
import com.oracle.svm.core.meta.SubstrateObjectConstant;
4847
import com.oracle.svm.core.monitor.MonitorSupport;
4948

5049
import jdk.graal.compiler.nodes.FrameState;
50+
import jdk.graal.compiler.word.Word;
5151
import jdk.vm.ci.code.InstalledCode;
5252
import jdk.vm.ci.meta.JavaConstant;
5353

54-
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
55-
5654
/**
5755
* The handle to a deoptimized frame. It contains all stack entries which are written to the frame
5856
* of the deopt target method(s). For details see {@link Deoptimizer}.
@@ -407,6 +405,7 @@ protected void buildContent(Pointer newSp) {
407405
* deoptimization stub return to the exception handler instead of the regular return address of
408406
* the deoptimization target.
409407
*/
408+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
410409
public void takeException() {
411410
if (rethrowException) {
412411
/*
@@ -415,6 +414,7 @@ public void takeException() {
415414
*/
416415
return;
417416
}
417+
418418
ReturnAddress firstAddressEntry = topFrame.returnAddress;
419419
CodePointer ip = Word.pointer(firstAddressEntry.returnAddress);
420420
CodeInfo info = CodeInfoTable.getImageCodeInfo(ip);
@@ -427,13 +427,15 @@ public void takeException() {
427427
firstAddressEntry.returnAddress += handler;
428428
}
429429

430-
@NeverInline("Has more relaxed heap access requirements than caller.")
431-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Printing out error and then crashing.")
430+
@Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
432431
private static void throwMissingExceptionHandler(CodeInfo info, ReturnAddress firstAddressEntry) {
432+
throwMissingExceptionHandler0(info, firstAddressEntry);
433+
}
434+
435+
private static void throwMissingExceptionHandler0(CodeInfo info, ReturnAddress firstAddressEntry) {
433436
CodeInfoQueryResult detailedQueryResult = new CodeInfoQueryResult();
434437
CodeInfoAccess.lookupCodeInfo(info, Word.pointer(firstAddressEntry.returnAddress), detailedQueryResult);
435438
FrameInfoQueryResult frameInfo = detailedQueryResult.getFrameInfo();
436439
throw Deoptimizer.fatalDeoptimizationError("No exception handler registered for deopt target", frameInfo);
437440
}
438-
439441
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.core.deopt;
2626

2727
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
import static com.oracle.svm.core.stack.JavaFrameAnchors.verifyTopFrameAnchor;
2829

2930
import java.lang.annotation.ElementType;
3031
import java.lang.annotation.Retention;
@@ -813,7 +814,7 @@ private static boolean isNonNullValue(UnsignedWord pointer) {
813814

814815
@DeoptStub(stubType = StubType.LazyEntryStub)
815816
@Uninterruptible(reason = "Rewriting stack; gpReturnValue holds object reference.")
816-
public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
817+
public static UnsignedWord lazyDeoptStubObjectReturn(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
817818
try {
818819
assert PointerUtils.isAMultiple(KnownIntrinsics.readStackPointer(), Word.unsigned(ConfigurationValues.getTarget().stackAlignment));
819820
assert Options.LazyDeoptimization.getValue();
@@ -828,7 +829,7 @@ public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, Unsig
828829
gpReturnValueObject = ((Pointer) gpReturnValue).toObject();
829830
}
830831

831-
lazyDeoptStubCore(framePointer, gpReturnValue, fpReturnValue, hasException, gpReturnValueObject);
832+
lazyDeoptStubCore(originalStackPointer, gpReturnValue, fpReturnValue, hasException, gpReturnValueObject);
832833
throw UnreachableNode.unreachable();
833834

834835
} catch (Throwable t) {
@@ -838,7 +839,7 @@ public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, Unsig
838839

839840
@DeoptStub(stubType = StubType.LazyEntryStub)
840841
@Uninterruptible(reason = "Rewriting stack.")
841-
public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
842+
public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
842843
/*
843844
* Note: when we dispatch an exception, we enter lazyDeoptStubObjectReturn instead, since
844845
* that involves returning an exception object.
@@ -849,7 +850,7 @@ public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, Un
849850
assert VMThreads.StatusSupport.isStatusJava() : "Deopt stub execution must not be visible to other threads.";
850851
assert !ExceptionUnwind.getLazyDeoptStubShouldReturnToExceptionHandler();
851852

852-
lazyDeoptStubCore(framePointer, gpReturnValue, fpReturnValue, false, null);
853+
lazyDeoptStubCore(originalStackPointer, gpReturnValue, fpReturnValue, false, null);
853854
throw UnreachableNode.unreachable();
854855

855856
} catch (Throwable t) {
@@ -864,32 +865,32 @@ public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, Un
864865
* the code info, and construct the {@link DeoptimizedFrame}.
865866
*/
866867
@Uninterruptible(reason = "Rewriting stack.")
867-
private static UnsignedWord lazyDeoptStubCore(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue, boolean hasException, Object gpReturnValueObject) {
868-
CodePointer deoptStubAddress = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), framePointer);
868+
private static UnsignedWord lazyDeoptStubCore(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue, boolean hasException, Object gpReturnValueObject) {
869+
CodePointer deoptStubAddress = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), originalStackPointer);
869870
assert isLazyDeoptStub(deoptStubAddress);
870871

871872
/* The original return address is at offset 0 from the stack pointer */
872-
CodePointer originalReturnAddress = framePointer.readWord(0);
873+
CodePointer originalReturnAddress = originalStackPointer.readWord(0);
873874
VMError.guarantee(originalReturnAddress.isNonNull());
874875

875876
DeoptimizedFrame deoptFrame;
876877
try {
877-
deoptFrame = constructLazilyDeoptimizedFrameInterruptibly(framePointer, originalReturnAddress, hasException);
878+
deoptFrame = constructLazilyDeoptimizedFrameInterruptibly(originalStackPointer, originalReturnAddress, hasException);
878879
} catch (OutOfMemoryError ex) {
879880
/*
880881
* If a OutOfMemoryError occurs during lazy deoptimization, we cannot let the frame
881882
* being deoptimized handle the exception, because it might have been invalidated due to
882883
* incorrect assumptions. Note that since unwindExceptionSkippingCaller does not return,
883884
* this try...catch must not have a finally block, as it will not be executed.
884885
*/
885-
ExceptionUnwind.unwindExceptionSkippingCaller(ex, framePointer);
886+
ExceptionUnwind.unwindExceptionSkippingCaller(ex, originalStackPointer);
886887
throw UnreachableNode.unreachable();
887888
}
888889

889890
DeoptimizationCounters.counters().deoptCount.inc();
890891
VMError.guarantee(deoptFrame != null, "was not able to lazily construct a deoptimized frame");
891892

892-
Pointer newSp = computeNewFramePointer(framePointer, deoptFrame);
893+
Pointer newSp = computeNewStackPointer(originalStackPointer, deoptFrame);
893894

894895
/* Build the content of the deopt target stack frames. */
895896
deoptFrame.buildContent(newSp);
@@ -900,9 +901,9 @@ private static UnsignedWord lazyDeoptStubCore(Pointer framePointer, UnsignedWord
900901
* can only be called from the current thread. Thus, there is no use case for eager
901902
* deoptimization to happen if the current thread is executing the lazy deopt stub.
902903
*/
903-
VMError.guarantee(framePointer.readWord(0) == originalReturnAddress, "Eager deoptimization should not occur when lazy deoptimization is in progress");
904+
VMError.guarantee(originalStackPointer.readWord(0) == originalReturnAddress, "Eager deoptimization should not occur when lazy deoptimization is in progress");
904905

905-
CodePointer returnAddressAfter = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), framePointer);
906+
CodePointer returnAddressAfter = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), originalStackPointer);
906907
VMError.guarantee(returnAddressAfter == deoptStubAddress, "Return address must remain unchanged during deoptimization");
907908

908909
recentDeoptimizationEvents.append(deoptFrame.getCompletedMessage());
@@ -962,7 +963,7 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
962963
* When {@link #eagerDeoptStub} is "called", the stack looks like this:
963964
*
964965
* <pre>
965-
* : :
966+
* : : highest stack address
966967
* | |
967968
* | | frame of the
968969
* +--------------------------------+ deoptimized method
@@ -977,9 +978,8 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
977978
* The instructions to compute the parameters must be generated in this method's prologue by a
978979
* backend-specific FrameContext class.
979980
*
980-
* @param framePointer This is a pointer to the reference which was written in
981-
* {@link #deoptimizeInRange} on the stack (the slot above the original return
982-
* address).
981+
* @param originalStackPointer the original stack pointer of the deoptimized method (points to
982+
* the {@link DeoptimizedFrame} object).
983983
* @param gpReturnValue This is the value which was stored in the general purpose return
984984
* register when the deopt stub was reached. It must be restored to the register
985985
* before completion of the stub.
@@ -989,18 +989,19 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
989989
*/
990990
@DeoptStub(stubType = StubType.EagerEntryStub)
991991
@Uninterruptible(reason = "Frame holds Objects in unmanaged storage.")
992-
public static UnsignedWord eagerDeoptStub(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
992+
public static UnsignedWord eagerDeoptStub(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
993993
try {
994994
assert PointerUtils.isAMultiple(KnownIntrinsics.readStackPointer(), Word.unsigned(ConfigurationValues.getTarget().stackAlignment));
995995
VMError.guarantee(VMThreads.StatusSupport.isStatusJava(), "Deopt stub execution must not be visible to other threads.");
996-
DeoptimizedFrame frame = (DeoptimizedFrame) ReferenceAccess.singleton().readObjectAt(framePointer, true);
996+
997+
DeoptimizedFrame frame = (DeoptimizedFrame) ReferenceAccess.singleton().readObjectAt(originalStackPointer, true);
997998

998999
DeoptimizationCounters.counters().deoptCount.inc();
9991000
if (DeoptimizationCounters.Options.ProfileDeoptimization.getValue()) {
10001001
DeoptimizationCounters.startTime.set(System.nanoTime());
10011002
}
10021003

1003-
final Pointer newSp = computeNewFramePointer(framePointer, frame);
1004+
final Pointer newSp = computeNewStackPointer(originalStackPointer, frame);
10041005

10051006
/* Build the content of the deopt target stack frames. */
10061007
frame.buildContent(newSp);
@@ -1023,13 +1024,15 @@ public static UnsignedWord eagerDeoptStub(Pointer framePointer, UnsignedWord gpR
10231024
}
10241025

10251026
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
1026-
private static Pointer computeNewFramePointer(Pointer originalFramePointer, DeoptimizedFrame deoptimizedFrame) {
1027-
/* Computation of the new stack pointer: we start with the stack pointer of this frame. */
1028-
return originalFramePointer
1029-
/* Remove the size of the frame that gets deoptimized. */
1030-
.add(Word.unsigned(deoptimizedFrame.getSourceTotalFrameSize()))
1031-
/* Add the size of the deoptimization target frames. */
1032-
.subtract(deoptimizedFrame.getTargetContent().getSize());
1027+
private static Pointer computeNewStackPointer(Pointer originalStackPointer, DeoptimizedFrame deoptimizedFrame) {
1028+
/* Remove the size of the frame that gets deoptimized. */
1029+
Pointer callerStackPointer = originalStackPointer.add(Word.unsigned(deoptimizedFrame.getSourceTotalFrameSize()));
1030+
1031+
/* Verify that the top frame anchor is in a part of the stack that is not rewritten. */
1032+
verifyTopFrameAnchor(callerStackPointer);
1033+
1034+
/* Add the size of the deoptimization target frames. */
1035+
return callerStackPointer.subtract(deoptimizedFrame.getTargetContent().getSize());
10331036
}
10341037

10351038
/**
@@ -1054,7 +1057,6 @@ private static UnsignedWord rewriteStackStub(Pointer newSp, UnsignedWord gpRetur
10541057
if (DeoptimizationCounters.Options.ProfileDeoptimization.getValue()) {
10551058
DeoptimizationCounters.counters().timeSpentInDeopt.add(System.nanoTime() - DeoptimizationCounters.startTime.get());
10561059
}
1057-
10581060
return gpReturnValue;
10591061
}
10601062

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CFunctionSnippets.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,20 @@ private static void matchCallStructure(CFunctionPrologueNode prologueNode) {
276276
}
277277
}
278278

279+
/**
280+
* {@link CFunctionSnippets} may only be used for code that cannot be deoptimized. Otherwise,
281+
* deoptimization could destroy stack allocated {@link JavaFrameAnchor} structs when rewriting the
282+
* stack.
283+
*/
279284
@AutomaticallyRegisteredFeature
280285
@Platforms(InternalPlatform.NATIVE_ONLY.class)
281286
class CFunctionSnippetsFeature implements InternalFeature {
282-
283287
@Override
284288
@SuppressWarnings("unused")
285289
public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Providers providers,
286290
Map<Class<? extends Node>, NodeLoweringProvider<?>> lowerings, boolean hosted) {
287-
new CFunctionSnippets(options, providers, lowerings);
291+
if (hosted) {
292+
new CFunctionSnippets(options, providers, lowerings);
293+
}
288294
}
289295
}

0 commit comments

Comments
 (0)