Skip to content

JIT: Decouple "DoNotEnregister" from liveness#127932

Draft
jakobbotsch wants to merge 7 commits intodotnet:mainfrom
jakobbotsch:decouple-liveness-dner
Draft

JIT: Decouple "DoNotEnregister" from liveness#127932
jakobbotsch wants to merge 7 commits intodotnet:mainfrom
jakobbotsch:decouple-liveness-dner

Conversation

@jakobbotsch
Copy link
Copy Markdown
Member

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 lvaAsyncContinuationArg unnecessarily 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:

  • Resets lvLiveInOutOfHandler on all locals every time we run liveness
  • Moves all the DNER setting from liveness to LSRA and to lowering
  • Encapsulates lvLiveInOutOfHandler into LclVarDsc::IsLiveInOutOfHandler() and adds an assert(lvTracked) to this function, so it cannot be misused
  • Introduces a few updates to heuristics to use IsLiveInOutOfHandler() in addition to DNER

Diffs aren't all that great, but I think this is a good clean up on its own.

Copilot AI review requested due to automatic review settings May 7, 2026 19:15
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 7, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Comment on lines 10710 to 10712
case DoNotEnregisterReason::PinningRef:
m_PinningRef++;
break;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This is under #ifdef DEBUG, so a bit scary to be mutating things...)

compiler->lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler));
return false;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was moved into checkForDNER.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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::IsEHVarARegCandidate to 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

  • optRemovedEHClausesAfterLiveness is currently only set/reset (in fgRemoveEHTableEntry and liveness) but not consumed anywhere else. If it’s meant to guard heuristics that rely on IsLiveInOutOfHandler() (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)

Comment thread src/coreclr/jit/lclvars.cpp
Comment thread src/coreclr/jit/liveness.cpp Outdated
Copilot AI review requested due to automatic review settings May 7, 2026 19:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Comment thread src/coreclr/jit/lower.cpp
Comment on lines +8929 to +8934
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();
}
Comment on lines 10707 to 10712
m_longParamField++;
break;
#endif
#ifdef JIT32_GCENCODER
case DoNotEnregisterReason::PinningRef:
m_PinningRef++;
break;
Comment thread src/coreclr/jit/liveness.cpp Outdated
}

// 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
Copilot AI review requested due to automatic review settings May 8, 2026 08:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/gschecks.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants