diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 01e137432ecf4..c3b98c8f80ebc 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -843,8 +843,7 @@ void BasicBlock::CopyTarget(Compiler* compiler, const BasicBlock* from) SetEhf(new (compiler, CMK_BasicBlock) BBehfDesc(compiler, from->GetEhfTargets())); break; case BBJ_COND: - // TODO-NoFallThrough: Copy false target, too? - SetCond(from->GetTrueTarget(), Next()); + SetCond(from->GetTrueTarget(), from->GetFalseTarget()); break; case BBJ_ALWAYS: SetKindAndTarget(from->GetKind(), from->GetTarget()); @@ -885,8 +884,7 @@ void BasicBlock::TransferTarget(BasicBlock* from) from->bbEhfTargets = nullptr; // Make sure nobody uses the descriptor after this. break; case BBJ_COND: - // TODO-NoFallThrough: Copy false target, too? - SetCond(from->GetTrueTarget(), Next()); + SetCond(from->GetTrueTarget(), from->GetFalseTarget()); break; case BBJ_ALWAYS: SetKindAndTarget(from->GetKind(), from->GetTarget()); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index bd8cd590eea53..97b56394b5357 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5134,6 +5134,29 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl StackLevelSetter stackLevelSetter(this); stackLevelSetter.Run(); + // Block layout should not change at this point. + // Do one last pass to reverse any conditions that might yield more fall-through branches. + if (opts.OptimizationEnabled()) + { + for (BasicBlock* const block : Blocks()) + { + if (block->KindIs(BBJ_COND) && block->CanRemoveJumpToTarget(block->GetTrueTarget(), this)) + { + // Reverse the jump condition + GenTree* test = block->lastNode(); + assert(test->OperIsConditionalJump()); + GenTree* cond = gtReverseCond(test); + assert(cond == test); // Ensure `gtReverseCond` did not create a new node. + + BasicBlock* newFalseTarget = block->GetTrueTarget(); + BasicBlock* newTrueTarget = block->GetFalseTarget(); + block->SetTrueTarget(newTrueTarget); + block->SetFalseTarget(newFalseTarget); + assert(block->CanRemoveJumpToTarget(newFalseTarget, this)); + } + } + } + // We can not add any new tracked variables after this point. lvaTrackedFixed = true; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 0f979e4700938..f0a39b14f265b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5919,8 +5919,6 @@ class Compiler void fgCompactBlocks(BasicBlock* block, BasicBlock* bNext); - BasicBlock* fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, bool noFallThroughQuirk = false); - bool fgRenumberBlocks(); bool fgExpandRarelyRunBlocks(); @@ -6773,7 +6771,7 @@ class Compiler bool optCompactLoop(FlowGraphNaturalLoop* loop); BasicBlock* optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top); BasicBlock* optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* insertionPoint, BasicBlock* top, BasicBlock* bottom); - bool optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext); + bool optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* newNext); bool optCreatePreheader(FlowGraphNaturalLoop* loop); void optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 3b768ab43e7d1..19900e5a133bf 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -4820,8 +4820,7 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr) curr->RemoveFlags(BBF_HAS_JMP | BBF_RETLESS_CALL); // Transfer the kind and target. Do this after the code above, to avoid null-ing out the old targets used by the - // above code (and so newBlock->bbNext is valid, so SetCond() can initialize bbFalseTarget if newBlock is a - // BBJ_COND). + // above code. newBlock->TransferTarget(curr); // Default to fallthrough, and add the arc for that. @@ -5222,7 +5221,7 @@ void Compiler::fgUnlinkRange(BasicBlock* bBeg, BasicBlock* bEnd) // // Arguments: // block - the block to remove -// unreachable - the block to remove +// unreachable - indicates whether removal is because block is unreachable or empty // // Return Value: // The block after the block, or blocks, removed. @@ -5272,7 +5271,7 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) // If we delete such a BBJ_CALLFINALLY we also delete the BBJ_CALLFINALLYRET. if (block->isBBCallFinallyPair()) { - BasicBlock* const leaveBlock = block->Next(); + BasicBlock* const leaveBlock = bNext; bNext = leaveBlock->Next(); fgPrepareCallFinallyRetForRemoval(leaveBlock); fgRemoveBlock(leaveBlock, /* unreachable */ true); @@ -5292,12 +5291,6 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable) /* At this point the bbPreds and bbRefs had better be zero */ noway_assert((block->bbRefs == 0) && (block->bbPreds == nullptr)); - - if (bPrev->KindIs(BBJ_COND) && bPrev->FalseTargetIs(block)) - { - assert(bNext != nullptr); - bPrev->SetFalseTarget(bNext); - } } else // block is empty { @@ -5498,102 +5491,6 @@ void Compiler::fgPrepareCallFinallyRetForRemoval(BasicBlock* block) block->SetKind(BBJ_ALWAYS); } -//------------------------------------------------------------------------ -// fgConnectFallThrough: fix flow from a block that previously had a fall through -// -// Arguments: -// bSrc - source of fall through -// bDst - target of fall through -// -// Returns: -// Newly inserted block after bSrc that jumps to bDst, -// or nullptr if bSrc already falls through to bDst -// -BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst, bool noFallThroughQuirk /* = false */) -{ - assert(bSrc != nullptr); - assert(fgPredsComputed); - BasicBlock* jmpBlk = nullptr; - - /* If bSrc falls through to a block that is not bDst, we will insert a jump to bDst */ - - if (bSrc->KindIs(BBJ_COND) && bSrc->FalseTargetIs(bDst) && !bSrc->NextIs(bDst)) - { - assert(fgGetPredForBlock(bDst, bSrc) != nullptr); - - // Allow the caller to decide whether to use the old logic of maintaining implicit fallthrough behavior, - // or to allow BBJ_COND blocks' false targets to diverge from bbNext. - // TODO-NoFallThrough: Remove this quirk once callers' dependencies on implicit fallthrough are gone. - if (noFallThroughQuirk) - { - return jmpBlk; - } - - // Add a new block after bSrc which jumps to 'bDst' - jmpBlk = fgNewBBafter(BBJ_ALWAYS, bSrc, true, bDst); - bSrc->SetFalseTarget(jmpBlk); - fgAddRefPred(jmpBlk, bSrc, fgGetPredForBlock(bDst, bSrc)); - - // When adding a new jmpBlk we will set the bbWeight and bbFlags - // - if (fgHaveValidEdgeWeights && fgHaveProfileWeights()) - { - FlowEdge* const newEdge = fgGetPredForBlock(jmpBlk, bSrc); - - jmpBlk->bbWeight = (newEdge->edgeWeightMin() + newEdge->edgeWeightMax()) / 2; - if (bSrc->bbWeight == BB_ZERO_WEIGHT) - { - jmpBlk->bbWeight = BB_ZERO_WEIGHT; - } - - if (jmpBlk->bbWeight == BB_ZERO_WEIGHT) - { - jmpBlk->SetFlags(BBF_RUN_RARELY); - } - - weight_t weightDiff = (newEdge->edgeWeightMax() - newEdge->edgeWeightMin()); - weight_t slop = BasicBlock::GetSlopFraction(bSrc, bDst); - // - // If the [min/max] values for our edge weight is within the slop factor - // then we will set the BBF_PROF_WEIGHT flag for the block - // - if (weightDiff <= slop) - { - jmpBlk->SetFlags(BBF_PROF_WEIGHT); - } - } - else - { - // We set the bbWeight to the smaller of bSrc->bbWeight or bDst->bbWeight - if (bSrc->bbWeight < bDst->bbWeight) - { - jmpBlk->bbWeight = bSrc->bbWeight; - jmpBlk->CopyFlags(bSrc, BBF_RUN_RARELY); - } - else - { - jmpBlk->bbWeight = bDst->bbWeight; - jmpBlk->CopyFlags(bDst, BBF_RUN_RARELY); - } - } - - fgReplacePred(bDst, bSrc, jmpBlk); - - JITDUMP("Added an unconditional jump to " FMT_BB " after block " FMT_BB "\n", jmpBlk->GetTarget()->bbNum, - bSrc->bbNum); - } - else if (bSrc->KindIs(BBJ_ALWAYS) && bSrc->HasInitializedTarget() && bSrc->JumpsToNext()) - { - bSrc->SetFlags(BBF_NONE_QUIRK); - } - else if (bSrc->KindIs(BBJ_COND) && bSrc->NextIs(bDst)) - { - bSrc->SetFalseTarget(bDst); - } - - return jmpBlk; -} - //------------------------------------------------------------------------ // fgRenumberBlocks: update block bbNums to reflect bbNext order // @@ -6109,11 +6006,15 @@ BasicBlock* Compiler::fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE r // We have decided to insert the block(s) after fgLastBlock fgMoveBlocksAfter(bStart, bLast, insertAfterBlk); - // If bPrev falls through, we will insert a jump to block - fgConnectFallThrough(bPrev, bStart); + if (bPrev->KindIs(BBJ_ALWAYS) && bPrev->JumpsToNext()) + { + bPrev->SetFlags(BBF_NONE_QUIRK); + } - // If bLast falls through, we will insert a jump to bNext - fgConnectFallThrough(bLast, bNext); + if (bLast->KindIs(BBJ_ALWAYS) && bLast->JumpsToNext()) + { + bLast->SetFlags(BBF_NONE_QUIRK); + } #endif // !FEATURE_EH_FUNCLETS @@ -7004,9 +6905,6 @@ BasicBlock* Compiler::fgNewBBinRegionWorker(BBKinds jumpKind, } } - /* If afterBlk falls through, we insert a jump around newBlk */ - fgConnectFallThrough(afterBlk, newBlk->Next()); - #ifdef DEBUG fgVerifyHandlerTab(); #endif diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 25afba806486a..89b8ad010aebd 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -895,7 +895,7 @@ PhaseStatus Compiler::fgPostImportationCleanup() // fgCanCompactBlocks: Determine if a block and its bbNext successor can be compacted. // // Arguments: -// block - block to check. If nullptr, return false. +// block - block to check. Cannot be nullptr. // bNext - bbNext of `block`. If nullptr, return false. // // Returns: @@ -903,11 +903,7 @@ PhaseStatus Compiler::fgPostImportationCleanup() // bool Compiler::fgCanCompactBlocks(BasicBlock* block, BasicBlock* bNext) { - if ((block == nullptr) || (bNext == nullptr)) - { - return false; - } - + assert(block != nullptr); assert(block->NextIs(bNext)); if (!block->KindIs(BBJ_ALWAYS) || !block->TargetIs(bNext) || block->HasFlag(BBF_KEEP_BBJ_ALWAYS)) @@ -1579,7 +1575,16 @@ bool Compiler::fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBloc break; case BBJ_COND: - block->SetTrueTarget(bDest->GetTarget()); + if (block->TrueTargetIs(bDest)) + { + assert(!block->FalseTargetIs(bDest)); + block->SetTrueTarget(bDest->GetTarget()); + } + else + { + assert(block->FalseTargetIs(bDest)); + block->SetFalseTarget(bDest->GetTarget()); + } break; default: @@ -2482,25 +2487,14 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* fgInsertStmtAtEnd(block, cloneStmt); } - // add an unconditional block after this block to jump to the target block's fallthrough block - // - assert(!target->IsLast()); - BasicBlock* next = fgNewBBafter(BBJ_ALWAYS, block, true, target->GetFalseTarget()); - // Fix up block's flow // - block->SetCond(target->GetTrueTarget(), next); + block->SetCond(target->GetTrueTarget(), target->GetFalseTarget()); fgAddRefPred(block->GetTrueTarget(), block); + fgAddRefPred(block->GetFalseTarget(), block); fgRemoveRefPred(target, block); - // The new block 'next' will inherit its weight from 'block' - // - next->inheritWeight(block); - fgAddRefPred(next, block); - fgAddRefPred(next->GetTarget(), next); - - JITDUMP("fgOptimizeUncondBranchToSimpleCond(from " FMT_BB " to cond " FMT_BB "), created new uncond " FMT_BB "\n", - block->bbNum, target->bbNum, next->bbNum); + JITDUMP("fgOptimizeUncondBranchToSimpleCond(from " FMT_BB " to cond " FMT_BB ")\n", block->bbNum, target->bbNum); JITDUMP(" expecting opts to key off V%02u in " FMT_BB "\n", lclNum, block->bbNum); return true; @@ -3577,7 +3571,6 @@ bool Compiler::fgReorderBlocks(bool useProfile) } else if (bPrev->KindIs(BBJ_COND)) { - assert(bPrev->FalseTargetIs(block) || useProfile); bDest = bPrev->GetTrueTarget(); bFalseDest = bPrev->GetFalseTarget(); // TODO: Cheaper to call fgRenumberBlocks first and compare bbNums? @@ -4645,35 +4638,26 @@ bool Compiler::fgReorderBlocks(bool useProfile) /* We have decided to insert the block(s) after 'insertAfterBlk' */ fgMoveBlocksAfter(bStart, bEnd, insertAfterBlk); - // useProfile should be true only when finalizing the block layout in Compiler::optOptimizeLayout. - // In this final pass, allow BBJ_COND blocks' false targets to diverge from bbNext. - // TODO-NoFallThrough: Always allow the false targets to diverge. - if (bDest) + if (bPrev->KindIs(BBJ_ALWAYS) && bPrev->JumpsToNext()) { - /* We may need to insert an unconditional branch after bPrev to bDest */ - fgConnectFallThrough(bPrev, bDest, /* noFallThroughQuirk */ useProfile); + bPrev->SetFlags(BBF_NONE_QUIRK); } - else + + if (bEnd->KindIs(BBJ_ALWAYS) && bEnd->JumpsToNext()) { - /* If bPrev falls through, we must insert a jump to block */ - fgConnectFallThrough(bPrev, block, /* noFallThroughQuirk */ useProfile); + bEnd->SetFlags(BBF_NONE_QUIRK); } - BasicBlock* bSkip = bEnd->Next(); - - /* If bEnd falls through, we must insert a jump to bNext */ - fgConnectFallThrough(bEnd, bNext, /* noFallThroughQuirk */ useProfile); - if (bStart2 == nullptr) { - /* If insertAfterBlk falls through, we are forced to */ - /* add a jump around the block(s) we just inserted */ - fgConnectFallThrough(insertAfterBlk, bSkip, /* noFallThroughQuirk */ useProfile); + if (insertAfterBlk->KindIs(BBJ_ALWAYS) && insertAfterBlk->JumpsToNext()) + { + insertAfterBlk->SetFlags(BBF_NONE_QUIRK); + } } - else + else if (bPrev2->KindIs(BBJ_ALWAYS) && bPrev2->JumpsToNext()) { - /* We may need to insert an unconditional branch after bPrev2 to bStart */ - fgConnectFallThrough(bPrev2, bStart, /* noFallThroughQuirk */ useProfile); + bPrev2->SetFlags(BBF_NONE_QUIRK); } #if DEBUG @@ -4832,9 +4816,8 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) change = true; modified = true; bDest = block->GetTrueTarget(); - bNext = block->GetFalseTarget(); - // TODO-NoFallThrough: Adjust the above logic once bbFalseTarget can diverge from bbNext + // Optimization should not have inserted a new block assert(block->NextIs(bNext)); } } @@ -4860,12 +4843,32 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) BasicBlock* bFalseDest = block->GetFalseTarget(); assert(bFalseDest != nullptr); - if (bFalseDest->KindIs(BBJ_ALWAYS) && bFalseDest->TargetIs(bDest) && bFalseDest->isEmpty()) + if (bFalseDest->KindIs(BBJ_ALWAYS) && bFalseDest->isEmpty()) { - // Optimize bFalseDest -> BBJ_ALWAYS -> bDest - block->SetFalseTarget(bDest); - fgRemoveRefPred(bFalseDest, block); - fgAddRefPred(bDest, block); + if (bFalseDest->TargetIs(bDest)) + { + // Optimize bFalseDest -> BBJ_ALWAYS -> bDest + block->SetFalseTarget(bDest); + fgRemoveRefPred(bFalseDest, block); + fgAddRefPred(bDest, block); + change = true; + modified = true; + } + else if (bFalseDest->TargetIs(bNext) && (bFalseDest != bNext)) // special case for self jumps + { + // Enable fallthrough into false target + block->SetFalseTarget(bNext); + fgRemoveRefPred(bFalseDest, block); + fgAddRefPred(bNext, block); + change = true; + modified = true; + } + + if ((bFalseDest->countOfInEdges() == 0) && !bFalseDest->HasFlag(BBF_DONT_REMOVE)) + { + assert(change && modified); + fgRemoveBlock(bFalseDest, /* unreachable */ true); + } } else if (bDest->KindIs(BBJ_ALWAYS) && bDest->TargetIs(bFalseDest) && bDest->isEmpty()) { @@ -4873,10 +4876,19 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) block->SetTrueTarget(bFalseDest); fgRemoveRefPred(bDest, block); fgAddRefPred(bFalseDest, block); + change = true; + modified = true; + + if ((bDest->countOfInEdges() == 0) && !bDest->HasFlag(BBF_DONT_REMOVE)) + { + fgRemoveBlock(bDest, /* unreachable */ true); + } + bDest = bFalseDest; } - if (block->FalseTargetIs(bDest)) + // Above transformations may convert block into a BBJ_ALWAYS + if (block->KindIs(BBJ_COND) && block->FalseTargetIs(bDest)) { fgRemoveConditionalJump(block); change = true; @@ -4884,6 +4896,15 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) assert(block->KindIs(BBJ_ALWAYS)); assert(block->TargetIs(bDest)); } + else if (block->KindIs(BBJ_ALWAYS)) + { + assert(change && modified); + assert(block->TargetIs(bDest)); + } + + // We might have removed previous/next block, so update those pointers here + bPrev = block->Prev(); + bNext = block->Next(); } if (bDest != nullptr) @@ -4902,35 +4923,33 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } } + BasicBlock* bFalseDest = block->KindIs(BBJ_COND) ? block->GetFalseTarget() : nullptr; + // Check for cases where reversing the branch condition may enable // other flow opts. // - // Current block falls through to an empty bNext BBJ_ALWAYS, and - // (a) block jump target is bNext's bbNext. + // Current block jumps to an empty bFalseDest BBJ_ALWAYS, and + // (a) block jump target is bFalseDest's bbNext. // (b) block jump target is elsewhere but join free, and - // bNext's jump target has a join. + // bFalseDest's jump target has a join. // - if (block->KindIs(BBJ_COND) && // block is a BBJ_COND block - (bNext != nullptr) && // block isn't the last block - block->FalseTargetIs(bNext) && // block falls into its false target - (bNext->bbRefs == 1) && // No other block jumps to bNext - bNext->KindIs(BBJ_ALWAYS) && // The next block is a BBJ_ALWAYS block - !bNext->JumpsToNext() && // and it doesn't jump to the next block (we might compact them) - bNext->isEmpty() && // and it is an empty block - !bNext->TargetIs(bNext) && // special case for self jumps + if ((bFalseDest != nullptr) && // block is a BBJ_COND block + (bFalseDest->bbRefs == 1) && // No other block jumps to bFalseDest + bFalseDest->KindIs(BBJ_ALWAYS) && // The next block is a BBJ_ALWAYS block + !bFalseDest->JumpsToNext() && // and it doesn't jump to the next block (we might compact them) + bFalseDest->isEmpty() && // and it is an empty block + !bFalseDest->TargetIs(bFalseDest) && // special case for self jumps !bDest->IsFirstColdBlock(this) && !fgInDifferentRegions(block, bDest)) // do not cross hot/cold sections { - assert(!block->IsLast()); - // case (a) // - const bool isJumpAroundEmpty = bNext->NextIs(bDest); + const bool isJumpAroundEmpty = bFalseDest->NextIs(bDest); // case (b) // // Note the asymmetric checks for refs == 1 and refs > 1 ensures that we - // differentiate the roles played by bDest and bNextJumpDest. We need some + // differentiate the roles played by bDest and bFalseDestTarget. We need some // sense of which arrangement is preferable to avoid getting stuck in a loop // reversing and re-reversing. // @@ -4941,9 +4960,9 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) // * don't consider lexical predecessors, or we may confuse loop recognition // * don't consider blocks of different rarities // - BasicBlock* const bNextJumpDest = bNext->GetTarget(); + BasicBlock* const bFalseDestTarget = bFalseDest->GetTarget(); const bool isJumpToJoinFree = !isJumpAroundEmpty && (bDest->bbRefs == 1) && - (bNextJumpDest->bbRefs > 1) && (bDest->bbNum > block->bbNum) && + (bFalseDestTarget->bbRefs > 1) && (bDest->bbNum > block->bbNum) && (block->isRunRarely() == bDest->isRunRarely()); bool optimizeJump = isJumpAroundEmpty || isJumpToJoinFree; @@ -4956,9 +4975,9 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) optimizeJump = false; } - // Also consider bNext's try region + // Also consider bFalseDest's try region // - if (bNext->hasTryIndex() && !BasicBlock::sameTryRegion(block, bNext)) + if (bFalseDest->hasTryIndex() && !BasicBlock::sameTryRegion(block, bFalseDest)) { optimizeJump = false; } @@ -4977,38 +4996,23 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } } - // TODO-NoFallThrough: Remove this requirement - if (bDest->KindIs(BBJ_COND) && !bDest->NextIs(bDest->GetFalseTarget())) - { - optimizeJump = false; - } - if (optimizeJump && isJumpToJoinFree) { - // In the join free case, we also need to move bDest right after bNext + // In the join free case, we also need to move bDest right after bFalseDest // to create same flow as in the isJumpAroundEmpty case. // - if (!fgEhAllowsMoveBlock(bNext, bDest) || bDest->isBBCallFinallyPair()) + if (!fgEhAllowsMoveBlock(bFalseDest, bDest) || bDest->isBBCallFinallyPair()) { optimizeJump = false; } else { - // We don't expect bDest to already be right after bNext. + // We don't expect bDest to already be right after bFalseDest. // - assert(!bNext->NextIs(bDest)); + assert(!bFalseDest->NextIs(bDest)); JITDUMP("\nMoving " FMT_BB " after " FMT_BB " to enable reversal\n", bDest->bbNum, - bNext->bbNum); - - // If bDest can fall through we'll need to create a jump - // block after it too. Remember where to jump to. - // - BasicBlock* const bDestNext = bDest->Next(); - - // Once bbFalseTarget and bbNext can diverge, this assert will hit - // TODO-NoFallThrough: Remove fall-through for BBJ_COND below - assert(!bDest->KindIs(BBJ_COND) || bDest->FalseTargetIs(bDestNext)); + bFalseDest->bbNum); // Move bDest // @@ -5018,24 +5022,11 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } fgUnlinkBlock(bDest); - fgInsertBBafter(bNext, bDest); + fgInsertBBafter(bFalseDest, bDest); - if (ehIsBlockEHLast(bNext)) + if (ehIsBlockEHLast(bFalseDest)) { - ehUpdateLastBlocks(bNext, bDest); - } - - // Add fall through fixup block, if needed. - // - if (bDest->KindIs(BBJ_COND)) - { - BasicBlock* const bFixup = fgNewBBafter(BBJ_ALWAYS, bDest, true, bDestNext); - bDest->SetFalseTarget(bFixup); - bFixup->inheritWeight(bDestNext); - - fgRemoveRefPred(bDestNext, bDest); - fgAddRefPred(bFixup, bDest); - fgAddRefPred(bDestNext, bFixup); + ehUpdateLastBlocks(bFalseDest, bDest); } } } @@ -5044,7 +5035,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) { JITDUMP("\nReversing a conditional jump around an unconditional jump (" FMT_BB " -> " FMT_BB ", " FMT_BB " -> " FMT_BB ")\n", - block->bbNum, bDest->bbNum, bNext->bbNum, bNextJumpDest->bbNum); + block->bbNum, bDest->bbNum, bFalseDest->bbNum, bFalseDestTarget->bbNum); // Reverse the jump condition // @@ -5063,32 +5054,33 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) } // Optimize the Conditional JUMP to go to the new target - assert(block->FalseTargetIs(bNext)); - block->SetTrueTarget(bNext->GetTarget()); - block->SetFalseTarget(bNext->Next()); + assert(block->FalseTargetIs(bFalseDest)); + block->SetTrueTarget(bFalseDest->GetTarget()); + block->SetFalseTarget(bFalseDest->Next()); - fgAddRefPred(bNext->GetTarget(), block, fgRemoveRefPred(bNext->GetTarget(), bNext)); + fgAddRefPred(bFalseDest->GetTarget(), block, + fgRemoveRefPred(bFalseDest->GetTarget(), bFalseDest)); /* - Unlink bNext from the BasicBlock list; note that we can + Unlink bFalseDest from the BasicBlock list; note that we can do this even though other blocks could jump to it - the reason is that elsewhere in this function we always redirect jumps to jumps to jump to the final label, - so even if another block jumps to bNext it won't matter + so even if another block jumps to bFalseDest it won't matter once we're done since any such jump will be redirected to the final target by the time we're done here. */ - fgRemoveRefPred(bNext, block); - fgUnlinkBlockForRemoval(bNext); + fgRemoveRefPred(bFalseDest, block); + fgUnlinkBlockForRemoval(bFalseDest); /* Mark the block as removed */ - bNext->SetFlags(BBF_REMOVED); + bFalseDest->SetFlags(BBF_REMOVED); // If this is the first Cold basic block update fgFirstColdBlock - if (bNext->IsFirstColdBlock(this)) + if (bFalseDest->IsFirstColdBlock(this)) { - fgFirstColdBlock = bNext->Next(); + fgFirstColdBlock = bFalseDest->Next(); } // @@ -5098,7 +5090,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) for (EHblkDsc* const HBtab : EHClauses(this)) { - if ((HBtab->ebdTryLast == bNext) || (HBtab->ebdHndLast == bNext)) + if ((HBtab->ebdTryLast == bFalseDest) || (HBtab->ebdHndLast == bFalseDest)) { fgSkipRmvdBlocks(HBtab); } @@ -5121,11 +5113,11 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) as jumping to REPEAT will cause us to delete 'block' because it currently appears to be unreachable. As it is a self loop that only has a single bbRef (itself) - However since the unlinked bNext has additional bbRefs + However since the unlinked bFalseDest has additional bbRefs (that we will later connect to 'block'), it is not really unreachable. */ - if ((bNext->bbRefs > 0) && bNext->TargetIs(block) && (block->bbRefs == 1)) + if ((bFalseDest->bbRefs > 0) && bFalseDest->TargetIs(block) && (block->bbRefs == 1)) { continue; } diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 79ee2e97881ed..9b4304e56df2b 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4373,10 +4373,9 @@ FlowGraphNaturalLoops* FlowGraphNaturalLoops::Find(const FlowGraphDfsTree* dfsTr for (FlowEdge* const predEdge : loop->m_header->PredEdges()) { BasicBlock* predBlock = predEdge->getSourceBlock(); - if (dfsTree->Contains(predBlock) && !dfsTree->IsAncestor(header, predEdge->getSourceBlock())) + if (dfsTree->Contains(predBlock) && !dfsTree->IsAncestor(header, predBlock)) { - JITDUMP(FMT_BB " -> " FMT_BB " is an entry edge\n", predEdge->getSourceBlock()->bbNum, - loop->m_header->bbNum); + JITDUMP(FMT_BB " -> " FMT_BB " is an entry edge\n", predBlock->bbNum, loop->m_header->bbNum); loop->m_entryEdges.push_back(predEdge); } } @@ -4946,7 +4945,7 @@ bool FlowGraphNaturalLoop::AnalyzeIteration(NaturalLoopIterInfo* info) return false; } - assert(ContainsBlock(cond->GetTrueTarget()) != ContainsBlock(cond->GetFalseTarget())); + // assert(ContainsBlock(cond->GetTrueTarget()) != ContainsBlock(cond->GetFalseTarget())); info->TestBlock = cond; info->IterVar = comp->optIsLoopIncrTree(info->IterTree); diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 730eb91738f67..ed6fddaba85c3 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -2043,25 +2043,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex // bottomNext BasicBlock* bottom = loop->GetLexicallyBottomMostBlock(); BasicBlock* newPred = bottom; - if (bottom->KindIs(BBJ_COND)) - { - // TODO-NoFallThrough: Shouldn't need new BBJ_ALWAYS block once bbFalseTarget can diverge from bbNext - BasicBlock* bottomNext = bottom->Next(); - assert(bottom->FalseTargetIs(bottomNext)); - JITDUMP("Create branch around cloned loop\n"); - BasicBlock* bottomRedirBlk = fgNewBBafter(BBJ_ALWAYS, bottom, /*extendRegion*/ true, bottomNext); - JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", bottomRedirBlk->bbNum, bottom->bbNum); - bottomRedirBlk->bbWeight = bottomRedirBlk->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; - - bottom->SetFalseTarget(bottomRedirBlk); - fgAddRefPred(bottomRedirBlk, bottom); - JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomRedirBlk->bbNum); - fgReplacePred(bottomNext, bottom, bottomRedirBlk); - JITDUMP("Replace " FMT_BB " -> " FMT_BB " with " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomNext->bbNum, - bottomRedirBlk->bbNum, bottomNext->bbNum); - - newPred = bottomRedirBlk; - } // Create a new preheader for the slow loop immediately before the slow // loop itself. All failed conditions will branch to the slow preheader. @@ -2112,37 +2093,6 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex newPred = newBlk; blockMap->Set(blk, newBlk); - // If the block falls through to a block outside the loop then we may - // need to insert a new block to redirect. - // Skip this for the bottom block; we duplicate the slow loop such that - // the bottom block will fall through to the bottom's original next. - if ((blk != bottom) && blk->bbFallsThrough() && !loop->ContainsBlock(blk->Next())) - { - if (blk->KindIs(BBJ_COND)) - { - BasicBlock* targetBlk = blk->GetFalseTarget(); - assert(blk->NextIs(targetBlk)); - - // Need to insert a block. - BasicBlock* newRedirBlk = fgNewBBafter(BBJ_ALWAYS, newPred, /* extendRegion */ true, targetBlk); - newRedirBlk->copyEHRegion(newPred); - newRedirBlk->bbWeight = blk->Next()->bbWeight; - newRedirBlk->CopyFlags(blk->Next(), (BBF_RUN_RARELY | BBF_PROF_WEIGHT)); - newRedirBlk->scaleBBWeight(slowPathWeightScaleFactor); - - JITDUMP(FMT_BB " falls through to " FMT_BB "; inserted redirection block " FMT_BB "\n", blk->bbNum, - blk->Next()->bbNum, newRedirBlk->bbNum); - // This block isn't part of the loop, so below loop won't add - // refs for it. - fgAddRefPred(targetBlk, newRedirBlk); - newPred = newRedirBlk; - } - else - { - assert(!"Cannot handle fallthrough"); - } - } - return BasicBlockVisit::Continue; }); @@ -2165,7 +2115,7 @@ void Compiler::optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* contex // Now redirect the new block according to "blockMap". optRedirectBlock(newblk, blockMap); - // Add predecessor edges for the new successors, as well as the fall-through paths. + // Add predecessor edges for the new successors. switch (newblk->GetKind()) { case BBJ_ALWAYS: diff --git a/src/coreclr/jit/optimizebools.cpp b/src/coreclr/jit/optimizebools.cpp index 7cca29bc7dbfe..fc3c1342a2f06 100644 --- a/src/coreclr/jit/optimizebools.cpp +++ b/src/coreclr/jit/optimizebools.cpp @@ -751,7 +751,7 @@ bool OptBoolsDsc::optOptimizeRangeTests() // BasicBlock* notInRangeBb = m_b1->GetTrueTarget(); BasicBlock* inRangeBb; - if (notInRangeBb == m_b2->GetTrueTarget()) + if (m_b2->TrueTargetIs(notInRangeBb)) { // Shape 1: both conditions jump to NotInRange // @@ -765,7 +765,7 @@ bool OptBoolsDsc::optOptimizeRangeTests() // ... inRangeBb = m_b2->GetFalseTarget(); } - else if (notInRangeBb == m_b2->GetFalseTarget()) + else if (m_b2->FalseTargetIs(notInRangeBb)) { // Shape 2: 2nd block jumps to InRange // @@ -807,11 +807,16 @@ bool OptBoolsDsc::optOptimizeRangeTests() return false; } + // Re-direct firstBlock to jump to inRangeBb m_comp->fgAddRefPred(inRangeBb, m_b1); if (!cmp2IsReversed) { - // Re-direct firstBlock to jump to inRangeBb m_b1->SetTrueTarget(inRangeBb); + m_b1->SetFalseTarget(notInRangeBb); + } + else + { + m_b1->SetFalseTarget(inRangeBb); } // Remove the 2nd condition block as we no longer need it @@ -1015,7 +1020,7 @@ bool OptBoolsDsc::optOptimizeCompareChainCondBlock() m_b2->CopyFlags(m_b1, BBF_COPY_PROPAGATE); // Join the two blocks. This is done now to ensure that additional conditions can be chained. - if (m_comp->fgCanCompactBlocks(m_b1, m_b2)) + if (m_b1->NextIs(m_b2) && m_comp->fgCanCompactBlocks(m_b1, m_b2)) { m_comp->fgCompactBlocks(m_b1, m_b2); } @@ -1887,13 +1892,7 @@ PhaseStatus Compiler::optOptimizeBools() continue; } - // If there is no next block, we're done - BasicBlock* b2 = b1->GetFalseTarget(); - if (b2 == nullptr) - { - break; - } // The next block must not be marked as BBF_DONT_REMOVE if (b2->HasFlag(BBF_DONT_REMOVE)) diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 8e5124745e9e7..b37e418fce9ac 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -570,7 +570,6 @@ void Compiler::optCheckPreds() // predOption - specifies how to update the pred lists // // Notes: -// Fall-through successors are assumed correct and are not modified. // Pred lists for successors of `blk` may be changed, depending on `predOption`. // void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, RedirectBlockOption predOption) @@ -578,11 +577,6 @@ void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, R const bool updatePreds = (predOption == RedirectBlockOption::UpdatePredLists); const bool addPreds = (predOption == RedirectBlockOption::AddToPredLists); - if (addPreds && blk->bbFallsThrough()) - { - fgAddRefPred(blk->Next(), blk); - } - BasicBlock* newJumpDest = nullptr; switch (blk->GetKind()) @@ -595,9 +589,15 @@ void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, R // These have no jump destination to update. break; + case BBJ_CALLFINALLY: + if (addPreds && blk->bbFallsThrough()) + { + fgAddRefPred(blk->Next(), blk); + } + + FALLTHROUGH; case BBJ_ALWAYS: case BBJ_LEAVE: - case BBJ_CALLFINALLY: case BBJ_CALLFINALLYRET: // All of these have a single jump destination to update. if (redirectMap->Lookup(blk->GetTarget(), &newJumpDest)) @@ -636,6 +636,24 @@ void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, R { fgAddRefPred(blk->GetTrueTarget(), blk); } + + // Update jump taken when condition is false + if (redirectMap->Lookup(blk->GetFalseTarget(), &newJumpDest)) + { + if (updatePreds) + { + fgRemoveRefPred(blk->GetFalseTarget(), blk); + } + blk->SetFalseTarget(newJumpDest); + if (updatePreds || addPreds) + { + fgAddRefPred(newJumpDest, blk); + } + } + else if (addPreds) + { + fgAddRefPred(blk->GetFalseTarget(), blk); + } break; case BBJ_EHFINALLYRET: @@ -1583,28 +1601,6 @@ bool Compiler::optTryUnrollLoop(FlowGraphNaturalLoop* loop, bool* changedIR) BasicBlock* exit = loop->ContainsBlock(exiting->GetTrueTarget()) ? exiting->GetFalseTarget() : exiting->GetTrueTarget(); - // If the bottom block falls out of the loop, then insert an - // explicit block to branch around the unrolled iterations we are - // going to create. - if (bottom->KindIs(BBJ_COND)) - { - // TODO-NoFallThrough: Shouldn't need new BBJ_ALWAYS block once bbFalseTarget can diverge from bbNext - BasicBlock* bottomNext = bottom->Next(); - assert(bottom->FalseTargetIs(bottomNext)); - JITDUMP("Create branch around unrolled loop\n"); - BasicBlock* bottomRedirBlk = fgNewBBafter(BBJ_ALWAYS, bottom, /*extendRegion*/ true, bottomNext); - JITDUMP("Adding " FMT_BB " after " FMT_BB "\n", bottomRedirBlk->bbNum, bottom->bbNum); - - bottom->SetFalseTarget(bottomRedirBlk); - fgAddRefPred(bottomRedirBlk, bottom); - JITDUMP("Adding " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomRedirBlk->bbNum); - fgReplacePred(bottomNext, bottom, bottomRedirBlk); - JITDUMP("Replace " FMT_BB " -> " FMT_BB " with " FMT_BB " -> " FMT_BB "\n", bottom->bbNum, bottomNext->bbNum, - bottomRedirBlk->bbNum, bottomNext->bbNum); - - insertAfter = bottomRedirBlk; - } - for (int lval = lbeg; iterToUnroll > 0; iterToUnroll--) { BasicBlock* testBlock = nullptr; @@ -2816,10 +2812,10 @@ bool Compiler::optCompactLoop(FlowGraphNaturalLoop* loop) ehUpdateLastBlocks(insertionPoint, lastNonLoopBlock); // Apply any adjustments needed for fallthrough at the boundaries of the moved region. - changedFlowGraph |= optLoopCompactionFixupFallThrough(insertionPoint, moveBefore, cur); - changedFlowGraph |= optLoopCompactionFixupFallThrough(lastNonLoopBlock, nextLoopBlock, moveBefore); + changedFlowGraph |= optLoopCompactionFixupFallThrough(insertionPoint, cur); + changedFlowGraph |= optLoopCompactionFixupFallThrough(lastNonLoopBlock, moveBefore); // Also apply any adjustments needed where the blocks were snipped out of the loop. - changedFlowGraph |= optLoopCompactionFixupFallThrough(previous, cur, nextLoopBlock); + changedFlowGraph |= optLoopCompactionFixupFallThrough(previous, nextLoopBlock); // Update insertionPoint for the next insertion. insertionPoint = lastNonLoopBlock; @@ -2849,6 +2845,12 @@ BasicBlock* Compiler::optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* BasicBlock* insertionPoint = bottom; while (insertionPoint->bbFallsThrough()) { + if (insertionPoint->KindIs(BBJ_COND) && !insertionPoint->NextIs(insertionPoint->GetFalseTarget())) + { + // Found a BBJ_COND block that does not fall through, so insert after it. + break; + } + // Keep looking for a better insertion point if we can. BasicBlock* newInsertionPoint = optTryAdvanceLoopCompactionInsertionPoint(loop, insertionPoint, top, bottom); if (newInsertionPoint == nullptr) @@ -2935,47 +2937,34 @@ BasicBlock* Compiler::optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNatural // // Parameters: // block - Block that may have fallthrough -// oldNext - The old block that was the fallthrough block // newNext - The new block that was the fallthrough block // // Returns: // True if the flow graph was changed by this function. // -bool Compiler::optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* oldNext, BasicBlock* newNext) +bool Compiler::optLoopCompactionFixupFallThrough(BasicBlock* block, BasicBlock* newNext) { bool changed = false; - if (block->bbFallsThrough()) + if (block->KindIs(BBJ_COND) && block->TrueTargetIs(newNext)) { - // Need to reconnect the flow from `block` to `oldNext`. + // Reverse the jump condition + GenTree* test = block->lastNode(); + noway_assert(test->OperIsConditionalJump()); - if (block->KindIs(BBJ_COND) && (newNext != nullptr) && block->TrueTargetIs(newNext)) + if (test->OperGet() == GT_JTRUE) { - // Reverse the jump condition - GenTree* test = block->lastNode(); - noway_assert(test->OperIsConditionalJump()); - - if (test->OperGet() == GT_JTRUE) - { - GenTree* cond = gtReverseCond(test->AsOp()->gtOp1); - assert(cond == test->AsOp()->gtOp1); // Ensure `gtReverseCond` did not create a new node. - test->AsOp()->gtOp1 = cond; - } - else - { - gtReverseCond(test); - } - - // Redirect the Conditional JUMP to go to `oldNext` - block->SetTrueTarget(oldNext); - block->SetFalseTarget(newNext); + GenTree* cond = gtReverseCond(test->AsOp()->gtOp1); + assert(cond == test->AsOp()->gtOp1); // Ensure `gtReverseCond` did not create a new node. + test->AsOp()->gtOp1 = cond; } else { - // Insert an unconditional jump to `oldNext` just after `block`. - fgConnectFallThrough(block, oldNext); + gtReverseCond(test); } + block->SetTrueTarget(block->GetFalseTarget()); + block->SetFalseTarget(newNext); changed = true; } else if (block->KindIs(BBJ_ALWAYS) && block->TargetIs(newNext)) @@ -3058,7 +3047,7 @@ bool Compiler::optCreatePreheader(FlowGraphNaturalLoop* loop) BasicBlock* preheaderCandidate = loop->EntryEdges()[0]->getSourceBlock(); unsigned candidateEHRegion = preheaderCandidate->hasTryIndex() ? preheaderCandidate->getTryIndex() : EHblkDsc::NO_ENCLOSING_INDEX; - if (preheaderCandidate->KindIs(BBJ_ALWAYS) && (preheaderCandidate->GetUniqueSucc() == loop->GetHeader()) && + if (preheaderCandidate->KindIs(BBJ_ALWAYS) && preheaderCandidate->TargetIs(loop->GetHeader()) && (candidateEHRegion == preheaderEHRegion)) { JITDUMP("Natural loop " FMT_LP " already has preheader " FMT_BB "\n", loop->GetIndex(), @@ -3098,29 +3087,6 @@ bool Compiler::optCreatePreheader(FlowGraphNaturalLoop* loop) fgReplaceJumpTarget(enterBlock, preheader, header); } - // For BBJ_COND blocks preceding header, fgReplaceJumpTarget set their false targets to preheader. - // Direct fallthrough to preheader by inserting a jump after the block. - // TODO-NoFallThrough: Remove this, and just rely on fgReplaceJumpTarget to do the fixup - BasicBlock* fallthroughSource = header->Prev(); - if (fallthroughSource->KindIs(BBJ_COND) && fallthroughSource->FalseTargetIs(preheader)) - { - FlowEdge* edge = fgRemoveRefPred(preheader, fallthroughSource); - BasicBlock* newAlways = fgNewBBafter(BBJ_ALWAYS, fallthroughSource, true, preheader); - fgAddRefPred(preheader, newAlways, edge); - fgAddRefPred(newAlways, fallthroughSource, edge); - - JITDUMP("Created new BBJ_ALWAYS " FMT_BB " to redirect " FMT_BB " to preheader\n", newAlways->bbNum, - fallthroughSource->bbNum); - fallthroughSource->SetFalseTarget(newAlways); - } - - // Make sure false target is set correctly for fallthrough into preheader. - if (!preheader->IsFirst()) - { - fallthroughSource = preheader->Prev(); - assert(!fallthroughSource->KindIs(BBJ_COND) || fallthroughSource->FalseTargetIs(preheader)); - } - optSetPreheaderWeight(loop, preheader); return true; diff --git a/src/coreclr/jit/redundantbranchopts.cpp b/src/coreclr/jit/redundantbranchopts.cpp index 0201a62ddf7b7..4241a30ab579b 100644 --- a/src/coreclr/jit/redundantbranchopts.cpp +++ b/src/coreclr/jit/redundantbranchopts.cpp @@ -1727,12 +1727,11 @@ bool Compiler::optJumpThreadCore(JumpThreadInfo& jti) { Statement* const lastStmt = jti.m_block->lastStmt(); fgRemoveStmt(jti.m_block, lastStmt); - JITDUMP(" repurposing " FMT_BB " to always fall through to " FMT_BB "\n", jti.m_block->bbNum, + JITDUMP(" repurposing " FMT_BB " to always jump to " FMT_BB "\n", jti.m_block->bbNum, jti.m_falseTarget->bbNum); fgRemoveRefPred(jti.m_trueTarget, jti.m_block); jti.m_block->SetKindAndTarget(BBJ_ALWAYS, jti.m_falseTarget); jti.m_block->SetFlags(BBF_NONE_QUIRK); - assert(jti.m_block->JumpsToNext()); } // Now reroute the flow from the predecessors. diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp index d422b6559632c..db97352d5e697 100644 --- a/src/coreclr/jit/stacklevelsetter.cpp +++ b/src/coreclr/jit/stacklevelsetter.cpp @@ -89,29 +89,6 @@ PhaseStatus StackLevelSetter::DoPhase() } } - // The above loop might have moved a BBJ_COND's true target to its next block. - // In such cases, reverse the condition so we can remove a branch. - if (madeChanges) - { - for (BasicBlock* const block : comp->Blocks()) - { - if (block->KindIs(BBJ_COND) && block->CanRemoveJumpToTarget(block->GetTrueTarget(), comp)) - { - // Reverse the jump condition - GenTree* test = block->lastNode(); - assert(test->OperIsConditionalJump()); - GenTree* cond = comp->gtReverseCond(test); - assert(cond == test); // Ensure `gtReverseCond` did not create a new node. - - BasicBlock* newFalseTarget = block->GetTrueTarget(); - BasicBlock* newTrueTarget = block->GetFalseTarget(); - block->SetTrueTarget(newTrueTarget); - block->SetFalseTarget(newFalseTarget); - assert(block->CanRemoveJumpToTarget(newFalseTarget, comp)); - } - } - } - return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; }