From 9e9a1c9c6ccf21b0786a95ec573f9181deeea924 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 23 Apr 2026 14:33:57 -0700 Subject: [PATCH 1/2] JIT: Tolerate edge/successor count mismatch at OSR entry in profile reconstruction When incorporating PGO edge profile data, PropagateOSREntryEdges asserted that the number of model edges (excluding the OSR pseudo-edge) matches the OSR entry block's real successor count. This can fail for the same reason already handled in PropagateEdges: some BBJ_LEAVE targets may be missed during the spanning tree walk, so the reconstructor has no edges for them. Mirror PropagateEdges' behavior: when nEdges != nSucc (or weights are zero), fall back to equal-likelihood heuristics over the real successors instead of asserting. Fixes #126725. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/fgprofile.cpp | 39 ++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 9fba731f33cf06..f1f045e2cc4f78 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -3917,17 +3917,46 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, assert(pseudoEdge != nullptr); } - assert(nEdges == nSucc); - - if ((info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) + // We may not have have the same number of model edges and flow edges. + // + // As in PropagateEdges, this can happen because some BBJ_LEAVE blocks may have + // been missed during our spanning tree walk since we don't know where all the + // finallies can return to just yet (specifically, in WalkSpanningTree, we may + // not add the target of a BBJ_LEAVE to the worklist). Worst case those missed + // blocks dominate other blocks so we can't limit the screening here to specific + // BBJ kinds. + // + // Handle those specially, and also the zero-weight cases, by just assuming + // equally likely successors. + // + // (TODO: use synthesis here) + // + if ((nEdges != nSucc) || (info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) { - JITDUMP("\nPropagate: OSR entry block or successor weight is zero\n"); - EntryWeightZero(); + JITDUMP("\nPropagate: OSR entry block %s, setting outgoing likelihoods heuristically\n", + (nEdges != nSucc) ? "has inaccurate flow model" : "or successor weight is zero"); + + weight_t const equalLikelihood = 1.0 / nSucc; + + for (FlowEdge* const succEdge : block->SuccEdges()) + { + BasicBlock* const succBlock = succEdge->getDestinationBlock(); + JITDUMP("Setting likelihood of " FMT_BB " -> " FMT_BB " to " FMT_WT " (heur)\n", block->bbNum, + succBlock->bbNum, equalLikelihood); + succEdge->setLikelihood(equalLikelihood); + } + + if ((info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) + { + EntryWeightZero(); + } + return; } // Transfer model edge weight onto the FlowEdges as likelihoods. // + assert(nEdges == nSucc); JITDUMP("Normalizing OSR successor likelihoods with factor 1/" FMT_WT "\n", successorWeight); for (Edge* edge = info->m_outgoingEdges; edge != nullptr; edge = edge->m_nextOutgoingEdge) From 50df6b7a68be787094a3145341b1a29ee6a8c166 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 23 Apr 2026 17:13:00 -0700 Subject: [PATCH 2/2] Address review feedback: fix typos and clarify JITDUMP message - 'have have' -> 'have' - 'finallies' -> 'finally blocks' - JITDUMP branch for zero-weight now says 'has zero weight' to match PropagateEdges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/fgprofile.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index f1f045e2cc4f78..a64c33d1403284 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -3917,24 +3917,24 @@ void EfficientEdgeCountReconstructor::PropagateOSREntryEdges(BasicBlock* block, assert(pseudoEdge != nullptr); } - // We may not have have the same number of model edges and flow edges. + // We may not have the same number of model edges and flow edges. // // As in PropagateEdges, this can happen because some BBJ_LEAVE blocks may have // been missed during our spanning tree walk since we don't know where all the - // finallies can return to just yet (specifically, in WalkSpanningTree, we may - // not add the target of a BBJ_LEAVE to the worklist). Worst case those missed - // blocks dominate other blocks so we can't limit the screening here to specific - // BBJ kinds. + // finally blocks can return to just yet (specifically, in WalkSpanningTree, we + // may not add the target of a BBJ_LEAVE to the worklist). Worst case those + // missed blocks dominate other blocks so we can't limit the screening here to + // specific BBJ kinds. // - // Handle those specially, and also the zero-weight cases, by just assuming - // equally likely successors. + // Handle those cases specifically, and also the zero-weight cases, by just + // assuming equally likely successors. // // (TODO: use synthesis here) // if ((nEdges != nSucc) || (info->m_weight == BB_ZERO_WEIGHT) || (successorWeight == BB_ZERO_WEIGHT)) { JITDUMP("\nPropagate: OSR entry block %s, setting outgoing likelihoods heuristically\n", - (nEdges != nSucc) ? "has inaccurate flow model" : "or successor weight is zero"); + (nEdges != nSucc) ? "has inaccurate flow model" : "has zero weight"); weight_t const equalLikelihood = 1.0 / nSucc;