Skip to content

Commit a79768e

Browse files
JIT: Propagate flow into finally regions correctly in synthesis (#114016)
Part of #107749. The first profile synthesis run happens before importation, so we don't model flow in and out of finally regions with flow edges yet. As a workaround, synthesis gives each finally region the same weight as its corresponding try region. When call-finally pairs are created, the tail inherits the weight of its head under the (faulty) assumption that all flow into a call-finally will return to the same pair. Once we have flow edges, we can compute the flow out of finally regions the same way as we compute flow elsewhere. It's important that synthesis models flow through finally regions via flow edges once we have them, or else flow through a loop that executes a finally might be lost, messing up the cyclic probability computation and flattening the loop's weight.
1 parent f6cafb9 commit a79768e

File tree

5 files changed

+115
-100
lines changed

5 files changed

+115
-100
lines changed

src/coreclr/jit/fgbasic.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,19 @@ void Compiler::fgRemoveEhfSuccessor(BasicBlock* block, const unsigned succIndex)
372372
(succCount - succIndex - 1) * sizeof(FlowEdge*));
373373
}
374374

375+
// Recompute the likelihoods of the block's other successor edges.
376+
const weight_t removedLikelihood = succEdge->getLikelihood();
377+
const unsigned newSuccCount = succCount - 1;
378+
379+
for (unsigned i = 0; i < newSuccCount; i++)
380+
{
381+
// If we removed all of the flow out of 'block', distribute flow among the remaining edges evenly.
382+
const weight_t currLikelihood = succTab[i]->getLikelihood();
383+
const weight_t newLikelihood =
384+
(removedLikelihood == 1.0) ? (1.0 / newSuccCount) : (currLikelihood / (1.0 - removedLikelihood));
385+
succTab[i]->setLikelihood(min(1.0, newLikelihood));
386+
}
387+
375388
#ifdef DEBUG
376389
// We only expect to see a successor once in the table.
377390
for (unsigned i = succIndex; i < (succCount - 1); i++)
@@ -431,6 +444,19 @@ void Compiler::fgRemoveEhfSuccessor(FlowEdge* succEdge)
431444
}
432445
}
433446

447+
// Recompute the likelihoods of the block's other successor edges.
448+
const weight_t removedLikelihood = succEdge->getLikelihood();
449+
const unsigned newSuccCount = succCount - 1;
450+
451+
for (unsigned i = 0; i < newSuccCount; i++)
452+
{
453+
// If we removed all of the flow out of 'block', distribute flow among the remaining edges evenly.
454+
const weight_t currLikelihood = succTab[i]->getLikelihood();
455+
const weight_t newLikelihood =
456+
(removedLikelihood == 1.0) ? (1.0 / newSuccCount) : (currLikelihood / (1.0 - removedLikelihood));
457+
succTab[i]->setLikelihood(min(1.0, newLikelihood));
458+
}
459+
434460
assert(found);
435461
ehfDesc->bbeCount--;
436462
}

src/coreclr/jit/fgehopt.cpp

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,27 +1644,28 @@ PhaseStatus Compiler::fgCloneFinally()
16441644

16451645
for (BasicBlock* const block : Blocks(firstBlock, lastBlock))
16461646
{
1647-
if (block->hasProfileWeight())
1648-
{
1649-
weight_t const blockWeight = block->bbWeight;
1650-
block->setBBProfileWeight(blockWeight * originalScale);
1651-
JITDUMP("Set weight of " FMT_BB " to " FMT_WT "\n", block->bbNum, block->bbWeight);
1647+
weight_t const blockWeight = block->bbWeight;
1648+
block->setBBProfileWeight(blockWeight * originalScale);
1649+
JITDUMP("Set weight of " FMT_BB " to " FMT_WT "\n", block->bbNum, block->bbWeight);
16521650

1653-
BasicBlock* const clonedBlock = blockMap[block];
1654-
clonedBlock->setBBProfileWeight(blockWeight * clonedScale);
1655-
JITDUMP("Set weight of " FMT_BB " to " FMT_WT "\n", clonedBlock->bbNum, clonedBlock->bbWeight);
1656-
}
1651+
BasicBlock* const clonedBlock = blockMap[block];
1652+
clonedBlock->setBBProfileWeight(blockWeight * clonedScale);
1653+
JITDUMP("Set weight of " FMT_BB " to " FMT_WT "\n", clonedBlock->bbNum, clonedBlock->bbWeight);
1654+
}
1655+
1656+
if (!retargetedAllCalls)
1657+
{
1658+
JITDUMP(
1659+
"Reduced flow out of EH%u needs to be propagated to continuation block(s). Data %s inconsistent.\n",
1660+
XTnum, fgPgoConsistent ? "is now" : "was already");
1661+
fgPgoConsistent = false;
16571662
}
16581663
}
16591664

16601665
// Update flow into normalCallFinallyReturn
16611666
if (normalCallFinallyReturn->hasProfileWeight())
16621667
{
1663-
normalCallFinallyReturn->bbWeight = BB_ZERO_WEIGHT;
1664-
for (FlowEdge* const predEdge : normalCallFinallyReturn->PredEdges())
1665-
{
1666-
normalCallFinallyReturn->increaseBBProfileWeight(predEdge->getLikelyWeight());
1667-
}
1668+
normalCallFinallyReturn->setBBProfileWeight(normalCallFinallyReturn->computeIncomingWeight());
16681669
}
16691670

16701671
// Done!
@@ -2185,33 +2186,13 @@ bool Compiler::fgRetargetBranchesToCanonicalCallFinally(BasicBlock* block,
21852186
//
21862187
if (block->hasProfileWeight())
21872188
{
2188-
// Add weight to the canonical call finally pair.
2189+
// Add weight to the canonical call-finally.
21892190
//
2190-
weight_t const canonicalWeight =
2191-
canonicalCallFinally->hasProfileWeight() ? canonicalCallFinally->bbWeight : BB_ZERO_WEIGHT;
2192-
weight_t const newCanonicalWeight = block->bbWeight + canonicalWeight;
2193-
2194-
canonicalCallFinally->setBBProfileWeight(newCanonicalWeight);
2191+
canonicalCallFinally->increaseBBProfileWeight(block->bbWeight);
21952192

2196-
BasicBlock* const canonicalLeaveBlock = canonicalCallFinally->Next();
2197-
2198-
weight_t const canonicalLeaveWeight =
2199-
canonicalLeaveBlock->hasProfileWeight() ? canonicalLeaveBlock->bbWeight : BB_ZERO_WEIGHT;
2200-
weight_t const newLeaveWeight = block->bbWeight + canonicalLeaveWeight;
2201-
2202-
canonicalLeaveBlock->setBBProfileWeight(newLeaveWeight);
2203-
2204-
// Remove weight from the old call finally pair.
2193+
// Remove weight from the old call-finally.
22052194
//
2206-
if (callFinally->hasProfileWeight())
2207-
{
2208-
callFinally->decreaseBBProfileWeight(block->bbWeight);
2209-
}
2210-
2211-
if (leaveBlock->hasProfileWeight())
2212-
{
2213-
leaveBlock->decreaseBBProfileWeight(block->bbWeight);
2214-
}
2195+
callFinally->decreaseBBProfileWeight(block->bbWeight);
22152196
}
22162197

22172198
return true;

src/coreclr/jit/fgprofile.cpp

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4787,20 +4787,12 @@ bool Compiler::fgDebugCheckIncomingProfileData(BasicBlock* block, ProfileChecks
47874787
weight_t incomingLikelyWeight = 0;
47884788
unsigned missingLikelyWeight = 0;
47894789
bool foundPreds = false;
4790-
bool foundEHPreds = false;
47914790

47924791
for (FlowEdge* const predEdge : block->PredEdges())
47934792
{
47944793
if (predEdge->hasLikelihood())
47954794
{
4796-
if (BasicBlock::sameHndRegion(block, predEdge->getSourceBlock()))
4797-
{
4798-
incomingLikelyWeight += predEdge->getLikelyWeight();
4799-
}
4800-
else
4801-
{
4802-
foundEHPreds = true;
4803-
}
4795+
incomingLikelyWeight += predEdge->getLikelyWeight();
48044796
}
48054797
else
48064798
{
@@ -4812,29 +4804,11 @@ bool Compiler::fgDebugCheckIncomingProfileData(BasicBlock* block, ProfileChecks
48124804
foundPreds = true;
48134805
}
48144806

4815-
// We almost certainly won't get the likelihoods on a BBJ_EHFINALLYRET right,
4816-
// so special-case BBJ_CALLFINALLYRET incoming flow.
4817-
//
4818-
if (block->isBBCallFinallyPairTail())
4819-
{
4820-
incomingLikelyWeight = block->Prev()->bbWeight;
4821-
foundEHPreds = false;
4822-
}
4823-
4824-
// We almost certainly won't get the likelihoods on a BBJ_EHFINALLYRET right,
4825-
// so special-case BBJ_CALLFINALLYRET incoming flow.
4826-
//
4827-
if (block->isBBCallFinallyPairTail())
4828-
{
4829-
incomingLikelyWeight = block->Prev()->bbWeight;
4830-
foundEHPreds = false;
4831-
}
4832-
48334807
bool likelyWeightsValid = true;
48344808

48354809
// If we have EH preds we may not have consistent incoming flow.
48364810
//
4837-
if (foundPreds && !foundEHPreds)
4811+
if (foundPreds)
48384812
{
48394813
if (verifyLikelyWeights)
48404814
{
@@ -4890,7 +4864,7 @@ bool Compiler::fgDebugCheckOutgoingProfileData(BasicBlock* block, ProfileChecks
48904864
//
48914865
const unsigned numSuccs = block->NumSucc(this);
48924866

4893-
if ((numSuccs > 0) && !block->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET))
4867+
if ((numSuccs > 0) && !block->KindIs(BBJ_EHFAULTRET, BBJ_EHFILTERRET))
48944868
{
48954869
weight_t const blockWeight = block->bbWeight;
48964870
weight_t outgoingLikelihood = 0;

src/coreclr/jit/fgprofilesynthesis.cpp

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ void ProfileSynthesis::Run(ProfileSynthesisOption option)
4141
assert(m_loops != nullptr);
4242
}
4343

44+
if (m_loops->NumLoops() > 0)
45+
{
46+
m_cyclicProbabilities = new (m_comp, CMK_Pgo) weight_t[m_loops->NumLoops()];
47+
}
48+
4449
// Profile synthesis can be run before or after morph, so tolerate (non-)canonical method entries
4550
//
4651
m_entryBlock = (m_comp->opts.IsOSR() && (m_comp->fgEntryBB != nullptr)) ? m_comp->fgEntryBB : m_comp->fgFirstBB;
@@ -168,7 +173,10 @@ void ProfileSynthesis::Run(ProfileSynthesisOption option)
168173
if (m_approximate)
169174
{
170175
JITDUMP("Profile is inconsistent. Bypassing post-phase consistency checks.\n");
171-
m_comp->Metrics.ProfileInconsistentInitially++;
176+
if (!m_comp->fgImportDone)
177+
{
178+
m_comp->Metrics.ProfileInconsistentInitially++;
179+
}
172180
}
173181

174182
// Derive the method's call count from the entry block's weight
@@ -208,7 +216,7 @@ void ProfileSynthesis::Run(ProfileSynthesisOption option)
208216
// Leave a note so we can bypass the post-phase check, and
209217
// instead assert at the end of fgImport, if we make it that far.
210218
//
211-
if (!isConsistent)
219+
if (!isConsistent && !m_comp->fgImportDone)
212220
{
213221
m_comp->fgPgoDeferredInconsistency = true;
214222
JITDUMP("Will defer asserting until after importation\n");
@@ -745,13 +753,6 @@ void ProfileSynthesis::RandomizeLikelihoods()
745753
//
746754
void ProfileSynthesis::ComputeCyclicProbabilities()
747755
{
748-
m_cyclicProbabilities = nullptr;
749-
if (m_loops->NumLoops() == 0)
750-
{
751-
return;
752-
}
753-
754-
m_cyclicProbabilities = new (m_comp, CMK_Pgo) weight_t[m_loops->NumLoops()];
755756
// Walk loops in post order to visit inner loops before outer loops.
756757
for (FlowGraphNaturalLoop* loop : m_loops->InPostOrder())
757758
{
@@ -828,10 +829,7 @@ void ProfileSynthesis::ComputeCyclicProbabilities(FlowGraphNaturalLoop* loop)
828829

829830
for (FlowEdge* const edge : nestedLoop->EntryEdges())
830831
{
831-
if (BasicBlock::sameHndRegion(block, edge->getSourceBlock()))
832-
{
833-
newWeight += edge->getLikelyWeight();
834-
}
832+
newWeight += edge->getLikelyWeight();
835833
}
836834

837835
newWeight *= m_cyclicProbabilities[nestedLoop->GetIndex()];
@@ -847,10 +845,10 @@ void ProfileSynthesis::ComputeCyclicProbabilities(FlowGraphNaturalLoop* loop)
847845
{
848846
BasicBlock* const sourceBlock = edge->getSourceBlock();
849847

850-
// Ignore flow across EH, or from preds not in the loop.
851-
// Latter can happen if there are unreachable blocks that flow into the loop.
848+
// Ignore flow from preds not in the loop.
849+
// This can happen if there are unreachable blocks that flow into the loop.
852850
//
853-
if (BasicBlock::sameHndRegion(block, sourceBlock) && loop->ContainsBlock(sourceBlock))
851+
if (loop->ContainsBlock(sourceBlock))
854852
{
855853
newWeight += edge->getLikelyWeight();
856854
}
@@ -1214,7 +1212,8 @@ void ProfileSynthesis::GaussSeidelSolver()
12141212
const FlowGraphDfsTree* const dfs = m_loops->GetDfsTree();
12151213
unsigned const blockCount = dfs->GetPostOrderCount();
12161214
bool checkEntryExitWeight = true;
1217-
bool showDetails = false;
1215+
bool const showDetails = false;
1216+
bool const callFinalliesCreated = m_comp->fgImportDone;
12181217

12191218
JITDUMP("Synthesis solver: flow graph has %u improper loop headers\n", m_improperLoopHeaders);
12201219

@@ -1290,9 +1289,10 @@ void ProfileSynthesis::GaussSeidelSolver()
12901289
{
12911290
newWeight = block->bbWeight;
12921291

1293-
// Finallies also add in the weight of their try.
1292+
// If we haven't added flow edges into/out of finallies yet,
1293+
// add in the weight of their corresponding try regions.
12941294
//
1295-
if (ehDsc->HasFinallyHandler())
1295+
if (!callFinalliesCreated && ehDsc->HasFinallyHandler())
12961296
{
12971297
newWeight += countVector[ehDsc->ebdTryBeg->bbNum];
12981298
}
@@ -1318,11 +1318,7 @@ void ProfileSynthesis::GaussSeidelSolver()
13181318
for (FlowEdge* const edge : loop->EntryEdges())
13191319
{
13201320
BasicBlock* const predBlock = edge->getSourceBlock();
1321-
1322-
if (BasicBlock::sameHndRegion(block, predBlock))
1323-
{
1324-
newWeight += edge->getLikelihood() * countVector[predBlock->bbNum];
1325-
}
1321+
newWeight += edge->getLikelihood() * countVector[predBlock->bbNum];
13261322
}
13271323

13281324
// Scale by cyclic probability
@@ -1354,10 +1350,7 @@ void ProfileSynthesis::GaussSeidelSolver()
13541350
continue;
13551351
}
13561352

1357-
if (BasicBlock::sameHndRegion(block, predBlock))
1358-
{
1359-
newWeight += edge->getLikelihood() * countVector[predBlock->bbNum];
1360-
}
1353+
newWeight += edge->getLikelihood() * countVector[predBlock->bbNum];
13611354
}
13621355

13631356
if (selfEdge != nullptr)
@@ -1444,10 +1437,12 @@ void ProfileSynthesis::GaussSeidelSolver()
14441437
}
14451438
}
14461439

1447-
// If there were no improper headers, we will have converged in one pass.
1440+
// If there were no improper headers, we will have converged in one pass
14481441
// (profile may still be inconsistent, if there were capped cyclic probabilities).
1442+
// After the importer runs, we require that synthesis achieves profile consistency
1443+
// unless the resultant profile is approximate, so don't skip the below checks.
14491444
//
1450-
if (m_improperLoopHeaders == 0)
1445+
if ((m_improperLoopHeaders == 0) && !m_comp->fgImportDone)
14511446
{
14521447
converged = true;
14531448
break;

src/coreclr/jit/importer.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12576,8 +12576,9 @@ void Compiler::impImport()
1257612576
//
1257712577
void Compiler::impFixPredLists()
1257812578
{
12579-
unsigned XTnum = 0;
12580-
bool added = false;
12579+
unsigned XTnum = 0;
12580+
bool added = false;
12581+
const bool usingProfileWeights = fgIsUsingProfileWeights();
1258112582

1258212583
for (EHblkDsc* HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++)
1258312584
{
@@ -12586,6 +12587,7 @@ void Compiler::impFixPredLists()
1258612587
BasicBlock* const finallyBegBlock = HBtab->ebdHndBeg;
1258712588
BasicBlock* const finallyLastBlock = HBtab->ebdHndLast;
1258812589
unsigned predCount = (unsigned)-1;
12590+
const weight_t finallyWeight = finallyBegBlock->bbWeight;
1258912591

1259012592
for (BasicBlock* const finallyBlock : BasicBlockRangeList(finallyBegBlock, finallyLastBlock))
1259112593
{
@@ -12629,7 +12631,8 @@ void Compiler::impFixPredLists()
1262912631
jumpEhf->bbeCount = predCount;
1263012632
jumpEhf->bbeSuccs = new (this, CMK_FlowEdge) FlowEdge*[predCount];
1263112633

12632-
unsigned predNum = 0;
12634+
unsigned predNum = 0;
12635+
weight_t remainingLikelihood = 1.0;
1263312636
for (BasicBlock* const predBlock : finallyBegBlock->PredBlocks())
1263412637
{
1263512638
// We only care about preds that are callfinallies.
@@ -12643,6 +12646,22 @@ void Compiler::impFixPredLists()
1264312646
FlowEdge* const newEdge = fgAddRefPred(continuation, finallyBlock);
1264412647
newEdge->setLikelihood(1.0 / predCount);
1264512648

12649+
if (usingProfileWeights && (finallyWeight != BB_ZERO_WEIGHT))
12650+
{
12651+
// Derive edge likelihood from the entry block's weight relative to other entries.
12652+
//
12653+
const weight_t callFinallyWeight = predBlock->bbWeight;
12654+
const weight_t likelihood = min(callFinallyWeight / finallyWeight, 1.0);
12655+
newEdge->setLikelihood(min(likelihood, remainingLikelihood));
12656+
remainingLikelihood = max(BB_ZERO_WEIGHT, remainingLikelihood - likelihood);
12657+
}
12658+
else
12659+
{
12660+
// If we don't have profile data, evenly distribute the likelihoods.
12661+
//
12662+
newEdge->setLikelihood(1.0 / predCount);
12663+
}
12664+
1264612665
jumpEhf->bbeSuccs[predNum] = newEdge;
1264712666
++predNum;
1264812667

@@ -12657,6 +12676,26 @@ void Compiler::impFixPredLists()
1265712676

1265812677
finallyBlock->SetEhfTargets(jumpEhf);
1265912678
}
12679+
12680+
if (usingProfileWeights)
12681+
{
12682+
// Compute new flow into the finally region's continuation successors.
12683+
//
12684+
bool profileConsistent = true;
12685+
for (BasicBlock* const callFinally : finallyBegBlock->PredBlocks())
12686+
{
12687+
BasicBlock* const callFinallyRet = callFinally->Next();
12688+
callFinallyRet->setBBProfileWeight(callFinallyRet->computeIncomingWeight());
12689+
profileConsistent &= fgProfileWeightsEqual(callFinally->bbWeight, callFinallyRet->bbWeight);
12690+
}
12691+
12692+
if (!profileConsistent)
12693+
{
12694+
JITDUMP("Flow into finally handler EH%u does not match outgoing flow. Data %s inconsistent.\n",
12695+
XTnum, fgPgoConsistent ? "is now" : "was already");
12696+
fgPgoConsistent = false;
12697+
}
12698+
}
1266012699
}
1266112700
}
1266212701
}

0 commit comments

Comments
 (0)