JIT: Add an IV opt that replaces primary IVs with enregisterable ones #129305
JIT: Add an IV opt that replaces primary IVs with enregisterable ones #129305jakobbotsch wants to merge 12 commits into
Conversation
This optimization detects when a primary IV will not be enregistered based on DNER or due to being EH live, and if possible, creates a new primary IV to split its lifetime to make it enregisterable.
| // We must be able to insert the store-back of the final value into every | ||
| // regular exit where the IV is live. Blocks that are part of a call-finally | ||
| // pair must remain empty, so we cannot sink into them. | ||
| BasicBlockVisit exitResult = loop->VisitRegularExitBlocks([=](BasicBlock* exit) { | ||
| if (optLocalIsLiveIntoBlock(lclNum, exit) && (exit->isBBCallFinallyPair() || exit->isBBCallFinallyPairTail())) | ||
| { | ||
| return BasicBlockVisit::Abort; | ||
| } | ||
|
|
||
| return BasicBlockVisit::Continue; | ||
| }); |
There was a problem hiding this comment.
We should change exit canonicalization to make sure there don't exist as exits, but I think it can be a follow up.
There was a problem hiding this comment.
Pull request overview
This PR extends CoreCLR JIT induction-variable optimizations by adding a late pass that can replace primary IV locals that won’t be enregistered (DNER or EH live-in/out-of-handler) with a fresh temp whose lifetime is constrained to the loop body, and adds a regression test covering try/finally and try/catch interaction.
Changes:
- Add a new IV replacement pass in
optInductionVariables()plus helper legality/liveness checks and an in-loop local replacement walker. - Add a new JIT metadata metric (
EnregisterableIVsCreated) to track how many replacements were performed. - Add a new JIT regression test (
PrimaryIVLiveInHandler) validating correctness across finally paths, throwing loops, and catch-resume paths.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/inductionvariableopts.cpp | Implements the new unenregisterable-primary-IV replacement optimization and hooks it into the IV opts phase. |
| src/coreclr/jit/compiler.h | Declares the new Compiler::opt* helpers used by the replacement pass. |
| src/coreclr/jit/jitmetadatalist.h | Adds a new metadata metric to report how many enregisterable IV replacements were created. |
| src/tests/JIT/opt/Loops/PrimaryIVLiveInHandler.cs | Adds regression coverage for IV replacement correctness with try/finally and try/catch shapes. |
| src/tests/JIT/opt/Loops/PrimaryIVLiveInHandler.csproj | Adds the project file to build the new JIT test. |
| // We must be able to insert the store-back of the final value into every | ||
| // regular exit where the IV is live. Blocks that are part of a call-finally | ||
| // pair must remain empty, so we cannot sink into them. | ||
| BasicBlockVisit exitResult = loop->VisitRegularExitBlocks([=](BasicBlock* exit) { | ||
| if (optLocalIsLiveIntoBlock(lclNum, exit) && (exit->isBBCallFinallyPair() || exit->isBBCallFinallyPairTail())) | ||
| { | ||
| return BasicBlockVisit::Abort; | ||
| } | ||
|
|
||
| return BasicBlockVisit::Continue; | ||
| }); | ||
|
|
||
| if (exitResult == BasicBlockVisit::Abort) | ||
| { | ||
| JITDUMP(" Cannot replace V%02u; a live regular exit is part of a call-finally pair\n", lclNum); | ||
| return false; | ||
| } |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
SuperPMI collections show:
Might be worth taking. |
…ther IV opts Run the enregisterable primary IV replacement before strength reduction and widening, and construct the new IV in proper SSA form via IncrementalSsaBuilder so those optimizations can apply to it. Delete the old IV's header phi so the scalar evolution analysis does not look at the now-storeless old IV. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Occurrences are collected from non-phi statements only, so the header phi is never visited during the rename. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Forward declare IncrementalSsaBuilder and UseDefLocation in compiler.h. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror the check in optCanSinkWidenedIV: if a regular exit the IV is live into has a predecessor from outside the loop, the store-back of the final value would incorrectly overwrite the original local on a non-loop path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| <Project Sdk="Microsoft.NET.Sdk"> | ||
| <PropertyGroup> | ||
| <DebugType>PdbOnly</DebugType> | ||
| <Optimize>True</Optimize> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <Compile Include="$(MSBuildProjectName).cs" /> | ||
| </ItemGroup> | ||
| </Project> |
Avoid carrying over stale data (SSA numbers, value numbers) from the old local's nodes by allocating new store/use nodes for the new local. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| if (!optCanReplaceUnenregisterablePrimaryIV(lclNum, loop)) | ||
| { | ||
| return false; | ||
| } |
This optimization detects when a primary IV will not be enregistered based on DNER or due to being EH live, and if possible, creates a new primary IV to split its lifetime to make it enregisterable.
Fixes #124285