Skip to content

Commit a4e3a42

Browse files
authored
JIT: refactor jump threading a bit (#76108)
In anticipation that phi disambiguation will end up reusing the core part of this transformation.
1 parent 2600909 commit a4e3a42

File tree

2 files changed

+145
-77
lines changed

2 files changed

+145
-77
lines changed

src/coreclr/jit/compiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class SpanningTreeVisitor; // defined in fgprofile.cpp
8585
class CSE_DataFlow; // defined in OptCSE.cpp
8686
class OptBoolsDsc; // defined in optimizer.cpp
8787
struct RelopImplicationInfo; // defined in redundantbranchopts.cpp
88+
struct JumpThreadInfo; // defined in redundantbranchopts.cpp
8889
#ifdef DEBUG
8990
struct IndentStack;
9091
#endif
@@ -6968,6 +6969,7 @@ class Compiler
69686969
bool optRedundantRelop(BasicBlock* const block);
69696970
bool optRedundantBranch(BasicBlock* const block);
69706971
bool optJumpThread(BasicBlock* const block, BasicBlock* const domBlock, bool domIsSameRelop);
6972+
bool optJumpThreadCore(JumpThreadInfo& jti);
69716973
bool optReachable(BasicBlock* const fromBlock, BasicBlock* const toBlock, BasicBlock* const excludedBlock);
69726974
BitVecTraits* optReachableBitVecTraits;
69736975
BitVec optReachableBitVec;

src/coreclr/jit/redundantbranchopts.cpp

Lines changed: 143 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,52 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
675675
return true;
676676
}
677677

678+
//------------------------------------------------------------------------
679+
// JumpThreadInfo
680+
//
681+
// Describes the relationship between a block-ending predicate value and the
682+
// block's predecessors.
683+
//
684+
struct JumpThreadInfo
685+
{
686+
JumpThreadInfo(Compiler* comp, BasicBlock* block)
687+
: m_block(block)
688+
, m_trueTarget(block->bbJumpDest)
689+
, m_falseTarget(block->bbNext)
690+
, m_fallThroughPred(nullptr)
691+
, m_truePreds(BlockSetOps::MakeEmpty(comp))
692+
, m_ambiguousPreds(BlockSetOps::MakeEmpty(comp))
693+
, m_numPreds(0)
694+
, m_numAmbiguousPreds(0)
695+
, m_numTruePreds(0)
696+
, m_numFalsePreds(0)
697+
{
698+
}
699+
700+
// Block we're trying to optimize
701+
BasicBlock* const m_block;
702+
// Block successor if predicate is true
703+
BasicBlock* const m_trueTarget;
704+
// Block successor if predicate is false
705+
BasicBlock* const m_falseTarget;
706+
// Unique pred that falls through to block, if any
707+
BasicBlock* m_fallThroughPred = nullptr;
708+
// Pred blocks for which the predicate will be true
709+
BlockSet m_truePreds;
710+
// Pred blocks that can't be threaded or for which the predicate
711+
// value can't be determined
712+
BlockSet m_ambiguousPreds;
713+
// Total number of predecessors
714+
int m_numPreds;
715+
// Number of predecessors that can't be threaded or for which the predicate
716+
// value can't be determined
717+
int m_numAmbiguousPreds;
718+
// Number of predecessors for which predicate is true
719+
int m_numTruePreds;
720+
// Number of predecessors for which predicate is false
721+
int m_numFalsePreds;
722+
};
723+
678724
//------------------------------------------------------------------------
679725
// optJumpThread: try and bypass the current block by rerouting
680726
// flow from predecessors directly to successors.
@@ -900,40 +946,32 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
900946
// latter should prove useful in subsequent work, where we aim to enable jump
901947
// threading in cases where block has side effects.
902948
//
903-
int numPreds = 0;
904-
int numAmbiguousPreds = 0;
905-
int numTruePreds = 0;
906-
int numFalsePreds = 0;
907-
BasicBlock* fallThroughPred = nullptr;
908-
BasicBlock* const trueSuccessor = domIsSameRelop ? domBlock->bbJumpDest : domBlock->bbNext;
909-
BasicBlock* const falseSuccessor = domIsSameRelop ? domBlock->bbNext : domBlock->bbJumpDest;
910-
BasicBlock* const trueTarget = block->bbJumpDest;
911-
BasicBlock* const falseTarget = block->bbNext;
912-
BlockSet truePreds = BlockSetOps::MakeEmpty(this);
913-
BlockSet ambiguousPreds = BlockSetOps::MakeEmpty(this);
949+
BasicBlock* const domTrueSuccessor = domIsSameRelop ? domBlock->bbJumpDest : domBlock->bbNext;
950+
BasicBlock* const domFalseSuccessor = domIsSameRelop ? domBlock->bbNext : domBlock->bbJumpDest;
951+
JumpThreadInfo jti(this, block);
914952

915953
for (BasicBlock* const predBlock : block->PredBlocks())
916954
{
917-
numPreds++;
955+
jti.m_numPreds++;
918956

919957
// Treat switch preds as ambiguous for now.
920958
//
921959
if (predBlock->bbJumpKind == BBJ_SWITCH)
922960
{
923961
JITDUMP(FMT_BB " is a switch pred\n", predBlock->bbNum);
924-
BlockSetOps::AddElemD(this, ambiguousPreds, predBlock->bbNum);
925-
numAmbiguousPreds++;
962+
BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, predBlock->bbNum);
963+
jti.m_numAmbiguousPreds++;
926964
continue;
927965
}
928966

929-
const bool isTruePred =
930-
((predBlock == domBlock) && (trueSuccessor == block)) || optReachable(trueSuccessor, predBlock, domBlock);
931-
const bool isFalsePred =
932-
((predBlock == domBlock) && (falseSuccessor == block)) || optReachable(falseSuccessor, predBlock, domBlock);
967+
const bool isTruePred = ((predBlock == domBlock) && (domTrueSuccessor == block)) ||
968+
optReachable(domTrueSuccessor, predBlock, domBlock);
969+
const bool isFalsePred = ((predBlock == domBlock) && (domFalseSuccessor == block)) ||
970+
optReachable(domFalseSuccessor, predBlock, domBlock);
933971

934972
if (isTruePred == isFalsePred)
935973
{
936-
// Either both reach, or neither reaches.
974+
// Either both dom successors reach, or neither reaches.
937975
//
938976
// We should rarely see (false,false) given that optReachable is returning
939977
// up to date results, but as we optimize we create unreachable blocks,
@@ -942,38 +980,38 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
942980
// lead to more complications, and it isn't that common. So we tolerate it.
943981
//
944982
JITDUMP(FMT_BB " is an ambiguous pred\n", predBlock->bbNum);
945-
BlockSetOps::AddElemD(this, ambiguousPreds, predBlock->bbNum);
946-
numAmbiguousPreds++;
983+
BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, predBlock->bbNum);
984+
jti.m_numAmbiguousPreds++;
947985
continue;
948986
}
949987

950988
if (isTruePred)
951989
{
952-
if (!BasicBlock::sameEHRegion(predBlock, trueTarget))
990+
if (!BasicBlock::sameEHRegion(predBlock, jti.m_trueTarget))
953991
{
954992
JITDUMP(FMT_BB " is an eh constrained pred\n", predBlock->bbNum);
955-
numAmbiguousPreds++;
956-
BlockSetOps::AddElemD(this, ambiguousPreds, predBlock->bbNum);
993+
jti.m_numAmbiguousPreds++;
994+
BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, predBlock->bbNum);
957995
continue;
958996
}
959997

960-
numTruePreds++;
961-
BlockSetOps::AddElemD(this, truePreds, predBlock->bbNum);
998+
jti.m_numTruePreds++;
999+
BlockSetOps::AddElemD(this, jti.m_truePreds, predBlock->bbNum);
9621000
JITDUMP(FMT_BB " is a true pred\n", predBlock->bbNum);
9631001
}
9641002
else
9651003
{
9661004
assert(isFalsePred);
9671005

968-
if (!BasicBlock::sameEHRegion(predBlock, falseTarget))
1006+
if (!BasicBlock::sameEHRegion(predBlock, jti.m_falseTarget))
9691007
{
9701008
JITDUMP(FMT_BB " is an eh constrained pred\n", predBlock->bbNum);
971-
BlockSetOps::AddElemD(this, ambiguousPreds, predBlock->bbNum);
972-
numAmbiguousPreds++;
1009+
BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, predBlock->bbNum);
1010+
jti.m_numAmbiguousPreds++;
9731011
continue;
9741012
}
9751013

976-
numFalsePreds++;
1014+
jti.m_numFalsePreds++;
9771015
JITDUMP(FMT_BB " is a false pred\n", predBlock->bbNum);
9781016
}
9791017

@@ -982,59 +1020,87 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
9821020
if (predBlock->bbNext == block)
9831021
{
9841022
JITDUMP(FMT_BB " is the fall-through pred\n", predBlock->bbNum);
985-
assert(fallThroughPred == nullptr);
986-
fallThroughPred = predBlock;
1023+
assert(jti.m_fallThroughPred == nullptr);
1024+
jti.m_fallThroughPred = predBlock;
9871025
}
9881026
}
9891027

1028+
// Do the optimization.
1029+
//
1030+
return optJumpThreadCore(jti);
1031+
}
1032+
1033+
//------------------------------------------------------------------------
1034+
// optJumpThreadCore: restructure block flow based on jump thread information
1035+
//
1036+
// Arguments:
1037+
// jit - information on how to jump thread this block
1038+
//
1039+
// Returns:
1040+
// True if the branch was optimized.
1041+
//
1042+
bool Compiler::optJumpThreadCore(JumpThreadInfo& jti)
1043+
{
9901044
// All preds should have been classified.
9911045
//
992-
assert(numPreds == numTruePreds + numFalsePreds + numAmbiguousPreds);
1046+
assert(jti.m_numPreds == jti.m_numTruePreds + jti.m_numFalsePreds + jti.m_numAmbiguousPreds);
9931047

994-
if ((numTruePreds == 0) && (numFalsePreds == 0))
1048+
// There should be at least one pred that can bypass block.
1049+
//
1050+
if ((jti.m_numTruePreds == 0) && (jti.m_numFalsePreds == 0))
9951051
{
9961052
// This is possible, but should be rare.
9971053
//
998-
JITDUMP(FMT_BB " only has ambiguous preds, not optimizing\n", block->bbNum);
1054+
JITDUMP(FMT_BB " only has ambiguous preds, not jump threading\n", jti.m_block->bbNum);
9991055
return false;
10001056
}
10011057

1002-
if ((numAmbiguousPreds > 0) && (fallThroughPred != nullptr))
1058+
if ((jti.m_numAmbiguousPreds > 0) && (jti.m_fallThroughPred != nullptr))
10031059
{
10041060
// Treat the fall through pred as an ambiguous pred.
1005-
JITDUMP(FMT_BB " has both ambiguous preds and a fall through pred\n", block->bbNum);
1006-
JITDUMP("Treating fall through pred " FMT_BB " as an ambiguous pred\n", fallThroughPred->bbNum);
1061+
JITDUMP(FMT_BB " has both ambiguous preds and a fall through pred\n", jti.m_block->bbNum);
1062+
JITDUMP("Treating fall through pred " FMT_BB " as an ambiguous pred\n", jti.m_fallThroughPred->bbNum);
10071063

1008-
if (BlockSetOps::IsMember(this, truePreds, fallThroughPred->bbNum))
1064+
if (BlockSetOps::IsMember(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum))
10091065
{
1010-
BlockSetOps::RemoveElemD(this, truePreds, fallThroughPred->bbNum);
1011-
assert(numTruePreds > 0);
1012-
numTruePreds--;
1066+
BlockSetOps::RemoveElemD(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum);
1067+
assert(jti.m_numTruePreds > 0);
1068+
jti.m_numTruePreds--;
10131069
}
10141070
else
10151071
{
1016-
assert(numFalsePreds > 0);
1017-
numFalsePreds--;
1072+
assert(jti.m_numFalsePreds > 0);
1073+
jti.m_numFalsePreds--;
10181074
}
10191075

1020-
assert(!(BlockSetOps::IsMember(this, ambiguousPreds, fallThroughPred->bbNum)));
1021-
BlockSetOps::AddElemD(this, ambiguousPreds, fallThroughPred->bbNum);
1022-
numAmbiguousPreds++;
1023-
fallThroughPred = nullptr;
1076+
assert(!(BlockSetOps::IsMember(this, jti.m_ambiguousPreds, jti.m_fallThroughPred->bbNum)));
1077+
BlockSetOps::AddElemD(this, jti.m_ambiguousPreds, jti.m_fallThroughPred->bbNum);
1078+
jti.m_numAmbiguousPreds++;
1079+
jti.m_fallThroughPred = nullptr;
1080+
}
1081+
1082+
// There still should be at least one pred that can bypass block.
1083+
//
1084+
if ((jti.m_numTruePreds == 0) && (jti.m_numFalsePreds == 0))
1085+
{
1086+
// This is possible, but also should be rare.
1087+
//
1088+
JITDUMP(FMT_BB " now only has ambiguous preds, not jump threading\n", jti.m_block->bbNum);
1089+
return false;
10241090
}
10251091

10261092
// Determine if either set of preds will route via block.
10271093
//
10281094
bool truePredsWillReuseBlock = false;
10291095
bool falsePredsWillReuseBlock = false;
10301096

1031-
if (fallThroughPred != nullptr)
1097+
if (jti.m_fallThroughPred != nullptr)
10321098
{
1033-
assert(numAmbiguousPreds == 0);
1034-
truePredsWillReuseBlock = BlockSetOps::IsMember(this, truePreds, fallThroughPred->bbNum);
1099+
assert(jti.m_numAmbiguousPreds == 0);
1100+
truePredsWillReuseBlock = BlockSetOps::IsMember(this, jti.m_truePreds, jti.m_fallThroughPred->bbNum);
10351101
falsePredsWillReuseBlock = !truePredsWillReuseBlock;
10361102
}
1037-
else if (numAmbiguousPreds == 0)
1103+
else if (jti.m_numAmbiguousPreds == 0)
10381104
{
10391105
truePredsWillReuseBlock = true;
10401106
falsePredsWillReuseBlock = !truePredsWillReuseBlock;
@@ -1050,35 +1116,36 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
10501116
//
10511117
if (truePredsWillReuseBlock)
10521118
{
1053-
Statement* lastStmt = block->lastStmt();
1054-
fgRemoveStmt(block, lastStmt);
1055-
JITDUMP(" repurposing " FMT_BB " to always jump to " FMT_BB "\n", block->bbNum, trueTarget->bbNum);
1056-
fgRemoveRefPred(block->bbNext, block);
1057-
block->bbJumpKind = BBJ_ALWAYS;
1119+
Statement* const lastStmt = jti.m_block->lastStmt();
1120+
fgRemoveStmt(jti.m_block, lastStmt);
1121+
JITDUMP(" repurposing " FMT_BB " to always jump to " FMT_BB "\n", jti.m_block->bbNum, jti.m_trueTarget->bbNum);
1122+
fgRemoveRefPred(jti.m_falseTarget, jti.m_block);
1123+
jti.m_block->bbJumpKind = BBJ_ALWAYS;
10581124
}
10591125
else if (falsePredsWillReuseBlock)
10601126
{
1061-
Statement* lastStmt = block->lastStmt();
1062-
fgRemoveStmt(block, lastStmt);
1063-
JITDUMP(" repurposing " FMT_BB " to always fall through to " FMT_BB "\n", block->bbNum, falseTarget->bbNum);
1064-
fgRemoveRefPred(block->bbJumpDest, block);
1065-
block->bbJumpKind = BBJ_NONE;
1127+
Statement* const lastStmt = jti.m_block->lastStmt();
1128+
fgRemoveStmt(jti.m_block, lastStmt);
1129+
JITDUMP(" repurposing " FMT_BB " to always fall through to " FMT_BB "\n", jti.m_block->bbNum,
1130+
jti.m_falseTarget->bbNum);
1131+
fgRemoveRefPred(jti.m_trueTarget, jti.m_block);
1132+
jti.m_block->bbJumpKind = BBJ_NONE;
10661133
}
10671134

10681135
// Now reroute the flow from the predecessors.
10691136
// If this pred is in the set that will reuse block, do nothing.
10701137
// Else revise pred to branch directly to the appropriate successor of block.
10711138
//
1072-
for (BasicBlock* const predBlock : block->PredBlocks())
1139+
for (BasicBlock* const predBlock : jti.m_block->PredBlocks())
10731140
{
10741141
// If this was an ambiguous pred, skip.
10751142
//
1076-
if (BlockSetOps::IsMember(this, ambiguousPreds, predBlock->bbNum))
1143+
if (BlockSetOps::IsMember(this, jti.m_ambiguousPreds, predBlock->bbNum))
10771144
{
10781145
continue;
10791146
}
10801147

1081-
const bool isTruePred = BlockSetOps::IsMember(this, truePreds, predBlock->bbNum);
1148+
const bool isTruePred = BlockSetOps::IsMember(this, jti.m_truePreds, predBlock->bbNum);
10821149

10831150
// Do we need to alter flow from this pred?
10841151
//
@@ -1087,36 +1154,35 @@ bool Compiler::optJumpThread(BasicBlock* const block, BasicBlock* const domBlock
10871154
// No, we can leave as is.
10881155
//
10891156
JITDUMP("%s pred " FMT_BB " will continue to target " FMT_BB "\n", isTruePred ? "true" : "false",
1090-
predBlock->bbNum, block->bbNum);
1157+
predBlock->bbNum, jti.m_block->bbNum);
10911158
continue;
10921159
}
10931160

10941161
// Yes, we need to jump to the appropriate successor.
10951162
// Note we should not be altering flow for the fall-through pred.
10961163
//
1097-
assert(predBlock != fallThroughPred);
1098-
assert(predBlock->bbNext != block);
1164+
assert(predBlock != jti.m_fallThroughPred);
1165+
assert(predBlock->bbNext != jti.m_block);
10991166

11001167
if (isTruePred)
11011168
{
1102-
assert(!optReachable(falseSuccessor, predBlock, domBlock));
11031169
JITDUMP("Jump flow from pred " FMT_BB " -> " FMT_BB
11041170
" implies predicate true; we can safely redirect flow to be " FMT_BB " -> " FMT_BB "\n",
1105-
predBlock->bbNum, block->bbNum, predBlock->bbNum, trueTarget->bbNum);
1171+
predBlock->bbNum, jti.m_block->bbNum, predBlock->bbNum, jti.m_trueTarget->bbNum);
11061172

1107-
fgRemoveRefPred(block, predBlock);
1108-
fgReplaceJumpTarget(predBlock, trueTarget, block);
1109-
fgAddRefPred(trueTarget, predBlock);
1173+
fgRemoveRefPred(jti.m_block, predBlock);
1174+
fgReplaceJumpTarget(predBlock, jti.m_trueTarget, jti.m_block);
1175+
fgAddRefPred(jti.m_trueTarget, predBlock);
11101176
}
11111177
else
11121178
{
11131179
JITDUMP("Jump flow from pred " FMT_BB " -> " FMT_BB
11141180
" implies predicate false; we can safely redirect flow to be " FMT_BB " -> " FMT_BB "\n",
1115-
predBlock->bbNum, block->bbNum, predBlock->bbNum, falseTarget->bbNum);
1181+
predBlock->bbNum, jti.m_block->bbNum, predBlock->bbNum, jti.m_falseTarget->bbNum);
11161182

1117-
fgRemoveRefPred(block, predBlock);
1118-
fgReplaceJumpTarget(predBlock, falseTarget, block);
1119-
fgAddRefPred(falseTarget, predBlock);
1183+
fgRemoveRefPred(jti.m_block, predBlock);
1184+
fgReplaceJumpTarget(predBlock, jti.m_falseTarget, jti.m_block);
1185+
fgAddRefPred(jti.m_falseTarget, predBlock);
11201186
}
11211187
}
11221188

0 commit comments

Comments
 (0)