JIT: Treat the indicator variable as transition-preserved - #131731
Conversation
In OSR functions we recomputed this from the async continuation argument (which itself is transition-preserved). That approach is not sufficient for EnC. Instead just preserve the state across EnC/OSR transitions in the same way as other state.
|
Azure Pipelines: Successfully started running 6 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR updates async OSR/EnC handling so the async “resumed?” indicator local is treated like other transition-preserved state: it gets a stable stack slot in the preserved area and its tier0-frame offset is communicated via PatchpointInfo for OSR compilations.
Changes:
- Adds
PatchpointInfosupport for a resumed-indicator stack offset and plumbs it through OSR patchpoint generation and OSR local offset resolution. - Allocates
lvaResumedIndicatoras part of the async preserved-area locals and includes it in EnC preserved-area size verification across xarch/arm. - Simplifies OSR async context setup by removing the OSR-specific recomputation of the indicator from the continuation argument.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/lclvars.cpp | Ensures the resumed-indicator local is stack-allocated as part of async preserved-area locals and excluded from later offset assignment passes. |
| src/coreclr/jit/compiler.h | Moves lvaResumedIndicator alongside other async preserved locals in the Compiler state. |
| src/coreclr/jit/compiler.cpp | Records and consumes the resumed-indicator tier0-frame offset via PatchpointInfo for OSR local remapping. |
| src/coreclr/jit/codegenxarch.cpp | Accounts for resumed-indicator in EnC preserved-area sizing assertions on x64. |
| src/coreclr/jit/codegenarmarch.cpp | Accounts for resumed-indicator in EnC preserved-area sizing assertions on arm64 (GC info encoder path). |
| src/coreclr/jit/codegenarm64.cpp | Includes resumed-indicator stack home in funclet prolog/epilog size accounting (non-OSR). |
| src/coreclr/jit/codegenarm.cpp | Includes resumed-indicator in funclet prolog/epilog size accounting (arm32). |
| src/coreclr/jit/async.cpp | Creates the resumed-indicator local as pointer-sized and removes OSR-only recomputation from continuation arg. |
| src/coreclr/inc/patchpointinfo.h | Adds a new resumed-indicator offset field + accessors to the shared OSR patchpoint info. |
Suppressed comments (1)
src/coreclr/jit/async.cpp:380
- Same issue as in
SaveAsyncContexts:lvaResumedIndicatorhaslvType = TYP_I_IMPL, so usinggtNewLclVarNode(lvaResumedIndicator, TYP_INT)will trip thegtNewLclvNodetype assert in DEBUG builds. Load asTYP_I_IMPLand cast toTYP_INTfor therestoreContextscall argument.
restoreCall->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewLclVarNode(lvaResumedIndicator, TYP_INT)));
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/coreclr/jit/async.cpp:78
lvaResumedIndicatoris now declared asTYP_I_IMPL, but several downstream parts of the async transformation still treat it as a byte/boolean flag. In particular,AsyncTransformation::StoreResumedDefonly does a full-width store when the local isTYP_UBYTE; withTYP_I_IMPLit falls back to aTYP_UBYTEfield store (byte write) into a pointer-sized local. That leaves the upper bytes undefined and makes the value representation dependent on prior zero-init, which is brittle for OSR/EnC transitions and any futureTYP_I_IMPLuses.
Either keep the local as TYP_UBYTE and explicitly force a pointer-sized stack home for the transition-preserved slot, or update the async transformation (and related address-def sizing logic) to store/load the indicator consistently as native-int (e.g., store 1 as TYP_I_IMPL when the local is TYP_I_IMPL).
lvaGetDesc(lvaResumedIndicator)->lvType = TYP_I_IMPL;
lvaGetDesc(lvaAsyncThreadObjectVar)->lvType = TYP_REF;
lvaGetDesc(lvaAsyncExecutionContextVar)->lvType = TYP_REF;
lvaGetDesc(lvaAsyncSynchronizationContextVar)->lvType = TYP_REF;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
src/coreclr/jit/lclvars.cpp:5864
- This comment says we "allocate 8 bytes" for the resumed indicator, but the actual allocation uses
TARGET_POINTER_SIZE(4 bytes on x86, 8 bytes on 64-bit). The comment should describe the pointer-sized intent to avoid confusion when reading x86 EnC frame layout changes.
// The indicator is actually a bool, but we allocate 8 bytes for it to
// simplify EnC remapping.
stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaResumedIndicator, TARGET_POINTER_SIZE, stkOffs);
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/coreclr/inc/readytoruninstructionset.h:106
- This introduces an otherwise unrelated whitespace-only change (extra blank line) in the enum; please remove it to keep the PR focused and avoid diff noise.
READYTORUN_INSTRUCTION_Zicond=94,
};
src/coreclr/jit/lclvars.cpp:5863
- The lvaAllocAsyncContexts header comment is now stale: the routine also allocates the resumed-indicator slot. Please update the comment so it matches the current behavior.
if (lvaResumedIndicator != BAD_VAR_NUM)
{
stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaResumedIndicator, lvaLclStackHomeSize(lvaResumedIndicator), stkOffs);
}
src/coreclr/jit/codegenxarch.cpp:8382
- The EnC preserved-area comment immediately above is now incomplete: the code also preserves the async resumed-indicator slot. Please update the comment to match the frame header layout.
if (m_compiler->lvaResumedIndicator != BAD_VAR_NUM)
{
preservedAreaSize += TARGET_POINTER_SIZE;
assert(m_compiler->lvaGetCallerSPRelativeOffset(m_compiler->lvaResumedIndicator) == -preservedAreaSize);
}
src/coreclr/jit/codegenarmarch.cpp:3936
- The EnC preserved-area comment immediately above is now incomplete: the code also preserves the async resumed-indicator slot. Please update the comment to match the frame header layout.
if (m_compiler->lvaResumedIndicator != BAD_VAR_NUM)
{
preservedAreaSize += TARGET_POINTER_SIZE;
assert(m_compiler->lvaGetCallerSPRelativeOffset(m_compiler->lvaResumedIndicator) == -preservedAreaSize);
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/coreclr/inc/readytoruninstructionset.h:106
- Remove the extra blank line at the end of the enum; it looks like accidental whitespace-only churn unrelated to the JIT/async change and can cause avoidable merge conflicts.
READYTORUN_INSTRUCTION_Cssc=93,
READYTORUN_INSTRUCTION_Zicond=94,
};
src/coreclr/jit/lclvars.cpp:5864
- The header comment for lvaAllocAsyncContexts is now out of sync with the implementation: it also allocates the resumed indicator slot. Updating the comment will help keep the frame-layout invariants clear (especially around EnC).
if (lvaResumedIndicator != BAD_VAR_NUM)
{
stkOffs =
lvaAllocLocalAndSetVirtualOffset(lvaResumedIndicator, lvaLclStackHomeSize(lvaResumedIndicator), stkOffs);
}
|
cc @dotnet/jit-contrib PTAL @AndyAyersMS |
|
Azure Pipelines: Successfully started running 6 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
In OSR functions we recomputed this from the async continuation argument (which itself is transition-preserved). That approach is not sufficient for EnC. Instead just preserve the state across EnC/OSR transitions in the same way as other state.
To simplify the remapping/local allocation logic this also changes the indicator to consistently be a
TYP_I_IMPLlocal, with all definitions of it writing that size. The arguments passed to the helper calls remainTYP_INTsince they are bools in the managed signatures. We useTYP_INTtyped local nodes for the indicator variable for these (allowed in JIT IR and used byoptNarrowTree/IV widening also).