Skip to content

Commit b50044e

Browse files
Eric Wupeter-hofer
authored andcommitted
[GR-55222] Support Lazy Deoptimization
PullRequest: graal/19048
2 parents 75344f7 + 2805efd commit b50044e

File tree

36 files changed

+867
-254
lines changed

36 files changed

+867
-254
lines changed

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ private static void walkStack(IsolateThread thread, JavaStackWalk walk, ObjectRe
847847
VMError.guarantee(!JavaFrames.isUnknownFrame(frame), "GC must not encounter unknown frames");
848848

849849
/* We are during a GC, so tethering of the CodeInfo is not necessary. */
850-
DeoptimizedFrame deoptFrame = Deoptimizer.checkDeoptimized(frame);
850+
DeoptimizedFrame deoptFrame = Deoptimizer.checkEagerDeoptimized(frame);
851851
if (deoptFrame == null) {
852852
Pointer sp = frame.getSP();
853853
CodeInfo codeInfo = CodeInfoAccess.unsafeConvert(frame.getIPCodeInfo());

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/RuntimeCodeCacheWalker.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,26 +76,28 @@ public boolean visitCode(CodeInfo codeInfo) {
7676
Object tether = UntetheredCodeInfoAccess.getTetherUnsafe(codeInfo);
7777
if (tether != null && !isReachable(tether)) {
7878
int state = CodeInfoAccess.getState(codeInfo);
79-
if (state == CodeInfo.STATE_INVALIDATED) {
79+
if (state == CodeInfo.STATE_REMOVED_FROM_CODE_CACHE) {
8080
/*
81-
* The tether object is not reachable and the CodeInfo was already invalidated, so
82-
* we only need to visit references that will be accessed before the unmanaged
83-
* memory is freed during this garbage collection.
81+
* The tether object is not reachable and the CodeInfo was already removed from the
82+
* code cache, so we only need to visit references that will be accessed before the
83+
* unmanaged memory is freed during this garbage collection.
8484
*/
8585
RuntimeCodeInfoAccess.walkObjectFields(codeInfo, greyToBlackObjectVisitor);
86-
CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_UNREACHABLE);
86+
CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_PENDING_FREE);
8787
return true;
8888
}
8989

9090
/*
91-
* We don't want to keep heap objects unnecessarily alive, so invalidate and free the
92-
* CodeInfo if it has weak references to otherwise unreachable objects. However, we need
93-
* to make sure that all the objects that are accessed during the invalidation remain
94-
* reachable. Those objects can only be collected in a subsequent garbage collection.
91+
* We don't want to keep heap objects unnecessarily alive. So, we check if the CodeInfo
92+
* has weak references to otherwise unreachable objects. If so, we remove the CodeInfo
93+
* from the code cache and free the CodeInfo during the current safepoint (see
94+
* RuntimeCodeCacheCleaner). However, we need to make sure that all the objects that are
95+
* accessed while doing so remain reachable. Those objects can only be collected in a
96+
* subsequent garbage collection.
9597
*/
9698
if (state == CodeInfo.STATE_NON_ENTRANT || invalidateCodeThatReferencesUnreachableObjects && state == CodeInfo.STATE_CODE_CONSTANTS_LIVE && hasWeakReferenceToUnreachableObject(codeInfo)) {
9799
RuntimeCodeInfoAccess.walkObjectFields(codeInfo, greyToBlackObjectVisitor);
98-
CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_READY_FOR_INVALIDATION);
100+
CodeInfoAccess.setState(codeInfo, CodeInfo.STATE_PENDING_REMOVAL_FROM_CODE_CACHE);
99101
return true;
100102
}
101103
}

substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -469,19 +469,27 @@ public SubstrateLIRGenerationResult(CompilationIdentifier compilationId, LIR lir
469469
super(compilationId, lir, frameMapBuilder, registerAllocationConfig, callingConvention);
470470
this.method = method;
471471

472-
if (method.hasCalleeSavedRegisters()) {
472+
/*
473+
* Besides for methods with callee saved registers, we reserve additional stack space
474+
* for lazyDeoptStub too. This is necessary because the lazy deopt stub might read
475+
* callee-saved register values in the callee of the function to be deoptimized, thus
476+
* that stack space must not be overwritten by the lazy deopt stub.
477+
*/
478+
if (method.hasCalleeSavedRegisters() || method.getDeoptStubType() == Deoptimizer.StubType.LazyEntryStub) {
473479
AArch64CalleeSavedRegisters calleeSavedRegisters = AArch64CalleeSavedRegisters.singleton();
474480
FrameMap frameMap = ((FrameMapBuilderTool) frameMapBuilder).getFrameMap();
475481
int registerSaveAreaSizeInBytes = calleeSavedRegisters.getSaveAreaSize();
476482
StackSlot calleeSaveArea = frameMap.allocateStackMemory(registerSaveAreaSizeInBytes, frameMap.getTarget().wordSize);
477483

478-
/*
479-
* The offset of the callee save area must be fixed early during image generation.
480-
* It is accessed when compiling methods that have a call with callee-saved calling
481-
* convention. Here we verify that offset computed earlier is the same as the offset
482-
* actually reserved.
483-
*/
484-
calleeSavedRegisters.verifySaveAreaOffsetInFrame(calleeSaveArea.getRawOffset());
484+
if (method.hasCalleeSavedRegisters()) {
485+
/*
486+
* The offset of the callee save area must be fixed early during image
487+
* generation. It is accessed when compiling methods that have a call with
488+
* callee-saved calling convention. Here we verify that offset computed earlier
489+
* is the same as the offset actually reserved.
490+
*/
491+
calleeSavedRegisters.verifySaveAreaOffsetInFrame(calleeSaveArea.getRawOffset());
492+
}
485493
}
486494

487495
if (method.canDeoptimize() || method.isDeoptTarget()) {
@@ -951,8 +959,8 @@ public void returned(CompilationResultBuilder crb) {
951959
}
952960

953961
/**
954-
* Generates the prolog of a {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EntryStub}
955-
* method.
962+
* Generates the prolog of a
963+
* {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EagerEntryStub} method.
956964
*/
957965
protected static class DeoptEntryStubContext extends SubstrateAArch64FrameContext {
958966
protected final CallingConvention callingConvention;
@@ -1241,7 +1249,7 @@ public CompilationResultBuilder newCompilationResultBuilder(LIRGenerationResult
12411249
}
12421250

12431251
protected FrameContext createFrameContext(SharedMethod method, Deoptimizer.StubType stubType, CallingConvention callingConvention) {
1244-
if (stubType == Deoptimizer.StubType.EntryStub) {
1252+
if (stubType == Deoptimizer.StubType.EagerEntryStub || stubType == Deoptimizer.StubType.LazyEntryStub) {
12451253
return new DeoptEntryStubContext(method, callingConvention);
12461254
} else if (stubType == Deoptimizer.StubType.ExitStub) {
12471255
return new DeoptExitStubContext(method, callingConvention);

substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64RegisterConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,9 @@ public CallingConvention getCallingConvention(Type t, JavaType returnType, JavaT
289289
int currentFP = 0;
290290

291291
/*
292-
* We have to reserve a slot between return address and outgoing parameters for the deopt
293-
* frame handle. Exception: calls to native methods.
292+
* We have to reserve a slot between return address and outgoing parameters for the
293+
* deoptimized frame (eager deoptimization), or the original return address (lazy
294+
* deoptimization). Exception: calls to native methods.
294295
*/
295296
int currentStackOffset = (type.nativeABI() ? nativeParamsStackOffset : target.wordSize);
296297

substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -555,19 +555,27 @@ public SubstrateLIRGenerationResult(CompilationIdentifier compilationId, LIR lir
555555
super(compilationId, lir, frameMapBuilder, registerAllocationConfig, callingConvention);
556556
this.method = method;
557557

558-
if (method.hasCalleeSavedRegisters()) {
558+
/*
559+
* Besides for methods with callee saved registers, we reserve additional stack space
560+
* for lazyDeoptStub too. This is necessary because the lazy deopt stub might read
561+
* callee-saved register values in the callee of the function to be deoptimized, thus
562+
* that stack space must not be overwritten by the lazy deopt stub.
563+
*/
564+
if (method.hasCalleeSavedRegisters() || method.getDeoptStubType() == Deoptimizer.StubType.LazyEntryStub) {
559565
AMD64CalleeSavedRegisters calleeSavedRegisters = AMD64CalleeSavedRegisters.singleton();
560566
FrameMap frameMap = ((FrameMapBuilderTool) frameMapBuilder).getFrameMap();
561567
int registerSaveAreaSizeInBytes = calleeSavedRegisters.getSaveAreaSize();
562568
StackSlot calleeSaveArea = frameMap.allocateStackMemory(registerSaveAreaSizeInBytes, frameMap.getTarget().wordSize);
563569

564-
/*
565-
* The offset of the callee save area must be fixed early during image generation.
566-
* It is accessed when compiling methods that have a call with callee-saved calling
567-
* convention. Here we verify that offset computed earlier is the same as the offset
568-
* actually reserved.
569-
*/
570-
calleeSavedRegisters.verifySaveAreaOffsetInFrame(calleeSaveArea.getRawOffset());
570+
if (method.hasCalleeSavedRegisters()) {
571+
/*
572+
* The offset of the callee save area must be fixed early during image
573+
* generation. It is accessed when compiling methods that have a call with
574+
* callee-saved calling convention. Here we verify that offset computed earlier
575+
* is the same as the offset actually reserved.
576+
*/
577+
calleeSavedRegisters.verifySaveAreaOffsetInFrame(calleeSaveArea.getRawOffset());
578+
}
571579
}
572580

573581
if (method.canDeoptimize() || method.isDeoptTarget()) {
@@ -1327,8 +1335,8 @@ public void returned(CompilationResultBuilder crb) {
13271335
}
13281336

13291337
/**
1330-
* Generates the prolog of a {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EntryStub}
1331-
* method.
1338+
* Generates the prolog of a
1339+
* {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EagerEntryStub} method.
13321340
*/
13331341
protected static class DeoptEntryStubContext extends SubstrateAMD64FrameContext {
13341342
protected DeoptEntryStubContext(SharedMethod method, CallingConvention callingConvention) {
@@ -1367,7 +1375,7 @@ public void enter(CompilationResultBuilder tasm) {
13671375
* method.
13681376
*
13691377
* Note no special handling is necessary for CFI as this will be a direct call from the
1370-
* {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EntryStub}.
1378+
* {@link com.oracle.svm.core.deopt.Deoptimizer.StubType#EagerEntryStub}.
13711379
*/
13721380
protected static class DeoptExitStubContext extends SubstrateAMD64FrameContext {
13731381
protected DeoptExitStubContext(SharedMethod method, CallingConvention callingConvention) {
@@ -1811,7 +1819,7 @@ protected AMD64MacroAssembler createAssembler(OptionValues options) {
18111819
}
18121820

18131821
protected FrameContext createFrameContext(SharedMethod method, Deoptimizer.StubType stubType, CallingConvention callingConvention) {
1814-
if (stubType == Deoptimizer.StubType.EntryStub) {
1822+
if (stubType == Deoptimizer.StubType.EagerEntryStub || stubType == Deoptimizer.StubType.LazyEntryStub) {
18151823
return new DeoptEntryStubContext(method, callingConvention);
18161824
} else if (stubType == Deoptimizer.StubType.ExitStub) {
18171825
return new DeoptExitStubContext(method, callingConvention);

substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64RegisterConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,9 @@ public CallingConvention getCallingConvention(Type t, JavaType returnType, JavaT
264264
boolean isEntryPoint = type.nativeABI() && !type.outgoing;
265265

266266
/*
267-
* We have to reserve a slot between return address and outgoing parameters for the deopt
268-
* frame handle. Exception: calls to native methods.
267+
* We have to reserve a slot between return address and outgoing parameters for the
268+
* deoptimized frame (eager deoptimization), or the original return address (lazy
269+
* deoptimization). Exception: calls to native methods.
269270
*/
270271
int currentStackOffset = type.nativeABI() ? nativeParamsStackOffset : target.wordSize;
271272

substratevm/src/com.oracle.svm.core.graal.riscv64/src/com/oracle/svm/core/graal/riscv64/SubstrateRISCV64RegisterConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,9 @@ public CallingConvention getCallingConvention(Type t, JavaType returnType, JavaT
230230
int currentFP = 0;
231231

232232
/*
233-
* We have to reserve a slot between return address and outgoing parameters for the deopt
234-
* frame handle. Exception: calls to native methods.
233+
* We have to reserve a slot between return address and outgoing parameters for the
234+
* deoptimized frame (eager deoptimization), or the original return address (lazy
235+
* deoptimization). Exception: calls to native methods.
235236
*/
236237
int currentStackOffset = (type.nativeABI() ? nativeParamsStackOffset : target.wordSize);
237238

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateDiagnostics.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,12 @@ public int maxInvocationCount() {
695695
@Override
696696
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate while printing diagnostics.")
697697
public void printDiagnostics(Log log, ErrorContext context, int maxDiagnosticLevel, int invocationCount) {
698-
log.string("DeoptStubPointer address: ").zhex(DeoptimizationSupport.getDeoptStubPointer()).newline().newline();
698+
log.string("EagerDeoptStub address: ").zhex(DeoptimizationSupport.getEagerDeoptStubPointer()).newline();
699+
if (Deoptimizer.Options.LazyDeoptimization.getValue()) {
700+
log.string("LazyDeoptStubPrimitiveReturn address: ").zhex(DeoptimizationSupport.getLazyDeoptStubPrimitiveReturnPointer()).newline();
701+
log.string("LazyDeoptStubObjectReturn address: ").zhex(DeoptimizationSupport.getLazyDeoptStubObjectReturnPointer()).newline();
702+
}
703+
log.newline();
699704
}
700705
}
701706

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package com.oracle.svm.core.jvmti.headers;
25+
package com.oracle.svm.core.c;
2626

2727
import org.graalvm.nativeimage.c.struct.CPointerTo;
2828
import org.graalvm.word.PointerBase;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfo.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,36 +66,37 @@ public interface CodeInfo extends UntetheredCodeInfo {
6666
/**
6767
* This state is only a temporary state when the VM is at a safepoint. It indicates that no
6868
* activations are remaining and that the code is no longer needed (code is non-entrant) or no
69-
* longer wanted (code has references to otherwise unreachable objects). The GC will invalidate
70-
* and free this {@link CodeInfo} object during the current safepoint. It is crucial that the GC
71-
* still visits all heap references that may be accessed while invalidating and freeing the
72-
* {@link CodeInfo} object (i.e., all object fields).
69+
* longer wanted (code has references to otherwise unreachable objects). The GC will remove this
70+
* {@link CodeInfo} object from the code cache and free it during the current safepoint. It is
71+
* crucial that the GC still visits all heap references that may be accessed while removing and
72+
* freeing the {@link CodeInfo} object (i.e., all object fields).
7373
*/
7474
@DuplicatedInNativeCode //
75-
int STATE_READY_FOR_INVALIDATION = STATE_NON_ENTRANT + 1;
75+
int STATE_PENDING_REMOVAL_FROM_CODE_CACHE = STATE_NON_ENTRANT + 1;
7676

7777
/**
78-
* Indicates that this {@link CodeInfo} object was invalidated. The data will be freed by the GC
79-
* once the tether object becomes unreachable. Until then, the GC must continue visiting all
80-
* heap references, including code constants that are directly embedded into the machine code.
78+
* Indicates that this {@link CodeInfo} object was removed from the code cache. The data will be
79+
* freed by the GC once the tether object becomes unreachable. Until then, the GC must continue
80+
* visiting all heap references, including code constants that are directly embedded into the
81+
* machine code.
8182
*/
8283
@DuplicatedInNativeCode //
83-
int STATE_INVALIDATED = STATE_READY_FOR_INVALIDATION + 1;
84+
int STATE_REMOVED_FROM_CODE_CACHE = STATE_PENDING_REMOVAL_FROM_CODE_CACHE + 1;
8485

8586
/**
8687
* This state is only a temporary state when the VM is at a safepoint. It indicates that a
87-
* previously invalidated {@link CodeInfo} object is no longer reachable from the GC point of
88-
* view. The GC will free the {@link CodeInfo} object during the current safepoint. It is
89-
* crucial that the GC still visits all heap references that may be accessed while freeing the
90-
* {@link CodeInfo} object (i.e., all object fields).
88+
* {@link CodeInfo} object which has already been removed from the code cache is no longer
89+
* reachable from the GC point of view. The GC will free the {@link CodeInfo} object during the
90+
* current safepoint. It is crucial that the GC still visits all heap references that may be
91+
* accessed while freeing the {@link CodeInfo} object (i.e., all object fields).
9192
*/
9293
@DuplicatedInNativeCode //
93-
int STATE_UNREACHABLE = STATE_INVALIDATED + 1;
94+
int STATE_PENDING_FREE = STATE_REMOVED_FROM_CODE_CACHE + 1;
9495

9596
/**
9697
* Indicates that the {@link CodeInfo} object was already freed. This state should never be
9798
* seen.
9899
*/
99100
@DuplicatedInNativeCode //
100-
int STATE_FREED = STATE_UNREACHABLE + 1;
101+
int STATE_FREED = STATE_PENDING_FREE + 1;
101102
}

0 commit comments

Comments
 (0)