Skip to content

Commit 52e65a5

Browse files
JIT: Remove BBJ_NONE (#94239)
Next step for #93020, per conversation on #93772. Replacing BBJ_NONE with BBJ_ALWAYS to the next block helps limit our use of implicit fall-through (though we still expect BBJ_COND to fall through when its false branch is taken; #93772 should eventually address this). I've added a small peephole optimization to skip emitting unconditional branches to the next block during codegen.
1 parent ee3ca1b commit 52e65a5

30 files changed

+654
-850
lines changed

src/coreclr/jit/block.cpp

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,25 @@ bool BasicBlock::IsFirstColdBlock(Compiler* compiler) const
285285
return (this == compiler->fgFirstColdBlock);
286286
}
287287

288+
//------------------------------------------------------------------------
289+
// CanRemoveJumpToNext: determine if jump to the next block can be omitted
290+
//
291+
// Arguments:
292+
// compiler - current compiler instance
293+
//
294+
// Returns:
295+
// true if the peephole optimization is enabled,
296+
// and block is a BBJ_ALWAYS to the next block that we can fall through into
297+
//
298+
bool BasicBlock::CanRemoveJumpToNext(Compiler* compiler)
299+
{
300+
assert(KindIs(BBJ_ALWAYS));
301+
const bool tryJumpOpt = compiler->opts.OptimizationEnabled() || ((bbFlags & BBF_NONE_QUIRK) != 0);
302+
const bool skipJump = tryJumpOpt && JumpsToNext() && !hasAlign() && ((bbFlags & BBF_KEEP_BBJ_ALWAYS) == 0) &&
303+
!compiler->fgInDifferentRegions(this, bbJumpDest);
304+
return skipJump;
305+
}
306+
288307
//------------------------------------------------------------------------
289308
// checkPredListOrder: see if pred list is properly ordered
290309
//
@@ -719,10 +738,6 @@ void BasicBlock::dspJumpKind()
719738
printf(" (return)");
720739
break;
721740

722-
case BBJ_NONE:
723-
// For fall-through blocks, print nothing.
724-
break;
725-
726741
case BBJ_ALWAYS:
727742
if (bbFlags & BBF_KEEP_BBJ_ALWAYS)
728743
{
@@ -981,7 +996,7 @@ BasicBlock* BasicBlock::GetUniquePred(Compiler* compiler) const
981996

982997
//------------------------------------------------------------------------
983998
// GetUniqueSucc: Returns the unique successor of a block, if one exists.
984-
// Only considers BBJ_ALWAYS and BBJ_NONE block types.
999+
// Only considers BBJ_ALWAYS block types.
9851000
//
9861001
// Arguments:
9871002
// None.
@@ -991,18 +1006,7 @@ BasicBlock* BasicBlock::GetUniquePred(Compiler* compiler) const
9911006
//
9921007
BasicBlock* BasicBlock::GetUniqueSucc() const
9931008
{
994-
if (bbJumpKind == BBJ_ALWAYS)
995-
{
996-
return bbJumpDest;
997-
}
998-
else if (bbJumpKind == BBJ_NONE)
999-
{
1000-
return bbNext;
1001-
}
1002-
else
1003-
{
1004-
return nullptr;
1005-
}
1009+
return KindIs(BBJ_ALWAYS) ? bbJumpDest : nullptr;
10061010
}
10071011

10081012
// Static vars.
@@ -1120,7 +1124,6 @@ bool BasicBlock::bbFallsThrough() const
11201124
case BBJ_SWITCH:
11211125
return false;
11221126

1123-
case BBJ_NONE:
11241127
case BBJ_COND:
11251128
return true;
11261129

@@ -1156,7 +1159,6 @@ unsigned BasicBlock::NumSucc() const
11561159
case BBJ_EHCATCHRET:
11571160
case BBJ_EHFILTERRET:
11581161
case BBJ_LEAVE:
1159-
case BBJ_NONE:
11601162
return 1;
11611163

11621164
case BBJ_COND:
@@ -1215,9 +1217,6 @@ BasicBlock* BasicBlock::GetSucc(unsigned i) const
12151217
case BBJ_LEAVE:
12161218
return bbJumpDest;
12171219

1218-
case BBJ_NONE:
1219-
return bbNext;
1220-
12211220
case BBJ_COND:
12221221
if (i == 0)
12231222
{
@@ -1283,7 +1282,6 @@ unsigned BasicBlock::NumSucc(Compiler* comp)
12831282
case BBJ_EHCATCHRET:
12841283
case BBJ_EHFILTERRET:
12851284
case BBJ_LEAVE:
1286-
case BBJ_NONE:
12871285
return 1;
12881286

12891287
case BBJ_COND:
@@ -1340,9 +1338,6 @@ BasicBlock* BasicBlock::GetSucc(unsigned i, Compiler* comp)
13401338
case BBJ_LEAVE:
13411339
return bbJumpDest;
13421340

1343-
case BBJ_NONE:
1344-
return bbNext;
1345-
13461341
case BBJ_COND:
13471342
if (i == 0)
13481343
{
@@ -1619,7 +1614,7 @@ BasicBlock* BasicBlock::New(Compiler* compiler, BBjumpKinds jumpKind, BasicBlock
16191614
block->bbJumpKind = jumpKind;
16201615
block->bbJumpDest = jumpDest;
16211616

1622-
if (jumpKind == BBJ_THROW)
1617+
if (block->KindIs(BBJ_THROW))
16231618
{
16241619
block->bbSetRunRarely();
16251620
}

src/coreclr/jit/block.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ enum BBjumpKinds : BYTE
6565
BBJ_EHCATCHRET, // block ends with a leave out of a catch (only #if defined(FEATURE_EH_FUNCLETS))
6666
BBJ_THROW, // block ends with 'throw'
6767
BBJ_RETURN, // block ends with 'ret'
68-
BBJ_NONE, // block flows into the next one (no jump)
6968
BBJ_ALWAYS, // block always jumps to the target
7069
BBJ_LEAVE, // block always jumps to the target, maybe out of guarded region. Only used until importing.
7170
BBJ_CALLFINALLY, // block always calls the target finally
@@ -83,7 +82,6 @@ const char* const BBjumpKindNames[] = {
8382
"BBJ_EHCATCHRET",
8483
"BBJ_THROW",
8584
"BBJ_RETURN",
86-
"BBJ_NONE",
8785
"BBJ_ALWAYS",
8886
"BBJ_LEAVE",
8987
"BBJ_CALLFINALLY",
@@ -430,6 +428,10 @@ enum BasicBlockFlags : unsigned __int64
430428
BBF_RECURSIVE_TAILCALL = MAKE_BBFLAG(40), // Block has recursive tailcall that may turn into a loop
431429
BBF_NO_CSE_IN = MAKE_BBFLAG(41), // Block should kill off any incoming CSE
432430
BBF_CAN_ADD_PRED = MAKE_BBFLAG(42), // Ok to add pred edge to this block, even when "safe" edge creation disabled
431+
BBF_NONE_QUIRK = MAKE_BBFLAG(43), // Block was created as a BBJ_ALWAYS to the next block,
432+
// and should be treated as if it falls through.
433+
// This is just to reduce diffs from removing BBJ_NONE.
434+
// (TODO: Remove this quirk after refactoring Compiler::fgFindInsertPoint)
433435

434436
// The following are sets of flags.
435437

@@ -459,7 +461,7 @@ enum BasicBlockFlags : unsigned __int64
459461
// TODO: Should BBF_RUN_RARELY be added to BBF_SPLIT_GAINED ?
460462

461463
BBF_SPLIT_GAINED = BBF_DONT_REMOVE | BBF_HAS_JMP | BBF_BACKWARD_JUMP | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_PROF_WEIGHT | \
462-
BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_MDARRAYREF | BBF_NEEDS_GCPOLL,
464+
BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_MDARRAYREF | BBF_NEEDS_GCPOLL | BBF_NONE_QUIRK,
463465

464466
// Flags that must be propagated to a new block if code is copied from a block to a new block. These are flags that
465467
// limit processing of a block if the code in question doesn't exist. This is conservative; we might not
@@ -599,6 +601,8 @@ struct BasicBlock : private LIR::Range
599601

600602
bool IsFirstColdBlock(Compiler* compiler) const;
601603

604+
bool CanRemoveJumpToNext(Compiler* compiler);
605+
602606
unsigned GetJumpOffs() const
603607
{
604608
return bbJumpOffs;
@@ -729,7 +733,7 @@ struct BasicBlock : private LIR::Range
729733
unsigned dspPreds(); // Print the predecessors (bbPreds)
730734
void dspSuccs(Compiler* compiler); // Print the successors. The 'compiler' argument determines whether EH
731735
// regions are printed: see NumSucc() for details.
732-
void dspJumpKind(); // Print the block jump kind (e.g., BBJ_NONE, BBJ_COND, etc.).
736+
void dspJumpKind(); // Print the block jump kind (e.g., BBJ_ALWAYS, BBJ_COND, etc.).
733737

734738
// Print a simple basic block header for various output, including a list of predecessors and successors.
735739
void dspBlockHeader(Compiler* compiler, bool showKind = true, bool showFlags = false, bool showPreds = true);
@@ -1771,12 +1775,6 @@ inline BasicBlock::BBSuccList::BBSuccList(const BasicBlock* block)
17711775
m_end = &m_succs[1];
17721776
break;
17731777

1774-
case BBJ_NONE:
1775-
m_succs[0] = block->bbNext;
1776-
m_begin = &m_succs[0];
1777-
m_end = &m_succs[1];
1778-
break;
1779-
17801778
case BBJ_COND:
17811779
m_succs[0] = block->bbNext;
17821780
m_begin = &m_succs[0];

src/coreclr/jit/codegencommon.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,15 @@ void CodeGen::genMarkLabelsForCodegen()
379379
switch (block->GetJumpKind())
380380
{
381381
case BBJ_ALWAYS: // This will also handle the BBJ_ALWAYS of a BBJ_CALLFINALLY/BBJ_ALWAYS pair.
382+
{
383+
// If we can skip this jump, don't create a label for the target
384+
if (block->CanRemoveJumpToNext(compiler))
385+
{
386+
break;
387+
}
388+
389+
FALLTHROUGH;
390+
}
382391
case BBJ_COND:
383392
case BBJ_EHCATCHRET:
384393
JITDUMP(" " FMT_BB " : branch target\n", block->GetJumpDest()->bbNum);
@@ -422,7 +431,6 @@ void CodeGen::genMarkLabelsForCodegen()
422431
case BBJ_EHFILTERRET:
423432
case BBJ_RETURN:
424433
case BBJ_THROW:
425-
case BBJ_NONE:
426434
break;
427435

428436
default:

src/coreclr/jit/codegenlinear.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ void CodeGen::genCodeForBBlist()
599599
noway_assert(genStackLevel == 0);
600600

601601
#ifdef TARGET_AMD64
602+
bool emitNop = false;
602603
// On AMD64, we need to generate a NOP after a call that is the last instruction of the block, in several
603604
// situations, to support proper exception handling semantics. This is mostly to ensure that when the stack
604605
// walker computes an instruction pointer for a frame, that instruction pointer is in the correct EH region.
@@ -621,6 +622,11 @@ void CodeGen::genCodeForBBlist()
621622
switch (block->GetJumpKind())
622623
{
623624
case BBJ_ALWAYS:
625+
// We might skip generating the jump via a peephole optimization.
626+
// If that happens, make sure a NOP is emitted as the last instruction in the block.
627+
emitNop = true;
628+
break;
629+
624630
case BBJ_THROW:
625631
case BBJ_CALLFINALLY:
626632
case BBJ_EHCATCHRET:
@@ -631,20 +637,6 @@ void CodeGen::genCodeForBBlist()
631637
case BBJ_EHFAULTRET:
632638
case BBJ_EHFILTERRET:
633639
// These are the "epilog follows" case, handled in the emitter.
634-
635-
break;
636-
637-
case BBJ_NONE:
638-
if (block->IsLast())
639-
{
640-
// Call immediately before the end of the code; we should never get here .
641-
instGen(INS_BREAKPOINT); // This should never get executed
642-
}
643-
else
644-
{
645-
// We need the NOP
646-
instGen(INS_nop);
647-
}
648640
break;
649641

650642
case BBJ_COND:
@@ -734,13 +726,26 @@ void CodeGen::genCodeForBBlist()
734726

735727
#endif // !FEATURE_EH_FUNCLETS
736728

737-
case BBJ_NONE:
738729
case BBJ_SWITCH:
739730
break;
740731

741732
case BBJ_ALWAYS:
742-
#ifdef TARGET_XARCH
743733
{
734+
// Peephole optimization: If this block jumps to the next one, skip emitting the jump
735+
// (unless we are jumping between hot/cold sections, or if we need the jump for EH reasons)
736+
// (Skip this if optimizations are disabled, unless the block shouldn't have a jump in the first place)
737+
if (block->CanRemoveJumpToNext(compiler))
738+
{
739+
#ifdef TARGET_AMD64
740+
if (emitNop)
741+
{
742+
instGen(INS_nop);
743+
}
744+
#endif // TARGET_AMD64
745+
746+
break;
747+
}
748+
#ifdef TARGET_XARCH
744749
// If a block was selected to place an alignment instruction because it ended
745750
// with a jump, do not remove jumps from such blocks.
746751
// Do not remove a jump between hot and cold regions.
@@ -755,10 +760,10 @@ void CodeGen::genCodeForBBlist()
755760
#endif // TARGET_AMD64
756761

757762
inst_JMP(EJ_jmp, block->GetJumpDest(), isRemovableJmpCandidate);
758-
}
759763
#else
760764
inst_JMP(EJ_jmp, block->GetJumpDest());
761765
#endif // TARGET_XARCH
766+
}
762767

763768
FALLTHROUGH;
764769

src/coreclr/jit/compiler.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5279,8 +5279,9 @@ PhaseStatus Compiler::placeLoopAlignInstructions()
52795279
}
52805280
}
52815281

5282-
// If there is an unconditional jump (which is not part of callf/always pair)
5283-
if (opts.compJitHideAlignBehindJmp && block->KindIs(BBJ_ALWAYS) && !block->isBBCallAlwaysPairTail())
5282+
// If there is an unconditional jump (which is not part of callf/always pair, and isn't to the next block)
5283+
if (opts.compJitHideAlignBehindJmp && block->KindIs(BBJ_ALWAYS) && !block->isBBCallAlwaysPairTail() &&
5284+
((block->bbFlags & BBF_NONE_QUIRK) == 0))
52845285
{
52855286
// Track the lower weight blocks
52865287
if (block->bbWeight < minBlockSoFar)

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6096,7 +6096,10 @@ class Compiler
60966096

60976097
bool fgIsThrow(GenTree* tree);
60986098

6099+
public:
60996100
bool fgInDifferentRegions(BasicBlock* blk1, BasicBlock* blk2);
6101+
6102+
private:
61006103
bool fgIsBlockCold(BasicBlock* block);
61016104

61026105
GenTree* fgMorphCastIntoHelper(GenTree* tree, int helper, GenTree* oper);
@@ -6676,7 +6679,7 @@ class Compiler
66766679
{
66776680
if (lpHead->NextIs(lpEntry))
66786681
{
6679-
assert(lpHead->bbFallsThrough());
6682+
assert(lpHead->bbFallsThrough() || lpHead->JumpsToNext());
66806683
assert(lpTop == lpEntry);
66816684
return true;
66826685
}

src/coreclr/jit/compiler.hpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -627,10 +627,6 @@ BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func)
627627

628628
return BasicBlockVisit::Continue;
629629

630-
case BBJ_NONE:
631-
RETURN_ON_ABORT(func(bbNext));
632-
return VisitEHSuccs(comp, func);
633-
634630
case BBJ_COND:
635631
RETURN_ON_ABORT(func(bbNext));
636632

@@ -697,9 +693,6 @@ BasicBlockVisit BasicBlock::VisitRegularSuccs(Compiler* comp, TFunc func)
697693
case BBJ_ALWAYS:
698694
return func(bbJumpDest);
699695

700-
case BBJ_NONE:
701-
return func(bbNext);
702-
703696
case BBJ_COND:
704697
RETURN_ON_ABORT(func(bbNext));
705698

0 commit comments

Comments
 (0)