Skip to content

Commit f9da3db

Browse files
authored
JIT: add OSR patchpoint strategy, inhibit tail duplication (#66208)
Two changes for OSR: * add new strategies for placing patchpoints -- either at backedge sources (instead of targets) or adaptive. depending on number of backedges. Change default to adaptive, since this works better with the flow we see from C# `for` loops. * inhibit tail duplication for OSR as it may end up interfering with loop recognition. We may not be able to place patchpoints at sources, for various reasons; if so we fall back to placing them at targets. We also can't place patchpoints unless block entries are also stack empty ponts. This means forgoing patchpoints in some IL cases..
1 parent 6fce82c commit f9da3db

File tree

7 files changed

+170
-11
lines changed

7 files changed

+170
-11
lines changed

src/coreclr/jit/block.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,10 @@ void BasicBlock::dspFlags()
464464
{
465465
printf("bwd-target ");
466466
}
467+
if (bbFlags & BBF_BACKWARD_JUMP_SOURCE)
468+
{
469+
printf("bwd-src ");
470+
}
467471
if (bbFlags & BBF_PATCHPOINT)
468472
{
469473
printf("ppoint ");

src/coreclr/jit/block.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,8 @@ enum BasicBlockFlags : unsigned __int64
551551
BBF_HAS_ALIGN = MAKE_BBFLAG(39), // BB ends with 'align' instruction
552552
BBF_TAILCALL_SUCCESSOR = MAKE_BBFLAG(40), // BB has pred that has potential tail call
553553

554+
BBF_BACKWARD_JUMP_SOURCE = MAKE_BBFLAG(41), // Block is a source of a backward jump
555+
554556
// The following are sets of flags.
555557

556558
// Flags that relate blocks to loop structure.

src/coreclr/jit/fgbasic.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,7 @@ void Compiler::fgMarkBackwardJump(BasicBlock* targetBlock, BasicBlock* sourceBlo
23852385
}
23862386
}
23872387

2388+
sourceBlock->bbFlags |= BBF_BACKWARD_JUMP_SOURCE;
23882389
targetBlock->bbFlags |= BBF_BACKWARD_JUMP_TARGET;
23892390
}
23902391

src/coreclr/jit/fgopt.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,6 +3477,31 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock*
34773477
return false;
34783478
}
34793479

3480+
// At this point we know target is BBJ_COND.
3481+
//
3482+
// Bail out if OSR, as we can have unusual flow into loops. If one
3483+
// of target's successors is also a backedge target, this optimization
3484+
// may mess up loop recognition by creating too many non-loop preds.
3485+
//
3486+
if (opts.IsOSR())
3487+
{
3488+
assert(target->bbJumpKind == BBJ_COND);
3489+
3490+
if ((target->bbNext->bbFlags & BBF_BACKWARD_JUMP_TARGET) != 0)
3491+
{
3492+
JITDUMP("Deferring: " FMT_BB " --> " FMT_BB "; latter looks like loop top\n", target->bbNum,
3493+
target->bbNext->bbNum);
3494+
return false;
3495+
}
3496+
3497+
if ((target->bbJumpDest->bbFlags & BBF_BACKWARD_JUMP_TARGET) != 0)
3498+
{
3499+
JITDUMP("Deferring: " FMT_BB " --> " FMT_BB "; latter looks like loop top\n", target->bbNum,
3500+
target->bbJumpDest->bbNum);
3501+
return false;
3502+
}
3503+
}
3504+
34803505
// See if this block assigns constant or other interesting tree to that same local.
34813506
//
34823507
if (!fgBlockEndFavorsTailDuplication(block, lclNum))

src/coreclr/jit/importer.cpp

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11873,35 +11873,151 @@ void Compiler::impImportBlockCode(BasicBlock* block)
1187311873

1187411874
// OSR is not yet supported for methods with explicit tail calls.
1187511875
//
11876-
// But we also do not have to switch these methods to be optimized as we should be
11876+
// But we also do not have to switch these methods to be optimized, as we should be
1187711877
// able to avoid getting trapped in Tier0 code by normal call counting.
1187811878
// So instead, just suppress adding patchpoints.
1187911879
//
1188011880
if (!compTailPrefixSeen)
1188111881
{
11882-
// The normaly policy is only to add patchpoints to the targets of lexically
11883-
// backwards branches.
11882+
// We only need to add patchpoints if the method can loop.
1188411883
//
1188511884
if (compHasBackwardJump)
1188611885
{
1188711886
assert(compCanHavePatchpoints());
1188811887

11889-
// Is the start of this block a suitable patchpoint?
11888+
// By default we use the "adaptive" strategy.
1189011889
//
11891-
if (((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) != 0) && (verCurrentState.esStackDepth == 0))
11890+
// This can create both source and target patchpoints within a given
11891+
// loop structure, which isn't ideal, but is not incorrect. We will
11892+
// just have some extra Tier0 overhead.
11893+
//
11894+
// Todo: implement support for mid-block patchpoints. If `block`
11895+
// is truly a backedge source (and not in a handler) then we should be
11896+
// able to find a stack empty point somewhere in the block.
11897+
//
11898+
const int patchpointStrategy = JitConfig.TC_PatchpointStrategy();
11899+
bool addPatchpoint = false;
11900+
bool mustUseTargetPatchpoint = false;
11901+
11902+
switch (patchpointStrategy)
1189211903
{
11893-
// We should have noted this earlier and bailed out of OSR.
11894-
//
11895-
assert(!block->hasHndIndex());
11904+
default:
11905+
{
11906+
// Patchpoints at backedge sources, if possible, otherwise targets.
11907+
//
11908+
addPatchpoint = ((block->bbFlags & BBF_BACKWARD_JUMP_SOURCE) == BBF_BACKWARD_JUMP_SOURCE);
11909+
mustUseTargetPatchpoint = (verCurrentState.esStackDepth != 0) || block->hasHndIndex();
11910+
break;
11911+
}
11912+
11913+
case 1:
11914+
{
11915+
// Patchpoints at stackempty backedge targets.
11916+
// Note if we have loops where the IL stack is not empty on the backedge we can't patchpoint
11917+
// them.
11918+
//
11919+
// We should not have allowed OSR if there were backedges in handlers.
11920+
//
11921+
assert(!block->hasHndIndex());
11922+
addPatchpoint = ((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == BBF_BACKWARD_JUMP_TARGET) &&
11923+
(verCurrentState.esStackDepth == 0);
11924+
break;
11925+
}
11926+
11927+
case 2:
11928+
{
11929+
// Adaptive strategy.
11930+
//
11931+
// Patchpoints at backedge targets if there are multiple backedges,
11932+
// otherwise at backedge sources, if possible. Note a block can be both; if so we
11933+
// just need one patchpoint.
11934+
//
11935+
if ((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == BBF_BACKWARD_JUMP_TARGET)
11936+
{
11937+
// We don't know backedge count, so just use ref count.
11938+
//
11939+
addPatchpoint = (block->bbRefs > 1) && (verCurrentState.esStackDepth == 0);
11940+
}
11941+
11942+
if (!addPatchpoint && ((block->bbFlags & BBF_BACKWARD_JUMP_SOURCE) == BBF_BACKWARD_JUMP_SOURCE))
11943+
{
11944+
addPatchpoint = true;
11945+
mustUseTargetPatchpoint = (verCurrentState.esStackDepth != 0) || block->hasHndIndex();
11946+
11947+
// Also force target patchpoint if target block has multiple (backedge) preds.
11948+
//
11949+
if (!mustUseTargetPatchpoint)
11950+
{
11951+
for (BasicBlock* const succBlock : block->Succs(this))
11952+
{
11953+
if ((succBlock->bbNum <= block->bbNum) && (succBlock->bbRefs > 1))
11954+
{
11955+
mustUseTargetPatchpoint = true;
11956+
break;
11957+
}
11958+
}
11959+
}
11960+
}
11961+
break;
11962+
}
11963+
}
11964+
11965+
if (addPatchpoint)
11966+
{
11967+
if (mustUseTargetPatchpoint)
11968+
{
11969+
// We wanted a source patchpoint, but could not have one.
11970+
// So, add patchpoints to the backedge targets.
11971+
//
11972+
for (BasicBlock* const succBlock : block->Succs(this))
11973+
{
11974+
if (succBlock->bbNum <= block->bbNum)
11975+
{
11976+
// The succBlock had better agree it's a target.
11977+
//
11978+
assert((succBlock->bbFlags & BBF_BACKWARD_JUMP_TARGET) == BBF_BACKWARD_JUMP_TARGET);
11979+
11980+
// We may already have decided to put a patchpoint in succBlock. If not, add one.
11981+
//
11982+
if ((succBlock->bbFlags & BBF_PATCHPOINT) != 0)
11983+
{
11984+
// In some cases the target may not be stack-empty at entry.
11985+
// If so, we will bypass patchpoints for this backedge.
11986+
//
11987+
if (succBlock->bbStackDepthOnEntry() > 0)
11988+
{
11989+
JITDUMP("\nCan't set source patchpoint at " FMT_BB ", can't use target " FMT_BB
11990+
" as it has non-empty stack on entry.\n",
11991+
block->bbNum, succBlock->bbNum);
11992+
}
11993+
else
11994+
{
11995+
JITDUMP("\nCan't set source patchpoint at " FMT_BB ", using target " FMT_BB
11996+
" instead\n",
11997+
block->bbNum, succBlock->bbNum);
11998+
11999+
assert(!succBlock->hasHndIndex());
12000+
succBlock->bbFlags |= BBF_PATCHPOINT;
12001+
}
12002+
}
12003+
}
12004+
}
12005+
}
12006+
else
12007+
{
12008+
assert(!block->hasHndIndex());
12009+
block->bbFlags |= BBF_PATCHPOINT;
12010+
}
1189612011

11897-
block->bbFlags |= BBF_PATCHPOINT;
1189812012
setMethodHasPatchpoint();
1189912013
}
1190012014
}
1190112015
else
1190212016
{
11903-
// Should not see backward branch targets w/o backwards branches
11904-
assert((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == 0);
12017+
// Should not see backward branch targets w/o backwards branches.
12018+
// So if !compHasBackwardsBranch, these flags should never be set.
12019+
//
12020+
assert((block->bbFlags & (BBF_BACKWARD_JUMP_TARGET | BBF_BACKWARD_JUMP_SOURCE)) == 0);
1190512021
}
1190612022
}
1190712023

src/coreclr/jit/jitconfigvalues.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,11 @@ CONFIG_INTEGER(TC_OnStackReplacement, W("TC_OnStackReplacement"), 0)
464464
CONFIG_INTEGER(TC_OnStackReplacement_InitialCounter, W("TC_OnStackReplacement_InitialCounter"), 1000)
465465
// Enable partial compilation for Tier0 methods
466466
CONFIG_INTEGER(TC_PartialCompilation, W("TC_PartialCompilation"), 0)
467+
// Patchpoint strategy:
468+
// 0 - backedge sources
469+
// 1 - backedge targets
470+
// 2 - adaptive (default)
471+
CONFIG_INTEGER(TC_PatchpointStrategy, W("TC_PatchpointStrategy"), 2)
467472
#if defined(DEBUG)
468473
// Randomly sprinkle patchpoints. Value is the likelyhood any given stack-empty point becomes a patchpoint.
469474
CONFIG_INTEGER(JitRandomOnStackReplacement, W("JitRandomOnStackReplacement"), 0)

src/coreclr/jit/jiteh.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,7 @@ bool Compiler::fgNormalizeEHCase2()
23502350

23512351
BasicBlock* newTryStart = bbNewBasicBlock(BBJ_NONE);
23522352
fgInsertBBbefore(insertBeforeBlk, newTryStart);
2353+
insertBeforeBlk->bbRefs++;
23532354

23542355
#ifdef DEBUG
23552356
if (verbose)
@@ -2376,6 +2377,11 @@ bool Compiler::fgNormalizeEHCase2()
23762377
// start.
23772378
newTryStart->bbFlags |= (BBF_TRY_BEG | BBF_DONT_REMOVE | BBF_INTERNAL);
23782379

2380+
if (insertBeforeBlk->bbFlags & BBF_BACKWARD_JUMP_TARGET)
2381+
{
2382+
newTryStart->bbFlags |= BBF_BACKWARD_JUMP_TARGET;
2383+
}
2384+
23792385
// Now we need to split any flow edges targetting the old try begin block between the old
23802386
// and new block. Note that if we are handling a multiply-nested 'try', we may have already
23812387
// split the inner set. So we need to split again, from the most enclosing block that we've

0 commit comments

Comments
 (0)