diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 5c41dcd02bcc0..d418101bf7bbd 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -1311,6 +1311,8 @@ struct flowList bool* wbUsedSlop); void setEdgeWeights(BasicBlock::weight_t newMinWeight, BasicBlock::weight_t newMaxWeight, BasicBlock* bDst); + void scaleEdgeWeights(BasicBlock::weight_t scale, BasicBlock* bDst); + flowList(BasicBlock* block, flowList* rest) : flNext(rest), m_block(block), flEdgeWeightMin(0), flEdgeWeightMax(0), flDupCount(0) { diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index d0256fb95141e..8ccf8fb7f1a8e 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -3207,6 +3207,22 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* // `fgUpdateFlowGraph` has been called with `doTailDuplication` set to true, and the // backend always calls `fgUpdateFlowGraph` with `doTailDuplication` set to false. assert(!block->IsLIR()); + + // The optimization doesn't make sense in this case + if (target->bbWeight == 0.0) + { + return false; + } + + // See https://github.com/dotnet/runtime/pull/50490#pullrequestreview-625700642 + // + // A - block + // X - target + // C - target->bbJumpDest + // D - target->bbNext + + const BasicBlock::weight_t scaleA = block->bbWeight / target->bbWeight; + const BasicBlock::weight_t scaleNotA = 1 - scaleA; Statement* stmt = target->FirstNonPhiDef(); assert(stmt == target->lastStmt()); @@ -3230,6 +3246,28 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* fgAddRefPred(next, block); fgAddRefPred(next->bbJumpDest, next); + // weight(X) = weight(X) * ScaleNotA + target->scaleBBWeight(scaleNotA); + + // weight(X->C) = weight(X->C) * ScaleNotA + flowList* xcEdge = fgGetPredForBlock(target->bbJumpDest, target); + xcEdge->scaleEdgeWeights(scaleNotA, target->bbJumpDest); + + // weight(X->D) = weight(X->D) * ScaleNotA + flowList* xdEdge = fgGetPredForBlock(target->bbNext, target); + xdEdge->scaleEdgeWeights(scaleNotA, target->bbNext); + + // weight(A->C) = weight(X->C) * ScaleA + flowList* acEdge = fgGetPredForBlock(target->bbJumpDest, block); + acEdge->setEdgeWeights(xcEdge->edgeWeightMin() * scaleA, xcEdge->edgeWeightMin() * scaleA, target->bbJumpDest); + + // weight(A->T) = weight(X->D) * ScaleA + flowList* atEdge = fgGetPredForBlock(next, block); + atEdge->setEdgeWeights(xdEdge->edgeWeightMin() * scaleA, xdEdge->edgeWeightMin() * scaleA, target->bbNext); + + // weight(T) = weight(X->D) * ScaleA + next->setBBProfileWeight(xdEdge->edgeWeightMax() * scaleA); + JITDUMP("fgOptimizeUncondBranchToSimpleCond(from " FMT_BB " to cond " FMT_BB "), created new uncond " FMT_BB "\n", block->bbNum, target->bbNum, next->bbNum); JITDUMP(" expecting opts to key off V%02u, added cloned compare [%06u] to " FMT_BB "\n", lclNum, diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index 85361a618a9ff..8a5fdf7e1a24a 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -2750,6 +2750,22 @@ void flowList::setEdgeWeights(BasicBlock::weight_t theMinWeight, BasicBlock::wei flEdgeWeightMax = theMaxWeight; } +//------------------------------------------------------------------------ +// scaleEdgeWeights: Scale both min and max weights by some factor +// +// Arguments: +// scale - scale to apply +// bDst - the destination block for the edge +// +void flowList::scaleEdgeWeights(BasicBlock::weight_t scale, BasicBlock* bDst) +{ + JITDUMP("Scalling edge weights for " FMT_BB " -> " FMT_BB " by " FMT_WT "\n", getBlock()->bbNum, bDst->bbNum, + scale); + + flEdgeWeightMin *= scale; + flEdgeWeightMax *= scale; +} + //------------------------------------------------------------- // fgComputeBlockAndEdgeWeights: determine weights for blocks // and optionally for edges