Skip to content

Commit 6714e8b

Browse files
committed
Update documentation.
1 parent 7d4a87c commit 6714e8b

File tree

3 files changed

+102
-117
lines changed

3 files changed

+102
-117
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,11 @@ public void enter(CompilationResultBuilder crb) {
10561056
/* Create the frame. */
10571057
super.enter(crb);
10581058

1059+
/*
1060+
* Synthesize the parameters for the deopt stub. This needs to be done after enter() to
1061+
* avoid overwriting register values that it might save to the stack.
1062+
*/
1063+
10591064
/* Pass the general purpose and floating point registers to the deopt stub. */
10601065
Register secondParameter = ValueUtil.asRegister(callingConvention.getArgument(1));
10611066
masm.mov(64, secondParameter, gpReturnReg);

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,12 +1364,17 @@ public void enter(CompilationResultBuilder tasm) {
13641364
* Keep the return address slot. The correct return address is written in the stub
13651365
* itself (read more there). The original return address is stored in the deopt slot.
13661366
*
1367-
* Keeping the return address also ensures that the stack pointer is aligned properly.
1367+
* Keeping this slot also ensures that the stack pointer is aligned properly.
13681368
*/
13691369
asm.subq(registerConfig.getFrameRegister(), FrameAccess.returnAddressSize());
13701370

13711371
super.enter(tasm);
13721372

1373+
/*
1374+
* Synthesize the parameters for the deopt stub. This needs to be done after enter() to
1375+
* avoid overwriting register values that it might save to the stack.
1376+
*/
1377+
13731378
/* Pass the address of the frame to deoptimize as first argument. */
13741379
asm.leaq(firstArgument, new AMD64Address(frameRegister, tasm.frameMap.totalFrameSize()));
13751380

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

Lines changed: 91 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
import jdk.graal.compiler.api.replacements.Fold;
8989
import jdk.graal.compiler.core.common.NumUtil;
9090
import jdk.graal.compiler.core.common.util.TypeConversion;
91+
import jdk.graal.compiler.lir.asm.FrameContext;
9192
import jdk.graal.compiler.nodes.UnreachableNode;
9293
import jdk.graal.compiler.options.Option;
9394
import jdk.graal.compiler.word.BarrieredAccess;
@@ -101,106 +102,100 @@
101102

102103
/**
103104
* Performs deoptimization. The method to deoptimize (= the source method) is either a specialized
104-
* runtime compiled method or an image compiled test method with the {@link Specialize} annotation.
105-
* The target method is always an image compiled method.
105+
* runtime compiled method or an AOT-compiled test method with the {@link Specialize} annotation.
106+
* The target method is always an AOT-compiled method.
106107
* <p>
107-
* Deoptimization is not limited to a single method. It can be done for all deoptimizable methods in
108+
* Deoptimization is not limited to a single method. It can be done for all deoptimizable methods on
108109
* the call stack. A method is deoptimizable if {@link FrameInfoQueryResult deoptimization
109110
* information} is available.
110111
* <p>
111112
* Deoptimization can happen eagerly or lazily. For eager deoptimization, a {@link DeoptimizedFrame}
112-
* is constructed immediately and pinned, whereas for lazy deoptimization, the
113-
* {@link DeoptimizedFrame} is not constructed until immediately before it is installed (and
114-
* therefore does not need to be pinned).
113+
* is constructed immediately and pinned and only installed on the stack later, whereas for lazy
114+
* deoptimization, the {@link DeoptimizedFrame} is not constructed until immediately before it is
115+
* installed. Eager deoptimization enables immediately freeing the code metadata needed for
116+
* constructing the {@link DeoptimizedFrame}, while lazy deoptimization requires keeping it, but
117+
* does not need the {@link DeoptimizedFrame} to stay allocated and pinned for potentially lengthy
118+
* periods of time, which is typically preferable especially when many frames might be deoptimized
119+
* at once. The stack slot at SP[0] of a deoptimizable method is reserved for deoptimization, and
120+
* can be used freely by lazy and eager deoptimization.
115121
* <p>
116-
* The stack slot at SP[0] is reserved for deoptimization, and can be used freely by lazy and eager
117-
* deoptimization.
122+
* With both eager and lazy deoptimization, returns to a deoptimized method are intercepted to enter
123+
* the deopt stub. Alternatively, {@link ExceptionUnwind} can "far-return" directly to the deopt
124+
* stub for dispatching an exception while unwinding the stack, in which case the exception object
125+
* will be passed as if it was the return value from the callee.
118126
* <p>
119-
* Eager Deoptimization is done in two steps:
127+
* Eager deoptimization is done in two steps:
120128
* <ol>
121-
* <li>A call to {@link #deoptimizeInRange} walks the stack and for each method to deoptimize it
122-
* builds a {@link DeoptimizedFrame}. This handle contains all constants and materialized objects
123-
* which are needed to build the deoptimization target frames. It is stored at SP[0] (directly above
124-
* the return address). The return address (to the deoptimized method) is replaced by a pointer to
125-
* {@link #eagerDeoptStub}.
126-
*
127-
* <pre>
128-
* top of stack (lowest address)
129-
*
130-
* | ... |
131-
* +---------------------------------------+-------------
132-
* | | frame of the
133-
* |---------------------------------------| callee of
134-
* | return address (points to deoptStub) | deopt method
135-
* +---------------------------------------+-------------
136-
* | pointer to DeoptimizedFrame | frame of the
137-
* |---------------------------------------| deopt method
138-
* | outgoing stack parameters |
139-
* |---------------------------------------|
140-
* | |
141-
* +---------------------------------------+-------------
142-
* | ... |
143-
* </pre>
129+
* <li>A call to {@link #deoptimizeInRange} walks the stack, and for each method to deoptimize, it
130+
* builds a {@link DeoptimizedFrame}. This object contains all constants and materialized objects
131+
* which are needed to build the deoptimization target frames. A reference to it is stored at SP[0]
132+
* (directly above the callee's return address). The callee's return address (to the deoptimized
133+
* method) is replaced by {@link #eagerDeoptStub}. (Find a diagram of the stack further below.)
144134
* <p>
145135
* From now on, the frame of the deoptimized method is no longer valid and the GC will ignore it.
146-
* Instead the GC will also visit the pointer to the {@link DeoptimizedFrame}. In other words: the
147-
* frame of the deoptimized method is "replaced" by a single entry, a pointer to
136+
* Instead, the GC will also visit the reference to the {@link DeoptimizedFrame}. In other words:
137+
* the frame of the deoptimized method is "replaced" by a single entry, a reference to
148138
* {@link DeoptimizedFrame}, which contains all objects which are needed by the deoptimization
149139
* targets.
150140
* <p>
151141
* There is one exception: outgoing primitive parameters of a deoptimized method may still be
152-
* accessed by a called method, even after the first step of eager deoptimization is done. Note that
153-
* this does not apply to outgoing object parameters as those are always copied to registers at the
142+
* accessed by the callee, even after the first step of eager deoptimization is done. Note that this
143+
* does not apply to outgoing object parameters as those are always copied to registers at the
154144
* beginning of the called method to avoid problems with the GC.</li>
155-
* <p>
156-
* <li>Now when a called method will return to a deoptimized method, the eager deopt stub will be
157-
* called instead. It reads the {@link DeoptimizedFrame} handle and replaces the deoptimized
158-
* method's frame with the frame(s) of the deopt target method(s). Note that the eager deopt stub is
145+
* <li>Now when a callee would return to a deoptimized method, the eager deopt stub will be entered
146+
* instead. It reads the {@link DeoptimizedFrame} reference and replaces the deoptimized method's
147+
* frame with the frame(s) of the deopt target method(s). Note that the eager deopt stub is
159148
* completely allocation free.</li>
160149
* </ol>
161150
*
162151
* <p>
163-
* Lazy Deoptimization is also done in two steps:
152+
* Lazy deoptimization is also done in two steps:
164153
* <ol>
165-
* <li>During the first step, we patch the frame's return address to the return address of a lazy
166-
* deopt stub. Depending on whether the method being deoptimized returns an object or a primitive,
167-
* this return address either points to {@link #lazyDeoptStubObjectReturn} or
168-
* {@link #lazyDeoptStubPrimitiveReturn}. The stack slot that is used to store the
169-
* {@link DeoptimizedFrame} in eager deoptimization is instead used to store the original return
170-
* address, which points somewhere into the deopt source method.
171-
*
172-
* <pre>
173-
* top of stack (lowest address)
174-
*
175-
* | ... |
176-
* +-------------------------------------------+-------------
177-
* | | frame of the
178-
* |-------------------------------------------| callee of
179-
* | return address (points to lazyDeoptStub) | deopt method
180-
* +-------------------------------------------+-------------
181-
* | original return address | frame of the
182-
* |-------------------------------------------| deopt method
183-
* | outgoing stack parameters |
184-
* |-------------------------------------------|
185-
* | |
186-
* +-------------------------------------------+-------------
187-
* | ... |
188-
* </pre>
189-
*
190-
* Stack walks and GC will now visit this frame that is pending lazy deoptimization as if it was a
191-
* normal stack frame, with the only difference being that the original return address is stored in
192-
* a different slot.</li>
193-
* <li>
154+
* <li>During the first step, we only patch the callee's return address to point to a lazy deopt
155+
* stub. Depending on whether the method being deoptimized returns an object or a primitive value,
156+
* that stub is either {@link #lazyDeoptStubObjectReturn} or {@link #lazyDeoptStubPrimitiveReturn}.
157+
* The reserved stack slot that is used to store the {@link DeoptimizedFrame} in eager
158+
* deoptimization is instead used to store the original return address, which points somewhere into
159+
* the deopt source method.
194160
* <p>
195-
* When a method returns to this method pending lazy deoptimization, it instead calls one of the
196-
* lazy deopt stubs, which leads to {@link #lazyDeoptStubCore}. This method performs all the
161+
* Stack walks and GC will now visit this frame that is pending lazy deoptimization as if it was a
162+
* normal stack frame, with the only difference being that the original return address is taken from
163+
* the reserved stack slot.</li>
164+
* <li>When a callee returns to the method pending lazy deoptimization, it instead enters one of the
165+
* lazy deopt stubs, which in turn invokes {@link #lazyDeoptStubCore}. This method performs all the
197166
* necessary operations to construct a {@link DeoptimizedFrame} just like the first step of eager
198167
* deoptimization. The process of constructing the frame is interruptible and involves allocation,
199-
* therefore if {@code gpReturnValue} contains an object reference, it must be turned into an object
200-
* reference so that the GC is aware of said reference.
168+
* therefore if {@code gpReturnValue} contains an object reference, it must be explicitly turned
169+
* into an object reference so that the GC is aware of said reference.
201170
* <p>
202171
* The frame is then copied onto the stack in {@link #rewriteStackStub}.</li>
203172
* </ol>
173+
* The stack at the time of entering a deopt stub looks as follows. Because while returning, the
174+
* stack pointer has already moved above potential callee-saved arguments and the return address,
175+
* they must be considered stale because they could have been overwritten by an interrupt or signal
176+
* handler (despite a safe zone guaranteed by the ABI which could not be large enough).
177+
*
178+
* <pre>
179+
* top of stack (lowest address)
180+
*
181+
* : ... :
182+
* +---------------------------------------+-------------
183+
* | ... |
184+
* |---------------------------------------| frame of the callee of deopt'ed method,
185+
* | potential callee-saved arguments | stale on deopt stub entry
186+
* |---------------------------------------|
187+
* | return address (to deopt stub) |
188+
* +---------------------------------------+------------- <== stack pointer on deopt stub entry
189+
* | deopt reserved slot |
190+
* | eager deopt: DeoptimizedFrame object |
191+
* | lazy deopt: original return address |
192+
* |---------------------------------------| frame of the deopt'ed method
193+
* | outgoing stack parameters |
194+
* |---------------------------------------|
195+
* | ... |
196+
* +---------------------------------------+-------------
197+
* : ... :
198+
* </pre>
204199
*/
205200
public final class Deoptimizer {
206201
private static final int MAX_DEOPTIMIZATION_EVENT_PRINT_LENGTH = 1000;
@@ -285,8 +280,8 @@ public static class Options {
285280
}
286281

287282
/**
288-
* If true, the GC is called during deoptimization. The deoptimizer allocates some objects (in
289-
* the first step), so GC must work inside the deoptimizer.
283+
* If true, the GC is called during deoptimization. The deoptimizer allocates some objects,
284+
* which requires GC to work. This is only set to true for testing.
290285
*/
291286
public static boolean testGCinDeoptimizer = false;
292287

@@ -665,7 +660,7 @@ private static void deoptimize0(IsolateThread targetThread, Pointer sp, CodePoin
665660

666661
/**
667662
* Invalidates the {@link InstalledCode} of the method of the given frame. The method must be a
668-
* runtime compiled method, since there is no {@link InstalledCode} for native image methods.
663+
* runtime compiled method, since there is no {@link InstalledCode} for AOT-compiled methods.
669664
*/
670665
public static void invalidateMethodOfFrame(IsolateThread thread, Pointer sp, SpeculationReason speculation) {
671666
VMError.guarantee(thread == CurrentIsolate.getCurrentThread());
@@ -806,7 +801,9 @@ private static boolean isNonNullValue(UnsignedWord pointer) {
806801
}
807802

808803
/**
809-
* Entry point for the lazy deopt stub.
804+
* Entry point for the lazy deopt stub. The parameters are computed with instructions generated
805+
* in this method's prologue by a backend-specific {@link FrameContext}. See the class-level
806+
* documentation for more context and a diagram of the stack.
810807
* <p>
811808
* This method uses {@link StubCallingConvention} for when the callee (the return of which is
812809
* intercepted) also uses stub calling convention. In that case, the callee (rather than the
@@ -823,6 +820,16 @@ private static boolean isNonNullValue(UnsignedWord pointer) {
823820
* If the callee does not use stub calling convention, this method unnecessarily saves
824821
* registers, but it avoids having additional stubs and selecting between them and should not
825822
* have significant impact.
823+
* <p>
824+
*
825+
* @param originalStackPointer the original stack pointer of the deoptimized method (points to
826+
* the {@link DeoptimizedFrame} object).
827+
* @param gpReturnValue This is the value which was stored in the general purpose return
828+
* register when the deopt stub was reached. It must be restored to the register
829+
* before completion of the stub.
830+
* @param fpReturnValue This is the value which was stored in the floating point return register
831+
* when the deopt stub was reached. It must be restored to the register before
832+
* completion of the stub.
826833
*/
827834
@StubCallingConvention
828835
@DeoptStub(stubType = StubType.EntryStub)
@@ -863,7 +870,7 @@ public static UnsignedWord lazyDeoptStubObjectReturn(Pointer originalStackPointe
863870
@DeoptStub(stubType = StubType.EntryStub)
864871
@Uninterruptible(reason = "Rewriting stack.")
865872
public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
866-
/* Establish the correct return address for this stub (see ObjectReturn stub for details) */
873+
/* Establish the correct return address for this stub to make the stack walkable. */
867874
CodePointer returnAddress = DeoptimizationSupport.getLazyDeoptStubPrimitiveReturnPointer();
868875
FrameAccess.singleton().writeReturnAddress(CurrentIsolate.getCurrentThread(), originalStackPointer, returnAddress);
869876

@@ -980,39 +987,10 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
980987
}
981988

982989
/**
983-
* Performs the second step of deoptimization: the actual rewriting of a deoptimized method's
984-
* frame.
985-
* <p>
986-
* The pointer to the deopt stub code was installed in the return address slot by
987-
* {@link #deoptimizeInRange}. Therefore the stub is "called" when a method wants to return to a
988-
* deoptimized method.
989-
* <p>
990-
* When {@link #eagerDeoptStub} is "called", the stack looks like this:
991-
*
992-
* <pre>
993-
* : : highest stack address
994-
* | |
995-
* | | frame of the
996-
* +--------------------------------+ deoptimized method
997-
* | pointer to DeoptimizedFrame |
998-
* +--------------------------------+--------- no return address between the frames!
999-
* | |
1000-
* | | frame of
1001-
* | | {@link #eagerDeoptStub}
1002-
* : ... :
1003-
* </pre>
1004-
*
1005-
* The instructions to compute the parameters must be generated in this method's prologue by a
1006-
* backend-specific FrameContext class.
1007-
*
1008-
* @param originalStackPointer the original stack pointer of the deoptimized method (points to
1009-
* the {@link DeoptimizedFrame} object).
1010-
* @param gpReturnValue This is the value which was stored in the general purpose return
1011-
* register when the deopt stub was reached. It must be restored to the register
1012-
* before completion of the stub.
1013-
* @param fpReturnValue This is the value which was stored in the floating point return register
1014-
* when the deopt stub was reached. It must be restored to the register before
1015-
* completion of the stub.
990+
* See {@link #lazyDeoptStubObjectReturn} for context, but note that here, the deoptimized frame
991+
* has already been prepared and only needs to be written to the stack. For the same reason,
992+
* this stub is fully uninterruptible because no allocations are needed, and does not use
993+
* {@link StubCallingConvention}, because access to any callee-saved registers is not required.
1016994
*/
1017995
@DeoptStub(stubType = StubType.EntryStub)
1018996
@Uninterruptible(reason = "Frame holds Objects in unmanaged storage.")
@@ -1136,9 +1114,6 @@ private void deoptSourceFrameLazily(CodePointer pc, boolean ignoreNonDeoptimizab
11361114
installLazyDeoptStubReturnAddress(targetInfo.getDeoptReturnValueIsObject(), deoptState.sourceSp, deoptState.targetThread);
11371115
}
11381116

1139-
/**
1140-
* Deoptimizes a source frame eagerly.
1141-
*/
11421117
private DeoptimizedFrame deoptSourceFrameEagerly(CodePointer pc, boolean ignoreNonDeoptimizable) {
11431118
if (!canBeDeoptimized(sourceChunk.getFrameInfo())) {
11441119
if (ignoreNonDeoptimizable) {
@@ -1375,7 +1350,7 @@ public static void logRecentDeoptimizationEvents(Log log) {
13751350
}
13761351

13771352
/**
1378-
* Constructs the frame entries for the deopimization target method.
1353+
* Constructs the frame entries for the deoptimization target method.
13791354
*
13801355
* @param targetInfo The bytecode frame (+ some other info) of the target.
13811356
* @param sourceFrame The bytecode frame of the source.

0 commit comments

Comments
 (0)