Skip to content

Commit e1ab224

Browse files
authored
JIT: Straighten out flow during early jump threading (#104603)
After early jump threading had kicked in we can frequently prove that a branch will go in one direction. This was previously left up to RBO; instead, try to fold it immediately and straighten out the flow before we build SSA, so that we can refine the phis. Fix #101176
1 parent 2199c77 commit e1ab224

File tree

2 files changed

+138
-4
lines changed

2 files changed

+138
-4
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6175,6 +6175,7 @@ class Compiler
61756175
#endif // FEATURE_EH_WINDOWS_X86
61766176

61776177
bool fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* target);
6178+
bool fgFoldSimpleCondByForwardSub(BasicBlock* block);
61786179

61796180
bool fgBlockEndFavorsTailDuplication(BasicBlock* block, unsigned lclNum);
61806181

src/coreclr/jit/fgopt.cpp

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,12 @@ bool Compiler::fgBlockIsGoodTailDuplicationCandidate(BasicBlock* target, unsigne
19661966
return false;
19671967
}
19681968

1969+
// No point duplicating this block if it would not remove (part of) the join.
1970+
if (target->TrueTargetIs(target) || target->FalseTargetIs(target))
1971+
{
1972+
return false;
1973+
}
1974+
19691975
Statement* const lastStmt = target->lastStmt();
19701976
Statement* const firstStmt = target->FirstNonPhiDef();
19711977

@@ -2265,6 +2271,108 @@ bool Compiler::fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock*
22652271
return true;
22662272
}
22672273

2274+
//-------------------------------------------------------------
2275+
// fgFoldSimpleCondByForwardSub:
2276+
// Try to refine the flow of a block that may have just been tail duplicated
2277+
// or compacted.
2278+
//
2279+
// Arguments:
2280+
// block - block that was tail duplicated or compacted
2281+
//
2282+
// Returns Value:
2283+
// true if control flow was changed
2284+
//
2285+
bool Compiler::fgFoldSimpleCondByForwardSub(BasicBlock* block)
2286+
{
2287+
assert(block->KindIs(BBJ_COND));
2288+
GenTree* jtrue = block->lastStmt()->GetRootNode();
2289+
assert(jtrue->OperIs(GT_JTRUE));
2290+
2291+
GenTree* relop = jtrue->gtGetOp1();
2292+
if (!relop->OperIsCompare())
2293+
{
2294+
return false;
2295+
}
2296+
2297+
GenTree* op1 = relop->gtGetOp1();
2298+
GenTree* op2 = relop->gtGetOp2();
2299+
2300+
GenTree** lclUse;
2301+
GenTreeLclVarCommon* lcl;
2302+
2303+
if (op1->OperIs(GT_LCL_VAR) && op2->IsIntegralConst())
2304+
{
2305+
lclUse = &relop->AsOp()->gtOp1;
2306+
lcl = op1->AsLclVarCommon();
2307+
}
2308+
else if (op2->OperIs(GT_LCL_VAR) && op1->IsIntegralConst())
2309+
{
2310+
lclUse = &relop->AsOp()->gtOp2;
2311+
lcl = op2->AsLclVarCommon();
2312+
}
2313+
else
2314+
{
2315+
return false;
2316+
}
2317+
2318+
Statement* secondLastStmt = block->lastStmt()->GetPrevStmt();
2319+
if ((secondLastStmt == nullptr) || (secondLastStmt == block->lastStmt()))
2320+
{
2321+
return false;
2322+
}
2323+
2324+
GenTree* prevTree = secondLastStmt->GetRootNode();
2325+
if (!prevTree->OperIs(GT_STORE_LCL_VAR))
2326+
{
2327+
return false;
2328+
}
2329+
2330+
GenTreeLclVarCommon* store = prevTree->AsLclVarCommon();
2331+
if (store->GetLclNum() != lcl->GetLclNum())
2332+
{
2333+
return false;
2334+
}
2335+
2336+
if (!store->Data()->IsIntegralConst())
2337+
{
2338+
return false;
2339+
}
2340+
2341+
if (genActualType(store) != genActualType(store->Data()) || (genActualType(store) != genActualType(lcl)))
2342+
{
2343+
return false;
2344+
}
2345+
2346+
JITDUMP("Forward substituting local after jump threading. Before:\n");
2347+
DISPSTMT(block->lastStmt());
2348+
2349+
JITDUMP("\nAfter:\n");
2350+
2351+
LclVarDsc* varDsc = lvaGetDesc(lcl);
2352+
GenTree* newData = gtCloneExpr(store->Data());
2353+
if (varTypeIsSmall(varDsc) && fgCastNeeded(store->Data(), varDsc->TypeGet()))
2354+
{
2355+
newData = gtNewCastNode(TYP_INT, newData, false, varDsc->TypeGet());
2356+
newData = gtFoldExpr(newData);
2357+
}
2358+
2359+
*lclUse = newData;
2360+
DISPSTMT(block->lastStmt());
2361+
2362+
JITDUMP("\nNow trying to fold...\n");
2363+
jtrue->AsUnOp()->gtOp1 = gtFoldExpr(relop);
2364+
DISPSTMT(block->lastStmt());
2365+
2366+
Compiler::FoldResult result = fgFoldConditional(block);
2367+
if (result != Compiler::FoldResult::FOLD_DID_NOTHING)
2368+
{
2369+
assert(block->KindIs(BBJ_ALWAYS));
2370+
return true;
2371+
}
2372+
2373+
return false;
2374+
}
2375+
22682376
//-------------------------------------------------------------
22692377
// fgRemoveConditionalJump:
22702378
// Optimize a BBJ_COND block that unconditionally jumps to the same target
@@ -5176,10 +5284,35 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication /* = false */,
51765284
{
51775285
assert(block->KindIs(BBJ_COND));
51785286
assert(bNext == block->Next());
5179-
change = true;
5180-
modified = true;
5181-
bDest = block->GetTrueTarget();
5182-
bFalseDest = block->GetFalseTarget();
5287+
change = true;
5288+
modified = true;
5289+
5290+
if (fgFoldSimpleCondByForwardSub(block))
5291+
{
5292+
// It is likely another pred of the target now can
5293+
// similarly have its control flow straightened out.
5294+
// Try to compact it and repeat the optimization for
5295+
// it.
5296+
if (bDest->bbRefs == 1)
5297+
{
5298+
BasicBlock* otherPred = bDest->bbPreds->getSourceBlock();
5299+
JITDUMP("Trying to compact last pred " FMT_BB " of " FMT_BB " that we now bypass\n",
5300+
otherPred->bbNum, bDest->bbNum);
5301+
if (fgCanCompactBlock(otherPred))
5302+
{
5303+
fgCompactBlock(otherPred);
5304+
fgFoldSimpleCondByForwardSub(otherPred);
5305+
}
5306+
}
5307+
5308+
assert(block->KindIs(BBJ_ALWAYS));
5309+
bDest = block->GetTarget();
5310+
}
5311+
else
5312+
{
5313+
bDest = block->GetTrueTarget();
5314+
bFalseDest = block->GetFalseTarget();
5315+
}
51835316
}
51845317
}
51855318

0 commit comments

Comments
 (0)