Skip to content

JIT: Canonicalize loop backedges to a single latch block#128303

Open
AndyAyersMS wants to merge 2 commits into
dotnet:mainfrom
AndyAyersMS:loop-backedge-canonicalization
Open

JIT: Canonicalize loop backedges to a single latch block#128303
AndyAyersMS wants to merge 2 commits into
dotnet:mainfrom
AndyAyersMS:loop-backedge-canonicalization

Conversation

@AndyAyersMS
Copy link
Copy Markdown
Member

Adds optCanonicalizeBackedges, run during optCanonicalizeLoops between preheader creation and exit canonicalization. For any natural loop with more than one backedge, create a new block that branches to the header and redirect every backedge source to the block.

Adds optCanonicalizeBackedges, run during optCanonicalizeLoops between
preheader creation and exit canonicalization. For any natural loop with
more than one backedge, this creates a new latch block that branches to
the header and redirects every backedge source to the latch. The result
is a canonical single-backedge loop shape that simplifies downstream
loop optimizations.

The latch is created with fgNewBBinRegion(BBJ_ALWAYS, header) so it
inherits the header's try/handler region. We bail out if any backedge
source is in a different EH region than the header, since a source that
can legally branch to the header (e.g. into the entry of a try region)
cannot necessarily branch to a non-entry block inside that region.

SPMI replay across 558k methods: 0 failures. SPMI asmdiffs: net code
size roughly neutral (-11 KB across ~1 GB), with PerfScore improving in
8 of 11 collections by 2.8% to 28%. Top size regressions are paired
with large PerfScore wins (e.g. ReportExportedTypeNameCollisions:
+310 bytes for -44% PerfScore), driven by downstream passes (RA,
hoisting, IV opts) exploiting the canonical loop shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 17, 2026 18:52
@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 17, 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.

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 extends CoreCLR JIT loop canonicalization by introducing a new step that merges multiple natural-loop backedges into a single “latch” block that unconditionally branches to the loop header, enabling downstream optimizations to rely on a single canonical backedge shape.

Changes:

  • Invoke a new backedge-canonicalization step during Compiler::optCanonicalizeLoops() after preheader creation and before exit canonicalization.
  • Implement Compiler::optCanonicalizeBackedges(FlowGraphNaturalLoop*) to create a latch block and redirect all backedge sources through it (with EH-region legality checks).
  • Add the new helper’s declaration to compiler.h.

Reviewed changes

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

File Description
src/coreclr/jit/optimizer.cpp Adds the new canonicalization phase and implements latch creation + backedge redirection logic.
src/coreclr/jit/compiler.h Declares optCanonicalizeBackedges on Compiler.

Comment thread src/coreclr/jit/optimizer.cpp Outdated
@jakobbotsch
Copy link
Copy Markdown
Member

This would close #107189. Note there is an implementation of this canonicalization on that issue too, but that one had large regressions.

@AndyAyersMS
Copy link
Copy Markdown
Member Author

diffs seem quite reasonable.

This is a bit less ambitious than https://github.com/jakobbotsch/runtime/pull/new/canonicalize-backedges as it doesn't try and handle the case where the back edges come from enclosing regions. I can try extending it to that case.

I was hoping perhaps for a bit more code reduction as this should simplify LSRA's need to put resolution moves in multiple places and also gives it a natural well-placed block to hold them (that is, back edges are quite often critical edges, coming from a conditional exit block to the join at the header).

@jakobbotsch
Copy link
Copy Markdown
Member

Yes, perhaps we have fixed something else since I tried it. I recall this broke loop cloning when I tried, but it doesn't seem like it from the metric diffs in this PR.

@jakobbotsch
Copy link
Copy Markdown
Member

I was hoping perhaps for a bit more code reduction as this should simplify LSRA's need to put resolution moves in multiple places and also gives it a natural well-placed block to hold them (that is, back edges are quite often critical edges, coming from a conditional exit block to the join at the header).

It seems likely that we are removing these blocks. Perhaps what has changed since I tried is that we do that sooner.

Mirror optCreatePreheader's region selection: when any backedge source is
outside the header's try region, place the latch in the header's true
enclosing try region (only legal because the header must be a try entry
in that case). Replaces the previous all-or-nothing sameEHRegion bail.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AndyAyersMS
Copy link
Copy Markdown
Member Author

it doesn't try and handle the case where the back edges come from enclosing regions. I can try extending it to that case.

Did this locally and it looks good too, so updated the changes.

@AndyAyersMS
Copy link
Copy Markdown
Member Author

Updated diffs

@AndyAyersMS AndyAyersMS marked this pull request as ready for review May 18, 2026 22:36
Copilot AI review requested due to automatic review settings May 18, 2026 22:36
@AndyAyersMS
Copy link
Copy Markdown
Member Author

@jakobbotsch PTAL
fyi @dotnet/jit-contrib

One last bit of loop canonicalization...

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 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/optimizer.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.

3 participants