JIT: Decouple "DoNotEnregister" from liveness#127932
JIT: Decouple "DoNotEnregister" from liveness#127932jakobbotsch wants to merge 7 commits intodotnet:mainfrom
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
| case DoNotEnregisterReason::PinningRef: | ||
| m_PinningRef++; | ||
| break; |
There was a problem hiding this comment.
JIT32 GC encoder does not support enregistering pinned locals, while the other GC encoder does. However, we never track pinned locals and hence they are never register candidates anyway. To simplify things I removed the ifdef and started always marking these DNER.
| break; | ||
| case DoNotEnregisterReason::LiveInOutOfHandler: | ||
| JITDUMP("live in/out of a handler\n"); | ||
| varDsc->lvLiveInOutOfHndlr = 1; |
There was a problem hiding this comment.
(This is under #ifdef DEBUG, so a bit scary to be mutating things...)
| compiler->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler)); | ||
| return false; | ||
| } | ||
|
|
There was a problem hiding this comment.
This was moved into checkForDNER.
There was a problem hiding this comment.
Pull request overview
This PR refactors CoreCLR JIT local liveness/EH handling so that EH “live-in/out-of-handler” state is recomputed each liveness run and lvDoNotEnregister (DNER) is no longer set by liveness, instead being set by regalloc / lowering.
Changes:
- Recompute and encapsulate EH live-in/out state via
LclVarDsc::IsLiveInOutOfHandler()and reset EH/must-init flags each liveness run. - Move DNER-setting decisions out of liveness into regalloc (
checkForDNER) and lowering (lvSetEHVarsDoNotEnreg), and update heuristics to consult EH-liveness in addition to DNER. - Add
Compiler::IsEHVarARegCandidateto centralize “EH write-thru candidate” logic and use it in multiple optimizations.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/sideeffects.h | Use IsLiveInOutOfHandler() for EH-related aliasing decisions. |
| src/coreclr/jit/regallocwasm.h | Declare checkForDNER for Wasm regalloc. |
| src/coreclr/jit/regallocwasm.cpp | Call checkForDNER and use encapsulated EH-live query. |
| src/coreclr/jit/regalloc.cpp | Add checkForDNER and remove DNER-setting from isRegCandidate. |
| src/coreclr/jit/optimizer.cpp | Switch EH-live checks to IsLiveInOutOfHandler(). |
| src/coreclr/jit/morph.cpp | Stop copying EH-live flag during implicit-byref arg retyping. |
| src/coreclr/jit/lsra.h | Declare checkForDNER for LSRA. |
| src/coreclr/jit/lsra.cpp | Call checkForDNER and use IsLiveInOutOfHandler() in LSRA logic. |
| src/coreclr/jit/lower.cpp | Add lvSetEHVarsDoNotEnreg() call to support containment heuristics in lowering. |
| src/coreclr/jit/liveness.cpp | Reset lvMustInit and EH-live state each liveness run; set EH-live without setting DNER. |
| src/coreclr/jit/lclvars.cpp | Add lvSetEHVarsDoNotEnreg; remove lvaSetVarLiveInOutOfHandler; adjust DNER behavior. |
| src/coreclr/jit/jiteh.cpp | Track when EH clauses are removed post-liveness. |
| src/coreclr/jit/inductionvariableopts.cpp | Update IV widening heuristics to consider EH-liveness independently of DNER. |
| src/coreclr/jit/gschecks.cpp | Remove direct clearing of EH-live flag on shadowing locals. |
| src/coreclr/jit/gentree.cpp | Add Compiler::IsEHVarARegCandidate helper; update EH-live checks. |
| src/coreclr/jit/earlyprop.cpp | Update EH-live checks to IsLiveInOutOfHandler(). |
| src/coreclr/jit/copyprop.cpp | Adjust “profitability” gating to compare expected enregistration status incl. EH-liveness. |
| src/coreclr/jit/compiler.h | Encapsulate EH-live flag, add helper APIs, and add optRemovedEHClausesAfterLiveness. |
| src/coreclr/jit/compiler.cpp | Update enregister stats accounting to include PinningRef unconditionally. |
| src/coreclr/jit/codegenwasm.cpp | Update EH-live checks to IsLiveInOutOfHandler(). |
| src/coreclr/jit/codegencommon.cpp | Update EH-live checks to IsLiveInOutOfHandler(). |
Comments suppressed due to low confidence (1)
src/coreclr/jit/compiler.h:5734
optRemovedEHClausesAfterLivenessis currently only set/reset (in fgRemoveEHTableEntry and liveness) but not consumed anywhere else. If it’s meant to guard heuristics that rely onIsLiveInOutOfHandler()(e.g., avoid setting DNER based on stale EH liveness after EH removal), consider wiring it into the relevant decision points; otherwise it should be removed to avoid accumulating dead state.
bool fgBBVarSetsInited = false;
// Track how many artificial ref counts we've added to fgEntryBB (for OSR)
| else | ||
| { | ||
| // By this point tracked locals live into EH clauses are _very_ likely to not be enregistered. | ||
| // DNER this for the same reason above, so that we can contain these locals. | ||
| m_compiler->lvSetEHVarsDoNotEnreg(); | ||
| } |
| m_longParamField++; | ||
| break; | ||
| #endif | ||
| #ifdef JIT32_GCENCODER | ||
| case DoNotEnregisterReason::PinningRef: | ||
| m_PinningRef++; | ||
| break; |
| } | ||
|
|
||
| // Fields of dependently promoted structs may be tracked. We shouldn't set lvMustInit on them since | ||
| // the whole parent struct will be initialized; however, lvLiveInOutOfHndlr should be set on them |
Liveness was marking locals as DNER in various places, including when it found locals to be live into EH handlers.
DNER is sticky and cannot be unset, and we call liveness multiple times, so that made things hard to reason about and led to unnecessary DNER.
This showed up for runtime async functions. Even when we were able to optimize out the try-fault clauses inserted around the body we would often still end up with
lvaAsyncContinuationArgunnecessarily DNER'd because we had already marked it as DNER during a previous liveness pass, when it was live into the fault handler.This PR:
lvLiveInOutOfHandleron all locals every time we run livenesslvLiveInOutOfHandlerintoLclVarDsc::IsLiveInOutOfHandler()and adds anassert(lvTracked)to this function, so it cannot be misusedIsLiveInOutOfHandler()in addition toDNERDiffs aren't all that great, but I think this is a good clean up on its own.