Skip to content

Commit 1e8b750

Browse files
authored
JIT: Factor loop duplication code (#97506)
Factor the loop duplication code out of loop cloning and loop unrolling in anticipation of also using it in loop peeling.
1 parent c105e7d commit 1e8b750

File tree

5 files changed

+314
-356
lines changed

5 files changed

+314
-356
lines changed

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,6 +2188,9 @@ class FlowGraphNaturalLoop
21882188

21892189
bool HasDef(unsigned lclNum);
21902190

2191+
bool CanDuplicate(INDEBUG(const char** reason));
2192+
void Duplicate(BasicBlock** insertAfter, BlockToBlockMap* map, weight_t weightScale, bool bottomNeedsRedirection);
2193+
21912194
#ifdef DEBUG
21922195
static void Dump(FlowGraphNaturalLoop* loop);
21932196
#endif // DEBUG
@@ -6785,6 +6788,7 @@ class Compiler
67856788
void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context);
67866789
PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info)
67876790
bool optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR);
6791+
void optRedirectPrevUnrollIteration(FlowGraphNaturalLoop* loop, BasicBlock* prevTestBlock, BasicBlock* target);
67886792
void optReplaceScalarUsesWithConst(BasicBlock* block, unsigned lclNum, ssize_t cnsVal);
67896793
void optRemoveRedundantZeroInits();
67906794
PhaseStatus optIfConversion(); // If conversion

src/coreclr/jit/compiler.hpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4893,27 +4893,40 @@ BasicBlockVisit FlowGraphNaturalLoop::VisitLoopBlocks(TFunc func)
48934893
template <typename TFunc>
48944894
BasicBlockVisit FlowGraphNaturalLoop::VisitLoopBlocksLexical(TFunc func)
48954895
{
4896-
BasicBlock* top = m_header;
4897-
BasicBlock* bottom = m_header;
4896+
BasicBlock* top = m_header;
4897+
unsigned numLoopBlocks = 0;
48984898
VisitLoopBlocks([&](BasicBlock* block) {
48994899
if (block->bbNum < top->bbNum)
4900+
{
49004901
top = block;
4901-
if (block->bbNum > bottom->bbNum)
4902-
bottom = block;
4902+
}
4903+
4904+
numLoopBlocks++;
49034905
return BasicBlockVisit::Continue;
49044906
});
49054907

4906-
BasicBlock* block = top;
4907-
while (true)
4908+
INDEBUG(BasicBlock* prev = nullptr);
4909+
BasicBlock* cur = top;
4910+
while (numLoopBlocks > 0)
49084911
{
4909-
if (ContainsBlock(block) && (func(block) == BasicBlockVisit::Abort))
4910-
return BasicBlockVisit::Abort;
4912+
// If we run out of blocks the blocks aren't sequential.
4913+
assert(cur != nullptr);
49114914

4912-
if (block == bottom)
4913-
return BasicBlockVisit::Continue;
4915+
if (ContainsBlock(cur))
4916+
{
4917+
assert((prev == nullptr) || (prev->bbNum < cur->bbNum));
49144918

4915-
block = block->Next();
4919+
if (func(cur) == BasicBlockVisit::Abort)
4920+
return BasicBlockVisit::Abort;
4921+
4922+
INDEBUG(prev = cur);
4923+
numLoopBlocks--;
4924+
}
4925+
4926+
cur = cur->Next();
49164927
}
4928+
4929+
return BasicBlockVisit::Continue;
49174930
}
49184931

49194932
/*****************************************************************************/

src/coreclr/jit/flowgraph.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5409,6 +5409,176 @@ bool FlowGraphNaturalLoop::HasDef(unsigned lclNum)
54095409
return !result;
54105410
}
54115411

5412+
//------------------------------------------------------------------------
5413+
// CanDuplicate: Check if this loop can be duplicated.
5414+
//
5415+
// Parameters:
5416+
// reason - If this function returns false, the reason why.
5417+
//
5418+
// Returns:
5419+
// True if the loop can be duplicated.
5420+
//
5421+
// Remarks:
5422+
// We currently do not support duplicating loops with EH constructs in them.
5423+
//
5424+
bool FlowGraphNaturalLoop::CanDuplicate(INDEBUG(const char** reason))
5425+
{
5426+
#ifdef DEBUG
5427+
const char* localReason;
5428+
if (reason == nullptr)
5429+
{
5430+
reason = &localReason;
5431+
}
5432+
#endif
5433+
5434+
Compiler* comp = m_dfsTree->GetCompiler();
5435+
BasicBlockVisit result = VisitLoopBlocks([=](BasicBlock* block) {
5436+
if (comp->bbIsTryBeg(block))
5437+
{
5438+
INDEBUG(*reason = "Loop has a `try` begin");
5439+
return BasicBlockVisit::Abort;
5440+
}
5441+
5442+
return BasicBlockVisit::Continue;
5443+
});
5444+
5445+
return result != BasicBlockVisit::Abort;
5446+
}
5447+
5448+
//------------------------------------------------------------------------
5449+
// Duplicate: Duplicate the blocks of this loop, inserting them after `insertAfter`.
5450+
//
5451+
// Parameters:
5452+
// insertAfter - [in, out] Block to insert duplicated blocks after; updated to last block inserted.
5453+
// map - A map that will have mappings from loop blocks to duplicated blocks added to it.
5454+
// weightScale - Factor to scale weight of new blocks by
5455+
// bottomNeedsRedirection - Whether or not to insert a redirection block for the bottom block in case of fallthrough
5456+
//
5457+
// Remarks:
5458+
// Due to fallthrough this block may need to insert blocks with no
5459+
// corresponding source block in "map".
5460+
//
5461+
void FlowGraphNaturalLoop::Duplicate(BasicBlock** insertAfter,
5462+
BlockToBlockMap* map,
5463+
weight_t weightScale,
5464+
bool bottomNeedsRedirection)
5465+
{
5466+
assert(CanDuplicate(nullptr));
5467+
5468+
Compiler* comp = m_dfsTree->GetCompiler();
5469+
5470+
BasicBlock* bottom = GetLexicallyBottomMostBlock();
5471+
5472+
VisitLoopBlocksLexical([=](BasicBlock* blk) {
5473+
// Initialize newBlk as BBJ_ALWAYS without jump target, and fix up jump target later
5474+
// with BasicBlock::CopyTarget().
5475+
BasicBlock* newBlk = comp->fgNewBBafter(BBJ_ALWAYS, *insertAfter, /*extendRegion*/ true);
5476+
JITDUMP("Adding " FMT_BB " (copy of " FMT_BB ") after " FMT_BB "\n", newBlk->bbNum, blk->bbNum,
5477+
(*insertAfter)->bbNum);
5478+
5479+
BasicBlock::CloneBlockState(comp, newBlk, blk);
5480+
5481+
// We're going to create the preds below, which will set the bbRefs properly,
5482+
// so clear out the cloned bbRefs field.
5483+
newBlk->bbRefs = 0;
5484+
5485+
newBlk->scaleBBWeight(weightScale);
5486+
5487+
// If the loop we're cloning contains nested loops, we need to clear the pre-header bit on
5488+
// any nested loop pre-header blocks, since they will no longer be loop pre-headers.
5489+
//
5490+
// TODO-Cleanup: BBF_LOOP_PREHEADER can be removed; we do not attempt
5491+
// to keep it up to date anymore when we do FG changes.
5492+
//
5493+
if (newBlk->HasFlag(BBF_LOOP_PREHEADER))
5494+
{
5495+
JITDUMP("Removing BBF_LOOP_PREHEADER flag from nested cloned loop block " FMT_BB "\n", newBlk->bbNum);
5496+
newBlk->RemoveFlags(BBF_LOOP_PREHEADER);
5497+
}
5498+
5499+
*insertAfter = newBlk;
5500+
map->Set(blk, newBlk, BlockToBlockMap::Overwrite);
5501+
5502+
// If the block falls through to a block outside the loop then we may
5503+
// need to insert a new block to redirect.
5504+
// Skip this once we get to the bottom block if our cloned version is
5505+
// going to fall into the right version anyway.
5506+
if (blk->bbFallsThrough() && !ContainsBlock(blk->Next()) && ((blk != bottom) || bottomNeedsRedirection))
5507+
{
5508+
if (blk->KindIs(BBJ_COND))
5509+
{
5510+
BasicBlock* targetBlk = blk->GetFalseTarget();
5511+
assert(blk->NextIs(targetBlk));
5512+
5513+
// Need to insert a block.
5514+
BasicBlock* newRedirBlk =
5515+
comp->fgNewBBafter(BBJ_ALWAYS, *insertAfter, /* extendRegion */ true, targetBlk);
5516+
newRedirBlk->copyEHRegion(*insertAfter);
5517+
newRedirBlk->bbWeight = blk->Next()->bbWeight;
5518+
newRedirBlk->CopyFlags(blk->Next(), (BBF_RUN_RARELY | BBF_PROF_WEIGHT));
5519+
newRedirBlk->scaleBBWeight(weightScale);
5520+
5521+
JITDUMP(FMT_BB " falls through to " FMT_BB "; inserted redirection block " FMT_BB "\n", blk->bbNum,
5522+
blk->Next()->bbNum, newRedirBlk->bbNum);
5523+
// This block isn't part of the loop, so below loop won't add
5524+
// refs for it.
5525+
comp->fgAddRefPred(targetBlk, newRedirBlk);
5526+
*insertAfter = newRedirBlk;
5527+
}
5528+
else
5529+
{
5530+
assert(!"Cannot handle fallthrough");
5531+
}
5532+
}
5533+
5534+
return BasicBlockVisit::Continue;
5535+
});
5536+
5537+
// Now go through the new blocks, remapping their jump targets within the loop
5538+
// and updating the preds lists.
5539+
VisitLoopBlocks([=](BasicBlock* blk) {
5540+
BasicBlock* newBlk = nullptr;
5541+
bool b = map->Lookup(blk, &newBlk);
5542+
assert(b && newBlk != nullptr);
5543+
5544+
// Jump target should not be set yet
5545+
assert(!newBlk->HasInitializedTarget());
5546+
5547+
// First copy the jump destination(s) from "blk".
5548+
newBlk->CopyTarget(comp, blk);
5549+
5550+
// Now redirect the new block according to "blockMap".
5551+
comp->optRedirectBlock(newBlk, map);
5552+
5553+
// Add predecessor edges for the new successors, as well as the fall-through paths.
5554+
switch (newBlk->GetKind())
5555+
{
5556+
case BBJ_ALWAYS:
5557+
case BBJ_CALLFINALLY:
5558+
case BBJ_CALLFINALLYRET:
5559+
comp->fgAddRefPred(newBlk->GetTarget(), newBlk);
5560+
break;
5561+
5562+
case BBJ_COND:
5563+
comp->fgAddRefPred(newBlk->GetFalseTarget(), newBlk);
5564+
comp->fgAddRefPred(newBlk->GetTrueTarget(), newBlk);
5565+
break;
5566+
5567+
case BBJ_SWITCH:
5568+
for (BasicBlock* const switchDest : newBlk->SwitchTargets())
5569+
{
5570+
comp->fgAddRefPred(switchDest, newBlk);
5571+
}
5572+
break;
5573+
5574+
default:
5575+
break;
5576+
}
5577+
5578+
return BasicBlockVisit::Continue;
5579+
});
5580+
}
5581+
54125582
//------------------------------------------------------------------------
54135583
// IterConst: Get the constant with which the iterator is modified
54145584
//

0 commit comments

Comments
 (0)