Skip to content

Commit cae78ae

Browse files
committed
JIT: handle case where we are cloning adjacent sibling loops
We can sometimes see adjacent sibling loops (where L1.bottom == L2.head) and if so, cloning L1 will repurpose L1.bottom and so leave L2 in an inconsistent state. Detect this case during optCanonicalizeLoop, and add an intermediary block to serve as L2's head. Fixes #70569.
1 parent e3a2097 commit cae78ae

File tree

1 file changed

+82
-1
lines changed

1 file changed

+82
-1
lines changed

src/coreclr/jit/optimizer.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2678,6 +2678,9 @@ void Compiler::optFindNaturalLoops()
26782678

26792679
// Make sure that loops are canonical: that every loop has a unique "top", by creating an empty "nop"
26802680
// one, if necessary, for loops containing others that share a "top."
2681+
//
2682+
// Also make sure that no loop's "bottom" is another loop's "head".
2683+
//
26812684
for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++)
26822685
{
26832686
// Traverse the outermost loops as entries into the loop nest; so skip non-outermost.
@@ -2923,7 +2926,7 @@ bool Compiler::optCanonicalizeLoopNest(unsigned char loopInd)
29232926

29242927
//-----------------------------------------------------------------------------
29252928
// optCanonicalizeLoop: ensure that each loop top's back edges come only from
2926-
// blocks in the same loop.
2929+
// blocks in the same loop, and that no loop head/bottom blocks coincide.
29272930
//
29282931
// Arguments:
29292932
// loopInd - index of the loop to consider
@@ -3006,6 +3009,84 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd)
30063009
modified |= didCanon;
30073010
}
30083011

3012+
// Check if this loopInd head is also the bottom of some sibling.
3013+
// If so, add a block in between to serve as the new head.
3014+
//
3015+
auto repairLoop = [this](unsigned char loopInd, unsigned char sibling) {
3016+
3017+
BasicBlock* const h = optLoopTable[loopInd].lpHead;
3018+
BasicBlock* const siblingB = optLoopTable[sibling].lpBottom;
3019+
3020+
if (h == siblingB)
3021+
{
3022+
// We have
3023+
//
3024+
// sibling.B (== loopInd.H) -e-> loopInd.T
3025+
//
3026+
// where e is a "critical edge", that is
3027+
// * sibling.B has other successors (notably sibling.T),
3028+
// * loopInd.T has other predecessors (notably loopInd.B)
3029+
//
3030+
// turn this into
3031+
//
3032+
// sibling.B -> newH (== loopInd.H) -> loopInd.T
3033+
//
3034+
// Ideally we'd just call fgSplitEdge, but we are
3035+
// not keeping pred lists in good shape.
3036+
//
3037+
BasicBlock* const t = optLoopTable[loopInd].lpTop;
3038+
assert(siblingB->bbJumpKind == BBJ_COND);
3039+
assert(siblingB->bbNext == t);
3040+
3041+
JITDUMP(FMT_LP " head " FMT_BB " is also " FMT_LP " bottom\n", loopInd, h->bbNum, sibling);
3042+
3043+
BasicBlock* const newH = fgNewBBbefore(BBJ_NONE, t, /*extendRegion*/ true);
3044+
3045+
// Anything that flows into sibling will flow here.
3046+
// So we use sibling.H as our best guess for weight.
3047+
//
3048+
newH->inheritWeight(optLoopTable[sibling].lpHead);
3049+
newH->bbNatLoopNum = optLoopTable[loopInd].lpParent;
3050+
optUpdateLoopHead(loopInd, h, newH);
3051+
3052+
return true;
3053+
}
3054+
return false;
3055+
};
3056+
3057+
if (optLoopTable[loopInd].lpParent == BasicBlock::NOT_IN_LOOP)
3058+
{
3059+
// check against all other top-level loops
3060+
//
3061+
for (unsigned char sibling = 0; sibling < optLoopCount; sibling++)
3062+
{
3063+
if (optLoopTable[sibling].lpParent != BasicBlock::NOT_IN_LOOP)
3064+
{
3065+
continue;
3066+
}
3067+
3068+
modified |= repairLoop(loopInd, sibling);
3069+
}
3070+
}
3071+
else
3072+
{
3073+
// check against all other sibling loops
3074+
//
3075+
const unsigned char parentLoop = optLoopTable[loopInd].lpParent;
3076+
3077+
for (unsigned char sibling = optLoopTable[parentLoop].lpChild; //
3078+
sibling != BasicBlock::NOT_IN_LOOP; //
3079+
sibling = optLoopTable[sibling].lpSibling)
3080+
{
3081+
if (sibling == loopInd)
3082+
{
3083+
continue;
3084+
}
3085+
3086+
modified |= repairLoop(loopInd, sibling);
3087+
}
3088+
}
3089+
30093090
if (modified)
30103091
{
30113092
JITDUMP("Done canonicalizing " FMT_LP "\n\n", loopInd);

0 commit comments

Comments
 (0)