Skip to content

JIT: enable loop cloning for MD arrays#129455

Draft
AndyAyersMS wants to merge 1 commit into
dotnet:mainfrom
AndyAyersMS:loop-clone-md-arr-phase-squashed
Draft

JIT: enable loop cloning for MD arrays#129455
AndyAyersMS wants to merge 1 commit into
dotnet:mainfrom
AndyAyersMS:loop-clone-md-arr-phase-squashed

Conversation

@AndyAyersMS

Copy link
Copy Markdown
Member

Move MD-array expansion before loop cloning so the optimizer sees per-dim length and lower-bound nodes.

Recognize MD-array length and GetUpperBound-derived loop limits, then strip matching post-morph per-dim bounds checks in the fast clone.

Add regression coverage for direct loops, non-unit strides, decreasing loops, and foreach over MD arrays.

Move MD-array expansion before loop cloning so the optimizer sees
per-dim length and lower-bound nodes.

Recognize MD-array length and GetUpperBound-derived loop limits, then
strip matching post-morph per-dim bounds checks in the fast clone.

Add regression coverage for direct loops, non-unit strides, decreasing
loops, and foreach over MD arrays.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 16, 2026 02:13
@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 Jun 16, 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 cloning to recognize multi-dimensional (rectangular) array per-dimension length/upper-bound loop limits, enabling fast-path clones that can strip matching post-morph per-dim bounds checks. It also adds a new JIT test suite intended to cover common MD-array loop shapes.

Changes:

  • Emit canonical GT_MDARR_LENGTH / GT_MDARR_LOWER_BOUND nodes for Array.GetLength/GetLowerBound/GetUpperBound intrinsics, and run MD-array morphing earlier so loop cloning sees per-dim forms.
  • Teach loop iteration analysis + loop cloning condition derivation to recognize MD-array per-dim length limits and GetUpperBound-derived bounded-range patterns.
  • Add new MdArrayCloning JIT tests for direct loops, strides, decreasing loops, and foreach-like patterns.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/tests/JIT/opt/Cloning/MdArrayCloning.csproj Adds a new JIT test project for MD-array loop cloning scenarios.
src/tests/JIT/opt/Cloning/MdArrayCloning.cs Adds xunit coverage for MD-array loop patterns and exception behavior.
src/coreclr/jit/loopcloning.h Refactors MD-array loop cloning opt-info and extends symbolic array model with md-rank storage.
src/coreclr/jit/loopcloning.cpp Implements MD-array length symbolic materialization, per-dim bounds-check extraction, and fast-clone bounds-check stripping.
src/coreclr/jit/importercalls.cpp Switches MD-array intrinsics to emit typed MD-array nodes and sets OMF_HAS_MDARRAYREF.
src/coreclr/jit/flowgraph.cpp Adds recognition of MD-array length limits and GetUpperBound-derived bounded-range loop limits.
src/coreclr/jit/compiler.h Adds iteration-analysis state for MD-array length limits and bounded-range loop recognition.
src/coreclr/jit/compiler.cpp Moves MD-array morphing earlier so post-morph optimizations (including loop cloning) see lowered per-dim forms.

Comment on lines +1635 to 1639
LC_Array arrLen(LC_Array::MdArray, mdIndex, (int)mdArrInfo->dim, LC_Array::ArrLen);
arrLen.mdRank = mdArrInfo->rank;
LC_Ident arrLenIdent = LC_Ident::CreateArrAccess(arrLen);
LC_Condition cond(opLimitCondition, LC_Expr(ident), LC_Expr(arrLenIdent));
context->EnsureConditions(loop->GetIndex())->Push(cond);
Comment on lines +1635 to 1639
LC_Array arrLen(LC_Array::MdArray, mdIndex, (int)mdArrInfo->dim, LC_Array::ArrLen);
arrLen.mdRank = mdArrInfo->rank;
LC_Ident arrLenIdent = LC_Ident::CreateArrAccess(arrLen);
LC_Condition cond(opLimitCondition, LC_Expr(ident), LC_Expr(arrLenIdent));
context->EnsureConditions(loop->GetIndex())->Push(cond);
Comment on lines +516 to +538
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
static int Sum2DBounded(Array a)
{
// Iterate dim 0 from 0 to GetLength(0)-1, accessing a[i, 0]. Note:
// for a bounded MD array, valid indices are
// [GetLowerBound(d), GetUpperBound(d)], NOT [0, GetLength(d)).
// The bounds checks (after morph) subtract the lower bound, so
// accessing at `i` with i < GetLength(d) (when lower bound != 0)
// throws IOORE for the first iteration if lower bound > 0.
int sum = 0;
for (int i = 0; i < a.GetLength(0); i++)
sum += (int)a.GetValue(i, 0)!;
return sum;
}

[Fact]
public static void Bounded_NonZeroLowerBound_Throws()
{
var a = Array.CreateInstance(typeof(int), new int[] { 4, 4 }, new int[] { 10, 10 });
// Indexing at 0 below the [10, 10] lower bound must throw, whether or
// not cloning fires.
Assert.Throws<IndexOutOfRangeException>(() => Sum2DBounded(a));
}
Comment on lines +1627 to +1634
// Build a synthetic ArrIndex referring to the MD array local. The
// MdArray LC_Array path materializes `MDARR_LENGTH(arr, dim, rank)`
// and only needs the arrLcl + dim + rank — indLcls are unused.
ArrIndex* mdIndex = new (this, CMK_LoopClone) ArrIndex(getAllocator(CMK_LoopClone));
mdIndex->arrLcl = mdArrInfo->arrLcl;
mdIndex->arrType = TYP_REF;
mdIndex->rank = mdArrInfo->rank;

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