diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 75def5b0e6a45..871978792b274 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -68,6 +68,24 @@ unsigned SsaStressHashHelper() } #endif +//------------------------------------------------------------------------ +// setLikelihood: set the likelihood of aflow edge +// +// Arguments: +// likelihood -- value in range [0.0, 1.0] indicating how likely +// the source block is to transfer control along this edge. +// +void FlowEdge::setLikelihood(weight_t likelihood) +{ + assert(likelihood >= 0.0); + assert(likelihood <= 1.0); + m_likelihoodSet = true; + m_likelihood = likelihood; + + JITDUMP("setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT "\n", m_sourceBlock->bbNum, m_destBlock->bbNum, + m_likelihood); +} + //------------------------------------------------------------------------ // AllSuccessorEnumerator: Construct an instance of the enumerator. // diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 12f9766896708..635f6b32e8994 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -635,13 +635,7 @@ struct FlowEdge return m_likelihood; } - void setLikelihood(weight_t likelihood) - { - assert(likelihood >= 0.0); - assert(likelihood <= 1.0); - m_likelihoodSet = true; - m_likelihood = likelihood; - } + void setLikelihood(weight_t likelihood); void clearLikelihood() { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 25607e21438d6..d94aca0e4b795 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1584,9 +1584,10 @@ enum class ProfileChecks : unsigned int CHECK_NONE = 0, CHECK_CLASSIC = 1 << 0, // check "classic" jit weights CHECK_HASLIKELIHOOD = 1 << 1, // check all FlowEdges for hasLikelihood - CHECK_LIKELY = 1 << 2, // fully check likelihood based weights - RAISE_ASSERT = 1 << 3, // assert on check failure - CHECK_ALL_BLOCKS = 1 << 4, // check blocks even if bbHasProfileWeight is false + CHECK_LIKELIHOODSUM = 1 << 2, // check block succesor likelihoods sum to 1 + CHECK_LIKELY = 1 << 3, // fully check likelihood based weights + RAISE_ASSERT = 1 << 4, // assert on check failure + CHECK_ALL_BLOCKS = 1 << 5, // check blocks even if bbHasProfileWeight is false }; inline constexpr ProfileChecks operator ~(ProfileChecks a) diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 8497e3c904164..baf0b409841fb 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -684,6 +684,9 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorfgRemoveRefPred(block->GetFalseEdge()); block->SetKind(BBJ_ALWAYS); } + + FlowEdge* const edge = m_compiler->fgGetPredForBlock(block->GetTarget(), block); + edge->setLikelihood(1.0); } } else diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 1499042cb8b42..3b6eb2a4921d2 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -3862,18 +3862,22 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, { // We expect one pseudo-edge and at least one normal edge. // - Edge* pseudoEdge = nullptr; - unsigned nEdges = 0; + Edge* pseudoEdge = nullptr; + weight_t pseudoEdgeWeight = 0; + unsigned nEdges = 0; + weight_t successorWeight = BB_ZERO_WEIGHT; for (Edge* edge = info->m_outgoingEdges; edge != nullptr; edge = edge->m_nextOutgoingEdge) { if (edge->m_isPseudoEdge) { assert(pseudoEdge == nullptr); - pseudoEdge = edge; + pseudoEdge = edge; + pseudoEdgeWeight = edge->m_weight; continue; } + successorWeight += edge->m_weight; nEdges++; } @@ -3890,28 +3894,25 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, assert(nEdges == nSucc); - if (info->m_weight == BB_ZERO_WEIGHT) + if ((info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) { - JITDUMP("\nPropagate: OSR entry block weight is zero\n"); + JITDUMP("\nPropagate: OSR entry block or successor weight is zero\n"); EntryWeightZero(); return; } // Transfer model edge weight onto the FlowEdges as likelihoods. // - assert(nEdges == nSucc); - weight_t totalLikelihood = 0; + JITDUMP("Normalizing OSR successor likelihoods with factor 1/" FMT_WT "\n", successorWeight); for (Edge* edge = info->m_outgoingEdges; edge != nullptr; edge = edge->m_nextOutgoingEdge) { assert(block == edge->m_sourceBlock); - // The pseudo edge doesn't correspond to a flow edge, - // but it carries away some flow. + // The pseudo edge doesn't correspond to a flow edge. // if (edge == pseudoEdge) { - totalLikelihood += edge->m_weight / info->m_weight; continue; } @@ -3927,51 +3928,19 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, if (nEdges == 1) { - // Conceptually we could assert(edge->m_weight == info->m_weight); - // but we can have inconsistencies. - // // Go with what we know for sure, edge should be 100% likely. // likelihood = 1.0; JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (uniq)\n", block->bbNum, edge->m_targetBlock->bbNum, likelihood); flowEdge->setLikelihood(likelihood); - totalLikelihood += likelihood; break; } - assert(info->m_weight != BB_ZERO_WEIGHT); - - // We may see nonsensical weights here, cap likelihood. - // - bool capped = false; - if (edge->m_weight > info->m_weight) - { - capped = true; - likelihood = 1.0; - } - else - { - likelihood = edge->m_weight / info->m_weight; - } - JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (%s)\n", block->bbNum, - edge->m_targetBlock->bbNum, likelihood, capped ? "pgo -- capped" : "pgo"); + likelihood = edge->m_weight / successorWeight; + JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (pgo)\n", block->bbNum, + edge->m_targetBlock->bbNum, likelihood); flowEdge->setLikelihood(likelihood); - totalLikelihood += likelihood; - } - - // Note we expect real flow imbalances here as it's likely there - // was no observed flow from the OSR entry to some of its successors. - // Since we added in the pseudo edge likelihood above, the check below - // probably won't flag this. - // - // Seems like for OSR we will always want to run synthesis/repair. - // - if (totalLikelihood != 1.0) - { - // Consider what to do here... flag this method as needing immediate profile repairs? - // - JITDUMP(FMT_BB " total outgoing likelihood inaccurate: " FMT_WT "\n", block->bbNum, totalLikelihood); } } @@ -3984,10 +3953,6 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, // info - model info for the block // nSucc - number of successors of the block in the flow graph // -// Notes: -// This block requires special handling because original method flow -// was interrupted here. -// void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInfo* info, unsigned nSucc) { // There is at least one FlowEdge. @@ -3995,8 +3960,9 @@ void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInf // Check the reconstruction graph edges. For normal blocks, if we have // any pseudo-edges there should be only one pseudo-edge, and no regular edges. // - Edge* pseudoEdge = nullptr; - unsigned nEdges = 0; + Edge* pseudoEdge = nullptr; + unsigned nEdges = 0; + weight_t successorWeight = BB_ZERO_WEIGHT; for (Edge* edge = info->m_outgoingEdges; edge != nullptr; edge = edge->m_nextOutgoingEdge) { @@ -4007,6 +3973,7 @@ void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInf continue; } + successorWeight += edge->m_weight; nEdges++; } @@ -4046,7 +4013,7 @@ void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInf // // (TODO: use synthesis here) // - if ((nEdges != nSucc) || (info->m_weight == BB_ZERO_WEIGHT)) + if ((nEdges != nSucc) || (info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) { JITDUMP(FMT_BB " %s , setting outgoing likelihoods heuristically\n", block->bbNum, (nEdges != nSucc) ? "has inaccurate flow model" : "has zero weight"); @@ -4067,7 +4034,7 @@ void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInf // Transfer model edge weight onto the FlowEdges as likelihoods. // assert(nEdges == nSucc); - weight_t totalLikelihood = 0; + JITDUMP("Normalizing successor likelihoods with factor 1/" FMT_WT "\n", successorWeight); for (Edge* edge = info->m_outgoingEdges; edge != nullptr; edge = edge->m_nextOutgoingEdge) { @@ -4079,45 +4046,17 @@ void EfficientEdgeCountReconstructor::PropagateEdges(BasicBlock* block, BlockInf if (nEdges == 1) { assert(nSucc == 1); - - // Conceptually we could assert(edge->m_weight == info->m_weight); - // but we can have inconsistencies. - // - // Go with what we know for sure, edge should be 100% likely. - // likelihood = 1.0; JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (uniq)\n", block->bbNum, edge->m_targetBlock->bbNum, likelihood); flowEdge->setLikelihood(likelihood); - totalLikelihood += likelihood; break; } - assert(info->m_weight != BB_ZERO_WEIGHT); - - // We may see nonsensical weights here, cap likelihood. - // - bool capped = false; - if (edge->m_weight > info->m_weight) - { - capped = true; - likelihood = 1.0; - } - else - { - likelihood = edge->m_weight / info->m_weight; - } - JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (%s)\n", block->bbNum, - edge->m_targetBlock->bbNum, likelihood, capped ? "pgo -- capped" : "pgo"); + likelihood = edge->m_weight / successorWeight; + JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (pgo)\n", block->bbNum, + edge->m_targetBlock->bbNum, likelihood); flowEdge->setLikelihood(likelihood); - totalLikelihood += likelihood; - } - - if (totalLikelihood != 1.0) - { - // Consider what to do here... flag this method as needing immediate profile repairs? - // - JITDUMP(FMT_BB " total outgoing likelihood inaccurate: " FMT_WT "\n", block->bbNum, totalLikelihood); } } @@ -5296,7 +5235,8 @@ void Compiler::fgDebugCheckProfileWeights() } else { - ProfileChecks checks = ProfileChecks::CHECK_HASLIKELIHOOD | ProfileChecks::RAISE_ASSERT; + ProfileChecks checks = + ProfileChecks::CHECK_HASLIKELIHOOD | ProfileChecks::CHECK_LIKELIHOODSUM | ProfileChecks::RAISE_ASSERT; fgDebugCheckProfileWeights(checks); } } @@ -5328,6 +5268,7 @@ void Compiler::fgDebugCheckProfileWeights(ProfileChecks checks) const bool verifyClassicWeights = fgEdgeWeightsComputed && hasFlag(checks, ProfileChecks::CHECK_CLASSIC); const bool verifyLikelyWeights = hasFlag(checks, ProfileChecks::CHECK_LIKELY); const bool verifyHasLikelihood = hasFlag(checks, ProfileChecks::CHECK_HASLIKELIHOOD); + const bool verifyLikelihoodSum = hasFlag(checks, ProfileChecks::CHECK_LIKELIHOODSUM); const bool assertOnFailure = hasFlag(checks, ProfileChecks::RAISE_ASSERT); const bool checkAllBlocks = hasFlag(checks, ProfileChecks::CHECK_ALL_BLOCKS); @@ -5478,6 +5419,10 @@ void Compiler::fgDebugCheckProfileWeights(ProfileChecks checks) JITDUMP("Profile is self-consistent (%d profiled blocks, %d unprofiled)\n", profiledBlocks, unprofiledBlocks); } + else if (verifyLikelihoodSum) + { + JITDUMP("All block successor flow edge likelihoods sum to 1.0\n"); + } else if (verifyHasLikelihood) { JITDUMP("All flow edges have likelihoods\n"); @@ -5618,10 +5563,10 @@ bool Compiler::fgDebugCheckIncomingProfileData(BasicBlock* block, ProfileChecks bool Compiler::fgDebugCheckOutgoingProfileData(BasicBlock* block, ProfileChecks checks) { const bool verifyClassicWeights = fgEdgeWeightsComputed && hasFlag(checks, ProfileChecks::CHECK_CLASSIC); - const bool verifyLikelyWeights = hasFlag(checks, ProfileChecks::CHECK_LIKELY); const bool verifyHasLikelihood = hasFlag(checks, ProfileChecks::CHECK_HASLIKELIHOOD); + const bool verifyLikelihoodSum = hasFlag(checks, ProfileChecks::CHECK_LIKELIHOODSUM); - if (!(verifyClassicWeights || verifyLikelyWeights || verifyHasLikelihood)) + if (!(verifyClassicWeights || verifyHasLikelihood || verifyLikelihoodSum)) { return true; } @@ -5711,7 +5656,7 @@ bool Compiler::fgDebugCheckOutgoingProfileData(BasicBlock* block, ProfileChecks } } - if (verifyLikelyWeights) + if (verifyLikelihoodSum) { if (!fgProfileWeightsConsistent(outgoingLikelihood, 1.0)) { diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 68812364a456b..8e429196c873c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -7276,6 +7276,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) // to set BBF_NONE_QUIRK block->SetFlags(BBF_NONE_QUIRK); } + + FlowEdge* const edge = fgGetPredForBlock(block->GetTarget(), block); + edge->setLikelihood(1.0); } break; @@ -7537,6 +7540,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { // transform the basic block into a BBJ_ALWAYS block->SetKindAndTargetEdge(BBJ_ALWAYS, curEdge); + curEdge->setLikelihood(1.0); foundVal = true; } else diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index 0e98b7d44b472..1a295384f464c 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -603,25 +603,25 @@ class IndirectCallTransformer checkBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, thenBlock); checkFallsThrough = false; - // Calculate the total likelihood for this check as a sum of likelihoods - // of all previous candidates (thenBlocks) - unsigned checkLikelihood = 100; - for (uint8_t previousCandidate = 0; previousCandidate < checkIdx; previousCandidate++) - { - checkLikelihood -= origCall->GetGDVCandidateInfo(previousCandidate)->likelihood; - } + // We computed the "then" likelihood in CreateThen, so we + // just use that to figure out the "else" likelihood. + // + assert(prevCheckBlock->KindIs(BBJ_ALWAYS)); + assert(prevCheckBlock->JumpsToNext()); + FlowEdge* const prevCheckThenEdge = prevCheckBlock->GetTargetEdge(); + assert(prevCheckThenEdge->hasLikelihood()); + weight_t checkLikelihood = max(0.0, 1.0 - prevCheckThenEdge->getLikelihood()); - // Make sure we didn't overflow - assert(checkLikelihood <= 100); - weight_t checkLikelihoodWt = ((weight_t)checkLikelihood) / 100.0; + JITDUMP("Level %u Check block " FMT_BB " success likelihood " FMT_WT "\n", checkIdx, checkBlock->bbNum, + checkLikelihood); // prevCheckBlock is expected to jump to this new check (if its type check doesn't succeed) - assert(prevCheckBlock->KindIs(BBJ_ALWAYS)); - assert(prevCheckBlock->JumpsToNext()); - FlowEdge* const checkEdge = compiler->fgAddRefPred(checkBlock, prevCheckBlock); - checkEdge->setLikelihood(checkLikelihoodWt); - checkBlock->inheritWeightPercentage(currBlock, checkLikelihood); - prevCheckBlock->SetCond(checkEdge, prevCheckBlock->GetTargetEdge()); + // + FlowEdge* const prevCheckCheckEdge = compiler->fgAddRefPred(checkBlock, prevCheckBlock); + prevCheckCheckEdge->setLikelihood(checkLikelihood); + checkBlock->inheritWeight(prevCheckBlock); + checkBlock->scaleBBWeight(checkLikelihood); + prevCheckBlock->SetCond(prevCheckCheckEdge, prevCheckThenEdge); } // Find last arg with a side effect. All args with any effect @@ -1020,27 +1020,59 @@ class IndirectCallTransformer { // Compute likelihoods // - unsigned const thenLikelihood = origCall->GetGDVCandidateInfo(checkIdx)->likelihood; - weight_t thenLikelihoodWt = min(((weight_t)thenLikelihood) / 100.0, 100.0); - weight_t elseLikelihoodWt = max(1.0 - thenLikelihoodWt, 0.0); + // If this is the first check the likelihood is just the candidate likelihood. + // If there are multiple checks things are a bit more complicated. + // + // Say we had three candidates with likelihoods 0.5, 0.3, and 0.1. + // + // The first one's likelihood is 0.5. + // + // The second one (given that we've already checked the first and failed) + // is (0.3) / (1.0 - 0.5) = 0.6. + // + // The third one is (0.1) / (1.0 - (0.5 + 0.3)) = (0.1)/(0.2) = 0.5 + // + // So to figure out the proper divisor, we start with 1.0 and subtract off each + // preceeding test's likelihood of success. + // + unsigned const thenLikelihood = origCall->GetGDVCandidateInfo(checkIdx)->likelihood; + unsigned baseLikelihood = 0; + + for (uint8_t i = 0; i < checkIdx; i++) + { + baseLikelihood += origCall->GetGDVCandidateInfo(i)->likelihood; + } + assert(baseLikelihood < 100); + baseLikelihood = 100 - baseLikelihood; + + weight_t adjustedThenLikelihood = min(((weight_t)thenLikelihood) / baseLikelihood, 100.0); + JITDUMP("For check in " FMT_BB ": orig likelihood " FMT_WT ", base likelihood " FMT_WT + ", adjusted likelihood " FMT_WT "\n", + checkBlock->bbNum, (weight_t)thenLikelihood / 100.0, (weight_t)baseLikelihood / 100.0, + adjustedThenLikelihood); // thenBlock always jumps to remainderBlock + // thenBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, checkBlock); thenBlock->CopyFlags(currBlock, BBF_SPLIT_GAINED); - thenBlock->inheritWeightPercentage(currBlock, thenLikelihood); + thenBlock->inheritWeight(currBlock); + thenBlock->scaleBBWeight(adjustedThenLikelihood); + FlowEdge* const thenRemainderEdge = compiler->fgAddRefPred(remainderBlock, thenBlock); + thenBlock->SetTargetEdge(thenRemainderEdge); + thenRemainderEdge->setLikelihood(1.0); - // Also, thenBlock has a single pred - last checkBlock + // thenBlock has a single pred - last checkBlock. + // assert(checkBlock->KindIs(BBJ_ALWAYS)); - FlowEdge* const thenEdge = compiler->fgAddRefPred(thenBlock, checkBlock); - thenEdge->setLikelihood(thenLikelihoodWt); - checkBlock->SetTargetEdge(thenEdge); + FlowEdge* const checkThenEdge = compiler->fgAddRefPred(thenBlock, checkBlock); + checkThenEdge->setLikelihood(adjustedThenLikelihood); + checkBlock->SetTargetEdge(checkThenEdge); checkBlock->SetFlags(BBF_NONE_QUIRK); assert(checkBlock->JumpsToNext()); - FlowEdge* const elseEdge = compiler->fgAddRefPred(remainderBlock, thenBlock); - elseEdge->setLikelihood(elseLikelihoodWt); - thenBlock->SetTargetEdge(elseEdge); - + // We will set the "else edge" likelihood in CreateElse later, + // based on the thenEdge likelihood. + // DevirtualizeCall(thenBlock, checkIdx); } @@ -1049,30 +1081,28 @@ class IndirectCallTransformer // virtual void CreateElse() { - // Calculate the likelihood of the else block as a remainder of the sum - // of all the other likelihoods. - unsigned elseLikelihood = 100; - for (uint8_t i = 0; i < origCall->GetInlineCandidatesCount(); i++) - { - elseLikelihood -= origCall->GetGDVCandidateInfo(i)->likelihood; - } - // Make sure it didn't overflow - assert(elseLikelihood <= 100); - weight_t elseLikelihoodDbl = ((weight_t)elseLikelihood) / 100.0; - elseBlock = CreateAndInsertBasicBlock(BBJ_ALWAYS, thenBlock); elseBlock->CopyFlags(currBlock, BBF_SPLIT_GAINED); elseBlock->SetFlags(BBF_NONE_QUIRK); + // We computed the "then" likelihood in CreateThen, so we + // just use that to figure out the "else" likelihood. + // + assert(checkBlock->KindIs(BBJ_ALWAYS)); + FlowEdge* const checkThenEdge = checkBlock->GetTargetEdge(); + assert(checkThenEdge->hasLikelihood()); + weight_t elseLikelihood = max(0.0, 1.0 - checkThenEdge->getLikelihood()); + // CheckBlock flows into elseBlock unless we deal with the case // where we know the last check is always true (in case of "exact" GDV) + // if (!checkFallsThrough) { assert(checkBlock->KindIs(BBJ_ALWAYS)); assert(checkBlock->JumpsToNext()); - FlowEdge* const checkEdge = compiler->fgAddRefPred(elseBlock, checkBlock); - checkEdge->setLikelihood(elseLikelihoodDbl); - checkBlock->SetCond(checkEdge, checkBlock->GetTargetEdge()); + FlowEdge* const checkElseEdge = compiler->fgAddRefPred(elseBlock, checkBlock); + checkElseEdge->setLikelihood(elseLikelihood); + checkBlock->SetCond(checkElseEdge, checkThenEdge); } else { @@ -1080,17 +1110,23 @@ class IndirectCallTransformer // and is NativeAOT-only, we just assume the unreached block will be removed // by other phases. assert(origCall->gtCallMoreFlags & GTF_CALL_M_GUARDED_DEVIRT_EXACT); + + // Since we're not modifying the check block a BBJ_COND, update the + // then edge likelihood (it should already have the right value, so perhaps assert instead?) + // + checkThenEdge->setLikelihood(1.0); } // elseBlock always flows into remainderBlock - FlowEdge* const elseEdge = compiler->fgAddRefPred(remainderBlock, elseBlock); - elseEdge->setLikelihood(1.0); - elseBlock->SetTargetEdge(elseEdge); + FlowEdge* const elseRemainderEdge = compiler->fgAddRefPred(remainderBlock, elseBlock); + elseRemainderEdge->setLikelihood(1.0); + elseBlock->SetTargetEdge(elseRemainderEdge); // Remove everything related to inlining from the original call origCall->ClearInlineInfo(); - elseBlock->inheritWeightPercentage(currBlock, elseLikelihood); + elseBlock->inheritWeight(checkBlock); + elseBlock->scaleBBWeight(elseLikelihood); GenTreeCall* call = origCall; Statement* newStmt = compiler->gtNewStmt(call, stmt->GetDebugInfo()); @@ -1190,7 +1226,7 @@ class IndirectCallTransformer coldBlock->SetKindAndTargetEdge(BBJ_ALWAYS, newEdge); } - // When the current candidate hads sufficiently high likelihood, scan + // When the current candidate has sufficiently high likelihood, scan // the remainer block looking for another GDV candidate. // // (also consider: if currBlock has sufficiently high execution frequency)