JIT: enable loop cloning for MD arrays#129455
Draft
AndyAyersMS wants to merge 1 commit into
Draft
Conversation
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>
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Contributor
There was a problem hiding this comment.
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_BOUNDnodes forArray.GetLength/GetLowerBound/GetUpperBoundintrinsics, 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
MdArrayCloningJIT 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; | ||
|
|
Open
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.