Skip to content

Commit 73e2f09

Browse files
AndyAyersMSmatouskozak
authored andcommitted
JIT: Move profile checking back until just before inlining (dotnet#101011)
Fixes the following areas with proper profile updates: * GDV chaining * instrumentation-introduces flow * OSR step blocks * fgSplitEdge (used by instrumentation) Adds checking bypasses for: * callfinally pair tails * original method entries in OSR methods Contributes to dotnet#93020
1 parent 358e8a6 commit 73e2f09

File tree

5 files changed

+175
-36
lines changed

5 files changed

+175
-36
lines changed

src/coreclr/jit/compiler.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,11 +4629,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
46294629
//
46304630
DoPhase(this, PHASE_IMPORTATION, &Compiler::fgImport);
46314631

4632-
// Drop back to just checking profile likelihoods.
4633-
//
4634-
activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE;
4635-
activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS;
4636-
46374632
// If this is a failed inline attempt, we're done.
46384633
//
46394634
if (compIsForInlining() && compInlineResult->IsFailure())
@@ -4688,6 +4683,11 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
46884683
return;
46894684
}
46904685

4686+
// Drop back to just checking profile likelihoods.
4687+
//
4688+
activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE;
4689+
activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS;
4690+
46914691
// At this point in the phase list, all the inlinee phases have
46924692
// been run, and inlinee compiles have exited, so we should only
46934693
// get this far if we are jitting the root method.

src/coreclr/jit/fgbasic.cpp

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,33 @@ bool Compiler::fgEnsureFirstBBisScratch()
214214

215215
block = BasicBlock::New(this);
216216

217-
// If we have profile data the new block will inherit fgFirstBlock's weight
217+
// If we have profile data determine the weight of the scratch BB
218+
//
218219
if (fgFirstBB->hasProfileWeight())
219220
{
220-
block->inheritWeight(fgFirstBB);
221+
// If current entry has preds, sum up those weights
222+
//
223+
weight_t nonEntryWeight = 0;
224+
for (FlowEdge* const edge : fgFirstBB->PredEdges())
225+
{
226+
nonEntryWeight += edge->getLikelyWeight();
227+
}
228+
229+
// entry weight is weight not from any pred
230+
//
231+
weight_t const entryWeight = fgFirstBB->bbWeight - nonEntryWeight;
232+
if (entryWeight <= 0)
233+
{
234+
// If the result is clearly nonsensical, just inherit
235+
//
236+
JITDUMP("\fgEnsureFirstBBisScratch: Profile data could not be locally repaired. Data %s inconsisent.\n",
237+
fgPgoConsistent ? "is now" : "was already");
238+
block->inheritWeight(fgFirstBB);
239+
}
240+
else
241+
{
242+
block->setBBProfileWeight(entryWeight);
243+
}
221244
}
222245

223246
// The new scratch bb will fall through to the old first bb
@@ -5062,17 +5085,28 @@ BasicBlock* Compiler::fgSplitEdge(BasicBlock* curr, BasicBlock* succ)
50625085
fgReplaceJumpTarget(curr, succ, newBlock);
50635086

50645087
// And 'succ' has 'newBlock' as a new predecessor.
5065-
FlowEdge* const newEdge = fgAddRefPred(succ, newBlock);
5066-
newBlock->SetTargetEdge(newEdge);
5088+
FlowEdge* const newSuccEdge = fgAddRefPred(succ, newBlock);
5089+
newBlock->SetTargetEdge(newSuccEdge);
50675090

5068-
// This isn't accurate, but it is complex to compute a reasonable number so just assume that we take the
5069-
// branch 50% of the time.
5070-
//
5071-
// TODO: leverage edge likelihood.
5091+
// Set weight for newBlock
50725092
//
5073-
if (!curr->KindIs(BBJ_ALWAYS))
5093+
if (curr->KindIs(BBJ_ALWAYS))
5094+
{
5095+
newBlock->inheritWeight(curr);
5096+
}
5097+
else
50745098
{
5075-
newBlock->inheritWeightPercentage(curr, 50);
5099+
if (curr->hasProfileWeight())
5100+
{
5101+
FlowEdge* const currNewEdge = fgGetPredForBlock(newBlock, curr);
5102+
newBlock->setBBProfileWeight(currNewEdge->getLikelyWeight());
5103+
}
5104+
else
5105+
{
5106+
// Todo: use likelihood even w/o profile?
5107+
//
5108+
newBlock->inheritWeightPercentage(curr, 50);
5109+
}
50765110
}
50775111

50785112
// The bbLiveIn and bbLiveOut are both equal to the bbLiveIn of 'succ'

src/coreclr/jit/fgopt.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -731,17 +731,8 @@ PhaseStatus Compiler::fgPostImportationCleanup()
731731
//
732732
auto addConditionalFlow = [this, entryStateVar, &entryJumpTarget, &addedBlocks](BasicBlock* fromBlock,
733733
BasicBlock* toBlock) {
734-
// We may have previously though this try entry was unreachable, but now we're going to
735-
// step through it on the way to the OSR entry. So ensure it has plausible profile weight.
736-
//
737-
if (fgHaveProfileWeights() && !fromBlock->hasProfileWeight())
738-
{
739-
JITDUMP("Updating block weight for now-reachable try entry " FMT_BB " via " FMT_BB "\n",
740-
fromBlock->bbNum, fgFirstBB->bbNum);
741-
fromBlock->inheritWeight(fgFirstBB);
742-
}
743-
744734
BasicBlock* const newBlock = fgSplitBlockAtBeginning(fromBlock);
735+
newBlock->inheritWeight(fromBlock);
745736
fromBlock->SetFlags(BBF_INTERNAL);
746737
newBlock->RemoveFlags(BBF_DONT_REMOVE);
747738
addedBlocks++;
@@ -754,16 +745,40 @@ PhaseStatus Compiler::fgPostImportationCleanup()
754745
fgNewStmtAtBeg(fromBlock, jumpIfEntryStateZero);
755746

756747
FlowEdge* const osrTryEntryEdge = fgAddRefPred(toBlock, fromBlock);
757-
newBlock->inheritWeight(fromBlock);
758748
fromBlock->SetCond(osrTryEntryEdge, normalTryEntryEdge);
759749

760-
// Not sure what the correct edge likelihoods are just yet;
761-
// for now we'll say the OSR path is the likely one.
762-
//
763-
// Todo: can we leverage profile data here to get a better answer?
764-
//
765-
osrTryEntryEdge->setLikelihood(0.9);
766-
normalTryEntryEdge->setLikelihood(0.1);
750+
if (fgHaveProfileWeights())
751+
{
752+
// We are adding a path from (ultimately) the method entry to "fromBlock"
753+
// Update the profile weight.
754+
//
755+
weight_t const entryWeight = fgFirstBB->bbWeight;
756+
757+
JITDUMP("Updating block weight for now-reachable try entry " FMT_BB " via " FMT_BB "\n",
758+
fromBlock->bbNum, fgFirstBB->bbNum);
759+
fromBlock->setBBProfileWeight(fromBlock->bbWeight + entryWeight);
760+
761+
// We updated the weight of fromBlock above.
762+
//
763+
// Set the likelihoods such that the additional weight flows to toBlock
764+
// (and so the "normal path" profile out of fromBlock to newBlock is unaltered)
765+
//
766+
// In some stress cases we may have a zero-weight OSR entry.
767+
// Tolerate this by capping the fromToLikelihood.
768+
//
769+
weight_t const fromWeight = fromBlock->bbWeight;
770+
weight_t const fromToLikelihood = min(1.0, entryWeight / fromWeight);
771+
772+
osrTryEntryEdge->setLikelihood(fromToLikelihood);
773+
normalTryEntryEdge->setLikelihood(1.0 - fromToLikelihood);
774+
}
775+
else
776+
{
777+
// Just set likelihoods arbitrarily
778+
//
779+
osrTryEntryEdge->setLikelihood(0.9);
780+
normalTryEntryEdge->setLikelihood(0.1);
781+
}
767782

768783
entryJumpTarget = fromBlock;
769784
};

src/coreclr/jit/fgprofile.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1689,16 +1689,42 @@ void EfficientEdgeCountInstrumentor::RelocateProbes()
16891689
{
16901690
BasicBlock* intermediary = m_comp->fgNewBBbefore(BBJ_ALWAYS, block, /* extendRegion */ true);
16911691
intermediary->SetFlags(BBF_IMPORTED);
1692-
intermediary->inheritWeight(block);
16931692
FlowEdge* const newEdge = m_comp->fgAddRefPred(block, intermediary);
16941693
intermediary->SetTargetEdge(newEdge);
16951694
NewRelocatedProbe(intermediary, probe->source, probe->target, &leader);
16961695
SetModifiedFlow();
16971696

1697+
// Redirect flow and figure out profile impact.
1698+
//
1699+
// We don't expect to see mixtures of profiled and unprofiled preds,
1700+
// but if we do, fall back to our old default behavior.
1701+
//
1702+
weight_t weight = 0;
1703+
bool allPredsHaveProfile = true;
1704+
16981705
while (criticalPreds.Height() > 0)
16991706
{
17001707
BasicBlock* const pred = criticalPreds.Pop();
17011708
m_comp->fgReplaceJumpTarget(pred, block, intermediary);
1709+
1710+
if (pred->hasProfileWeight())
1711+
{
1712+
FlowEdge* const predIntermediaryEdge = m_comp->fgGetPredForBlock(intermediary, pred);
1713+
weight += predIntermediaryEdge->getLikelyWeight();
1714+
}
1715+
else
1716+
{
1717+
allPredsHaveProfile = false;
1718+
}
1719+
}
1720+
1721+
if (allPredsHaveProfile)
1722+
{
1723+
intermediary->setBBProfileWeight(weight);
1724+
}
1725+
else
1726+
{
1727+
intermediary->inheritWeight(block);
17021728
}
17031729
}
17041730
}
@@ -4773,6 +4799,19 @@ bool Compiler::fgDebugCheckProfileWeights(ProfileChecks checks)
47734799
verifyIncoming = false;
47744800
}
47754801

4802+
// Original entries in OSR methods that also are
4803+
// loop headers.
4804+
//
4805+
// These will frequently have a profile imbalance as
4806+
// synthesis will have injected profile weight for
4807+
// method entry, but when we transform flow for OSR,
4808+
// only the loop back edges remain.
4809+
//
4810+
if (block == fgEntryBB)
4811+
{
4812+
verifyIncoming = false;
4813+
}
4814+
47764815
// Handler entries
47774816
//
47784817
if (block->hasEHBoundaryIn())
@@ -4935,6 +4974,15 @@ bool Compiler::fgDebugCheckIncomingProfileData(BasicBlock* block, ProfileChecks
49354974
foundEHPreds = false;
49364975
}
49374976

4977+
// We almost certainly won't get the likelihoods on a BBJ_EHFINALLYRET right,
4978+
// so special-case BBJ_CALLFINALLYRET incoming flow.
4979+
//
4980+
if (block->isBBCallFinallyPairTail())
4981+
{
4982+
incomingLikelyWeight = block->Prev()->bbWeight;
4983+
foundEHPreds = false;
4984+
}
4985+
49384986
bool likelyWeightsValid = true;
49394987

49404988
// If we have EH preds we may not have consistent incoming flow.

src/coreclr/jit/indirectcalltransformer.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ class IndirectCallTransformer
10521052
//
10531053
thenBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, checkBlock);
10541054
thenBlock->CopyFlags(currBlock, BBF_SPLIT_GAINED);
1055-
thenBlock->inheritWeight(currBlock);
1055+
thenBlock->inheritWeight(checkBlock);
10561056
thenBlock->scaleBBWeight(adjustedThenLikelihood);
10571057
FlowEdge* const thenRemainderEdge = compiler->fgAddRefPred(remainderBlock, thenBlock);
10581058
thenBlock->SetTargetEdge(thenRemainderEdge);
@@ -1214,10 +1214,52 @@ class IndirectCallTransformer
12141214
checkStmt = nextStmt;
12151215
}
12161216

1217-
// Finally, rewire the cold block to jump to the else block,
1217+
// Rewire the cold block to jump to the else block,
12181218
// not fall through to the check block.
12191219
//
12201220
compiler->fgRedirectTargetEdge(coldBlock, elseBlock);
1221+
1222+
// Update the profile data
1223+
//
1224+
if (coldBlock->hasProfileWeight())
1225+
{
1226+
// Check block
1227+
//
1228+
FlowEdge* const coldElseEdge = compiler->fgGetPredForBlock(elseBlock, coldBlock);
1229+
weight_t newCheckWeight = checkBlock->bbWeight - coldElseEdge->getLikelyWeight();
1230+
1231+
if (newCheckWeight < 0)
1232+
{
1233+
// If weights were consistent, we expect at worst a slight underflow.
1234+
//
1235+
if (compiler->fgPgoConsistent)
1236+
{
1237+
bool const isReasonableUnderflow = Compiler::fgProfileWeightsEqual(newCheckWeight, 0.0);
1238+
assert(isReasonableUnderflow);
1239+
1240+
if (!isReasonableUnderflow)
1241+
{
1242+
compiler->fgPgoConsistent = false;
1243+
}
1244+
}
1245+
1246+
// No matter what, the minimum weight is zero
1247+
//
1248+
newCheckWeight = 0;
1249+
}
1250+
checkBlock->setBBProfileWeight(newCheckWeight);
1251+
1252+
// Else block
1253+
//
1254+
FlowEdge* const checkElseEdge = compiler->fgGetPredForBlock(elseBlock, checkBlock);
1255+
weight_t const newElseWeight = checkElseEdge->getLikelyWeight() + coldElseEdge->getLikelyWeight();
1256+
elseBlock->setBBProfileWeight(newElseWeight);
1257+
1258+
// Then block
1259+
//
1260+
FlowEdge* const checkThenEdge = compiler->fgGetPredForBlock(thenBlock, checkBlock);
1261+
thenBlock->setBBProfileWeight(checkThenEdge->getLikelyWeight());
1262+
}
12211263
}
12221264

12231265
// When the current candidate has sufficiently high likelihood, scan

0 commit comments

Comments
 (0)