Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Move profile checking back until just before inlining #101011

Merged
merged 7 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4629,11 +4629,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_IMPORTATION, &Compiler::fgImport);

// Drop back to just checking profile likelihoods.
//
activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE;
activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS;

// If this is a failed inline attempt, we're done.
//
if (compIsForInlining() && compInlineResult->IsFailure())
Expand Down Expand Up @@ -4688,6 +4683,11 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
return;
}

// Drop back to just checking profile likelihoods.
//
activePhaseChecks &= ~PhaseChecks::CHECK_PROFILE;
activePhaseChecks |= PhaseChecks::CHECK_LIKELIHOODS;

// At this point in the phase list, all the inlinee phases have
// been run, and inlinee compiles have exited, so we should only
// get this far if we are jitting the root method.
Expand Down
54 changes: 44 additions & 10 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,33 @@ bool Compiler::fgEnsureFirstBBisScratch()

block = BasicBlock::New(this);

// If we have profile data the new block will inherit fgFirstBlock's weight
// If we have profile data determine the weight of the scratch BB
//
if (fgFirstBB->hasProfileWeight())
{
block->inheritWeight(fgFirstBB);
// If current entry has preds, sum up those weights
//
weight_t nonEntryWeight = 0;
for (FlowEdge* const edge : fgFirstBB->PredEdges())
{
nonEntryWeight += edge->getLikelyWeight();
}

// entry weight is weight not from any pred
//
weight_t const entryWeight = fgFirstBB->bbWeight - nonEntryWeight;
if (entryWeight <= 0)
{
// If the result is clearly nonsensical, just inherit
//
JITDUMP("\fgEnsureFirstBBisScratch: Profile data could not be locally repaired. Data %s inconsisent.\n",
fgPgoConsistent ? "is now" : "was already");
block->inheritWeight(fgFirstBB);
}
else
{
block->setBBProfileWeight(entryWeight);
}
}

// The new scratch bb will fall through to the old first bb
Expand Down Expand Up @@ -5063,17 +5086,28 @@ BasicBlock* Compiler::fgSplitEdge(BasicBlock* curr, BasicBlock* succ)
fgReplaceJumpTarget(curr, succ, newBlock);

// And 'succ' has 'newBlock' as a new predecessor.
FlowEdge* const newEdge = fgAddRefPred(succ, newBlock);
newBlock->SetTargetEdge(newEdge);
FlowEdge* const newSuccEdge = fgAddRefPred(succ, newBlock);
newBlock->SetTargetEdge(newSuccEdge);

// This isn't accurate, but it is complex to compute a reasonable number so just assume that we take the
// branch 50% of the time.
//
// TODO: leverage edge likelihood.
// Set weight for newBlock
//
if (!curr->KindIs(BBJ_ALWAYS))
if (curr->KindIs(BBJ_ALWAYS))
{
newBlock->inheritWeight(curr);
}
else
{
newBlock->inheritWeightPercentage(curr, 50);
if (curr->hasProfileWeight())
{
FlowEdge* const currNewEdge = fgGetPredForBlock(newBlock, curr);
newBlock->setBBProfileWeight(currNewEdge->getLikelyWeight());
}
else
{
// Todo: use likelihood even w/o profile?
//
newBlock->inheritWeightPercentage(curr, 50);
}
}

// The bbLiveIn and bbLiveOut are both equal to the bbLiveIn of 'succ'
Expand Down
51 changes: 33 additions & 18 deletions src/coreclr/jit/fgopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,17 +731,8 @@ PhaseStatus Compiler::fgPostImportationCleanup()
//
auto addConditionalFlow = [this, entryStateVar, &entryJumpTarget, &addedBlocks](BasicBlock* fromBlock,
BasicBlock* toBlock) {
// We may have previously though this try entry was unreachable, but now we're going to
// step through it on the way to the OSR entry. So ensure it has plausible profile weight.
//
if (fgHaveProfileWeights() && !fromBlock->hasProfileWeight())
{
JITDUMP("Updating block weight for now-reachable try entry " FMT_BB " via " FMT_BB "\n",
fromBlock->bbNum, fgFirstBB->bbNum);
fromBlock->inheritWeight(fgFirstBB);
}

BasicBlock* const newBlock = fgSplitBlockAtBeginning(fromBlock);
newBlock->inheritWeight(fromBlock);
fromBlock->SetFlags(BBF_INTERNAL);
newBlock->RemoveFlags(BBF_DONT_REMOVE);
addedBlocks++;
Expand All @@ -754,16 +745,40 @@ PhaseStatus Compiler::fgPostImportationCleanup()
fgNewStmtAtBeg(fromBlock, jumpIfEntryStateZero);

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

// Not sure what the correct edge likelihoods are just yet;
// for now we'll say the OSR path is the likely one.
//
// Todo: can we leverage profile data here to get a better answer?
//
osrTryEntryEdge->setLikelihood(0.9);
normalTryEntryEdge->setLikelihood(0.1);
if (fgHaveProfileWeights())
{
// We are adding a path from (ultimately) the method entry to "fromBlock"
// Update the profile weight.
//
weight_t const entryWeight = fgFirstBB->bbWeight;

JITDUMP("Updating block weight for now-reachable try entry " FMT_BB " via " FMT_BB "\n",
fromBlock->bbNum, fgFirstBB->bbNum);
fromBlock->setBBProfileWeight(fromBlock->bbWeight + entryWeight);

// We updated the weight of fromBlock above.
//
// Set the likelihoods such that the additional weight flows to toBlock
// (and so the "normal path" profile out of fromBlock to newBlock is unaltered)
//
// In some stress cases we may have a zero-weight OSR entry.
// Tolerate this by capping the fromToLikelihood.
//
weight_t const fromWeight = fromBlock->bbWeight;
weight_t const fromToLikelihood = min(1.0, entryWeight / fromWeight);

osrTryEntryEdge->setLikelihood(fromToLikelihood);
normalTryEntryEdge->setLikelihood(1.0 - fromToLikelihood);
}
else
{
// Just set likelihoods arbitrarily
//
osrTryEntryEdge->setLikelihood(0.9);
normalTryEntryEdge->setLikelihood(0.1);
}

entryJumpTarget = fromBlock;
};
Expand Down
50 changes: 49 additions & 1 deletion src/coreclr/jit/fgprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1689,16 +1689,42 @@ void EfficientEdgeCountInstrumentor::RelocateProbes()
{
BasicBlock* intermediary = m_comp->fgNewBBbefore(BBJ_ALWAYS, block, /* extendRegion */ true);
intermediary->SetFlags(BBF_IMPORTED);
intermediary->inheritWeight(block);
FlowEdge* const newEdge = m_comp->fgAddRefPred(block, intermediary);
intermediary->SetTargetEdge(newEdge);
NewRelocatedProbe(intermediary, probe->source, probe->target, &leader);
SetModifiedFlow();

// Redirect flow and figure out profile impact.
//
// We don't expect to see mixtures of profiled and unprofiled preds,
// but if we do, fall back to our old default behavior.
//
weight_t weight = 0;
bool allPredsHaveProfile = true;

while (criticalPreds.Height() > 0)
{
BasicBlock* const pred = criticalPreds.Pop();
m_comp->fgReplaceJumpTarget(pred, block, intermediary);

if (pred->hasProfileWeight())
{
FlowEdge* const predIntermediaryEdge = m_comp->fgGetPredForBlock(intermediary, pred);
weight += predIntermediaryEdge->getLikelyWeight();
}
else
{
allPredsHaveProfile = false;
}
}

if (allPredsHaveProfile)
{
intermediary->setBBProfileWeight(weight);
}
else
{
intermediary->inheritWeight(block);
}
}
}
Expand Down Expand Up @@ -4773,6 +4799,19 @@ bool Compiler::fgDebugCheckProfileWeights(ProfileChecks checks)
verifyIncoming = false;
}

// Original entries in OSR methods that also are
// loop headers.
//
// These will frequently have a profile imbalance as
// synthesis will have injected profile weight for
// method entry, but when we transform flow for OSR,
// only the loop back edges remain.
//
if (block == fgEntryBB)
{
verifyIncoming = false;
}

// Handler entries
//
if (block->hasEHBoundaryIn())
Expand Down Expand Up @@ -4935,6 +4974,15 @@ bool Compiler::fgDebugCheckIncomingProfileData(BasicBlock* block, ProfileChecks
foundEHPreds = false;
}

// We almost certainly won't get the likelihoods on a BBJ_EHFINALLYRET right,
// so special-case BBJ_CALLFINALLYRET incoming flow.
//
if (block->isBBCallFinallyPairTail())
{
incomingLikelyWeight = block->Prev()->bbWeight;
foundEHPreds = false;
}

bool likelyWeightsValid = true;

// If we have EH preds we may not have consistent incoming flow.
Expand Down
46 changes: 44 additions & 2 deletions src/coreclr/jit/indirectcalltransformer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ class IndirectCallTransformer
//
thenBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, checkBlock);
thenBlock->CopyFlags(currBlock, BBF_SPLIT_GAINED);
thenBlock->inheritWeight(currBlock);
thenBlock->inheritWeight(checkBlock);
thenBlock->scaleBBWeight(adjustedThenLikelihood);
FlowEdge* const thenRemainderEdge = compiler->fgAddRefPred(remainderBlock, thenBlock);
thenBlock->SetTargetEdge(thenRemainderEdge);
Expand Down Expand Up @@ -1214,10 +1214,52 @@ class IndirectCallTransformer
checkStmt = nextStmt;
}

// Finally, rewire the cold block to jump to the else block,
// Rewire the cold block to jump to the else block,
// not fall through to the check block.
//
compiler->fgRedirectTargetEdge(coldBlock, elseBlock);

// Update the profile data
//
if (coldBlock->hasProfileWeight())
{
// Check block
//
FlowEdge* const coldElseEdge = compiler->fgGetPredForBlock(elseBlock, coldBlock);
weight_t newCheckWeight = checkBlock->bbWeight - coldElseEdge->getLikelyWeight();

if (newCheckWeight < 0)
{
// If weights were consistent, we expect at worst a slight underflow.
//
if (compiler->fgPgoConsistent)
{
bool const isReasonableUnderflow = Compiler::fgProfileWeightsEqual(newCheckWeight, 0.0);
assert(isReasonableUnderflow);

if (!isReasonableUnderflow)
{
compiler->fgPgoConsistent = false;
}
}

// No matter what, the minimum weight is zero
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// No matter what, the minimum weight is zero
// No matter what, the minimum weight is zero

//
newCheckWeight = 0;
}
checkBlock->setBBProfileWeight(newCheckWeight);

// Else block
//
FlowEdge* const checkElseEdge = compiler->fgGetPredForBlock(elseBlock, checkBlock);
weight_t const newElseWeight = checkElseEdge->getLikelyWeight() + coldElseEdge->getLikelyWeight();
elseBlock->setBBProfileWeight(newElseWeight);

// Then block
//
FlowEdge* const checkThenEdge = compiler->fgGetPredForBlock(thenBlock, checkBlock);
thenBlock->setBBProfileWeight(checkThenEdge->getLikelyWeight());
}
}

// When the current candidate has sufficiently high likelihood, scan
Expand Down
Loading