Skip to content

Commit b1fbc2b

Browse files
committed
Rewrite SimplifyCFG's trampoline removal.
Avoid introducing new critical edges. Passes will end up resplitting them, forcing SimplifyCFG to continually rerun. Also, we want to allow SIL utilities to assume no critical edges, and avoid the need for several passes to internally split edges and modify the CFG for no reason. Also, handle arbitrary block arguments which may be trivial and unrelated to the real optimizations that trampoline removal exposes, such as "unwrapping" enumeration-type arguments. The previous code was an example of how to write an unstable optimization. It could be defeated by other code in the function that isn't directly related to the SSA graph being optimized. In general, when an optimization can be defeated by unrelated code in the function, that leads to instability which can be very hard to track down (I spent multiple full days on this one). In this case, we have enum-type branch args which need to be simplified by unwrapping them. But, the existence of a trivial and entirely unrelated block argument would defeat the optimization.
1 parent a69502e commit b1fbc2b

File tree

6 files changed

+265
-136
lines changed

6 files changed

+265
-136
lines changed

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 120 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,49 +1123,79 @@ static bool onlyHasTerminatorAndDebugInsts(SILBasicBlock *BB) {
11231123
return true;
11241124
}
11251125

1126-
/// \return If this basic blocks has a single br instruction passing all of the
1127-
/// arguments in the original order, then returns the destination of that br.
1128-
static SILBasicBlock *getTrampolineDest(SILBasicBlock *SBB) {
1126+
namespace {
1127+
1128+
/// Will be valid if the constructor's targetBB has a a single branch and all
1129+
/// its block arguments are only used by that branch.
1130+
struct TrampolineDest {
1131+
SILBasicBlock *destBB = nullptr;
1132+
// Source block's branch args after bypassing targetBB.
1133+
SmallVector<SILValue, 4> newSourceBranchArgs;
1134+
1135+
TrampolineDest() {}
1136+
TrampolineDest(SILBasicBlock *sourceBB, SILBasicBlock *targetBB);
1137+
TrampolineDest(const TrampolineDest &) = delete;
1138+
TrampolineDest &operator=(const TrampolineDest &) = delete;
1139+
TrampolineDest(TrampolineDest &&) = default;
1140+
TrampolineDest &operator=(TrampolineDest &&) = default;
1141+
1142+
bool operator==(const TrampolineDest &rhs) {
1143+
return destBB == rhs.destBB
1144+
&& newSourceBranchArgs == rhs.newSourceBranchArgs;
1145+
}
1146+
bool operator!=(const TrampolineDest &rhs) {
1147+
return !(*this == rhs);
1148+
}
1149+
1150+
operator bool() const { return destBB != nullptr; }
1151+
};
1152+
1153+
} // end anonymous namespace
1154+
1155+
TrampolineDest::TrampolineDest(SILBasicBlock *sourceBB,
1156+
SILBasicBlock *targetBB) {
11291157
// Ignore blocks with more than one instruction.
1130-
if (!onlyHasTerminatorAndDebugInsts(SBB))
1131-
return nullptr;
1158+
if (!onlyHasTerminatorAndDebugInsts(targetBB))
1159+
return;
11321160

1133-
auto *BI = dyn_cast<BranchInst>(SBB->getTerminator());
1134-
if (!BI)
1135-
return nullptr;
1161+
auto *targetBranch = dyn_cast<BranchInst>(targetBB->getTerminator());
1162+
if (!targetBranch)
1163+
return;
11361164

1137-
// Disallow infinite loops through SBB.
1165+
// Disallow infinite loops through targetBB.
11381166
llvm::SmallPtrSet<SILBasicBlock *, 8> VisitedBBs;
1139-
BranchInst *NextBI = BI;
1167+
BranchInst *nextBI = targetBranch;
11401168
do {
1141-
SILBasicBlock *NextBB = NextBI->getDestBB();
1169+
SILBasicBlock *nextBB = nextBI->getDestBB();
11421170
// We don't care about infinite loops after SBB.
1143-
if (!VisitedBBs.insert(NextBB).second)
1171+
if (!VisitedBBs.insert(nextBB).second)
11441172
break;
11451173
// Only if the infinite loop goes through SBB directly we bail.
1146-
if (NextBB == SBB)
1147-
return nullptr;
1148-
NextBI = dyn_cast<BranchInst>(NextBB->getTerminator());
1149-
} while (NextBI);
1150-
1151-
auto BrArgs = BI->getArgs();
1152-
if (BrArgs.size() != SBB->getNumArguments())
1153-
return nullptr;
1174+
if (nextBB == targetBB)
1175+
return;
1176+
nextBI = dyn_cast<BranchInst>(nextBB->getTerminator());
1177+
} while (nextBI);
11541178

1155-
// Check that the arguments are the same and in the right order.
1156-
for (int i = 0, e = SBB->getNumArguments(); i < e; ++i) {
1157-
SILArgument *BBArg = SBB->getArgument(i);
1158-
if (BrArgs[i] != BBArg)
1159-
return nullptr;
1160-
1161-
// The arguments may not be used in another block, because when the
1162-
// predecessor of SBB directly jumps to the successor, the SBB block does
1163-
// not dominate the other use anymore.
1164-
if (!BBArg->hasOneUse())
1165-
return nullptr;
1179+
// Check that all the target block arguments are only used by the branch.
1180+
for (SILValue blockArg : targetBB->getArguments()) {
1181+
Operand *operand = blockArg->getSingleUse();
1182+
if (!operand || operand->getUser() != targetBranch) {
1183+
return;
1184+
}
11661185
}
1167-
1168-
return BI->getDestBB();
1186+
newSourceBranchArgs.reserve(targetBranch->getArgs().size());
1187+
for (SILValue branchArg : targetBranch->getArgs()) {
1188+
if (branchArg->getParentBlock() == targetBB) {
1189+
auto *phi = dyn_cast<SILPhiArgument>(branchArg);
1190+
if (!phi || !phi->isPhiArgument()) {
1191+
return;
1192+
}
1193+
branchArg = phi->getIncomingPhiValue(sourceBB);
1194+
}
1195+
newSourceBranchArgs.push_back(branchArg);
1196+
}
1197+
// Setting destBB constructs a valid TrampolineDest.
1198+
destBB = targetBranch->getDestBB();
11691199
}
11701200

11711201
/// \return If this is a basic block without any arguments and it has
@@ -1334,17 +1364,18 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
13341364

13351365
// If the destination block is a simple trampoline (jump to another block)
13361366
// then jump directly.
1337-
if (SILBasicBlock *TrampolineDest = getTrampolineDest(DestBB)) {
1338-
LLVM_DEBUG(llvm::dbgs() << "jump to trampoline from bb" << BB->getDebugID()
1339-
<< " to bb" << TrampolineDest->getDebugID() <<'\n');
1340-
SILBuilderWithScope(BI).createBranch(BI->getLoc(), TrampolineDest,
1341-
BI->getArgs());
1367+
if (auto trampolineDest = TrampolineDest(BB, DestBB)) {
1368+
LLVM_DEBUG(llvm::dbgs()
1369+
<< "jump to trampoline from bb" << BB->getDebugID() << " to bb"
1370+
<< trampolineDest.destBB->getDebugID() << '\n');
1371+
SILBuilderWithScope(BI).createBranch(BI->getLoc(), trampolineDest.destBB,
1372+
trampolineDest.newSourceBranchArgs);
13421373
// Eliminating the trampoline can expose opportunities to improve the
13431374
// new block we branch to.
13441375
if (LoopHeaders.count(DestBB))
13451376
LoopHeaders.insert(BB);
13461377

1347-
addToWorklist(TrampolineDest);
1378+
addToWorklist(trampolineDest.destBB);
13481379
BI->eraseFromParent();
13491380
removeIfDead(DestBB);
13501381
addToWorklist(BB);
@@ -1535,14 +1566,17 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15351566

15361567
// If the destination block is a simple trampoline (jump to another block)
15371568
// then jump directly.
1538-
SILBasicBlock *TrueTrampolineDest = getTrampolineDest(TrueSide);
1539-
if (TrueTrampolineDest && TrueTrampolineDest != FalseSide) {
1540-
LLVM_DEBUG(llvm::dbgs() << "true-trampoline from bb" << ThisBB->getDebugID()
1541-
<< " to bb" << TrueTrampolineDest->getDebugID()
1542-
<< '\n');
1569+
auto trueTrampolineDest = TrampolineDest(ThisBB, TrueSide);
1570+
if (trueTrampolineDest
1571+
&& trueTrampolineDest.destBB->getSinglePredecessorBlock()) {
1572+
LLVM_DEBUG(llvm::dbgs()
1573+
<< "true-trampoline from bb" << ThisBB->getDebugID() << " to bb"
1574+
<< trueTrampolineDest.destBB->getDebugID() << '\n');
1575+
SmallVector<SILValue, 4> falseArgsCopy(FalseArgs.begin(), FalseArgs.end());
15431576
SILBuilderWithScope(BI).createCondBranch(
1544-
BI->getLoc(), BI->getCondition(), TrueTrampolineDest, TrueArgs,
1545-
FalseSide, FalseArgs, BI->getTrueBBCount(), BI->getFalseBBCount());
1577+
BI->getLoc(), BI->getCondition(), trueTrampolineDest.destBB,
1578+
trueTrampolineDest.newSourceBranchArgs, FalseSide, falseArgsCopy,
1579+
BI->getTrueBBCount(), BI->getFalseBBCount());
15461580
BI->eraseFromParent();
15471581

15481582
if (LoopHeaders.count(TrueSide))
@@ -1552,15 +1586,17 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15521586
return true;
15531587
}
15541588

1555-
SILBasicBlock *FalseTrampolineDest = getTrampolineDest(FalseSide);
1556-
if (FalseTrampolineDest && FalseTrampolineDest != TrueSide) {
1557-
LLVM_DEBUG(llvm::dbgs() << "false-trampoline from bb"
1558-
<< ThisBB->getDebugID() << " to bb"
1559-
<< FalseTrampolineDest->getDebugID() << '\n');
1589+
auto falseTrampolineDest = TrampolineDest(ThisBB, FalseSide);
1590+
if (falseTrampolineDest
1591+
&& falseTrampolineDest.destBB->getSinglePredecessorBlock()) {
1592+
LLVM_DEBUG(llvm::dbgs()
1593+
<< "false-trampoline from bb" << ThisBB->getDebugID() << " to bb"
1594+
<< falseTrampolineDest.destBB->getDebugID() << '\n');
1595+
SmallVector<SILValue, 4> trueArgsCopy(TrueArgs.begin(), TrueArgs.end());
15601596
SILBuilderWithScope(BI).createCondBranch(
1561-
BI->getLoc(), BI->getCondition(), TrueSide, TrueArgs,
1562-
FalseTrampolineDest, FalseArgs, BI->getTrueBBCount(),
1563-
BI->getFalseBBCount());
1597+
BI->getLoc(), BI->getCondition(), TrueSide, trueArgsCopy,
1598+
falseTrampolineDest.destBB, falseTrampolineDest.newSourceBranchArgs,
1599+
BI->getTrueBBCount(), BI->getFalseBBCount());
15641600
BI->eraseFromParent();
15651601
if (LoopHeaders.count(FalseSide))
15661602
LoopHeaders.insert(ThisBB);
@@ -1571,31 +1607,32 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15711607

15721608
// Simplify cond_br where both sides jump to the same blocks with the same
15731609
// args.
1574-
auto condBrToBr = [&](OperandValueArrayRef branchArgs,
1575-
SILBasicBlock *newDest) {
1610+
auto condBrToBr = [&](ArrayRef<SILValue> branchArgs, SILBasicBlock *newDest) {
15761611
LLVM_DEBUG(llvm::dbgs()
15771612
<< "replace cond_br with same dests with br: " << *BI);
15781613
SILBuilderWithScope(BI).createBranch(BI->getLoc(), newDest, branchArgs);
15791614
BI->eraseFromParent();
15801615
addToWorklist(ThisBB);
15811616
++NumConstantFolded;
15821617
};
1583-
if (TrueArgs == FalseArgs) {
1584-
if (TrueTrampolineDest) {
1585-
if (TrueTrampolineDest == FalseSide
1586-
|| TrueTrampolineDest == FalseTrampolineDest) {
1587-
condBrToBr(TrueArgs, TrueTrampolineDest);
1588-
removeIfDead(TrueSide);
1589-
removeIfDead(FalseSide);
1590-
return true;
1591-
}
1592-
} else if (FalseTrampolineDest == TrueSide) {
1593-
condBrToBr(TrueArgs, FalseTrampolineDest);
1594-
removeIfDead(FalseSide);
1595-
return true;
1596-
}
1618+
if (trueTrampolineDest.destBB == FalseSide
1619+
&& trueTrampolineDest.newSourceBranchArgs == FalseArgs) {
1620+
condBrToBr(trueTrampolineDest.newSourceBranchArgs, FalseSide);
1621+
removeIfDead(TrueSide);
1622+
return true;
1623+
}
1624+
if (falseTrampolineDest.destBB == TrueSide) {
1625+
condBrToBr(falseTrampolineDest.newSourceBranchArgs, TrueSide);
1626+
removeIfDead(FalseSide);
1627+
return true;
1628+
}
1629+
if (trueTrampolineDest && (trueTrampolineDest == falseTrampolineDest)) {
1630+
condBrToBr(trueTrampolineDest.newSourceBranchArgs,
1631+
trueTrampolineDest.destBB);
1632+
removeIfDead(TrueSide);
1633+
removeIfDead(FalseSide);
1634+
return true;
15971635
}
1598-
15991636
auto *TrueTrampolineBr = getTrampolineWithoutBBArgsTerminator(TrueSide);
16001637
if (TrueTrampolineBr &&
16011638
!wouldIntroduceCriticalEdge(BI, TrueTrampolineBr->getDestBB())) {
@@ -2531,32 +2568,31 @@ bool SimplifyCFG::simplifyTryApplyBlock(TryApplyInst *TAI) {
25312568
// to trampoline jumps to the same destination block. The successor blocks
25322569
// and the destination blocks may have no arguments.
25332570
bool SimplifyCFG::simplifyTermWithIdenticalDestBlocks(SILBasicBlock *BB) {
2534-
SILBasicBlock *commonDest = nullptr;
2571+
TrampolineDest commonDest;
25352572
for (auto *SuccBlock : BB->getSuccessorBlocks()) {
25362573
if (SuccBlock->getNumArguments() != 0)
25372574
return false;
2538-
SILBasicBlock *DestBlock = getTrampolineDest(SuccBlock);
2539-
if (!DestBlock)
2575+
auto trampolineDest = TrampolineDest(BB, SuccBlock);
2576+
if (!trampolineDest) {
25402577
return false;
2578+
}
2579+
// The branch must have the same destination and same branch arguments.
25412580
if (!commonDest) {
2542-
commonDest = DestBlock;
2543-
} else if (DestBlock != commonDest) {
2581+
commonDest = std::move(trampolineDest);
2582+
} else if (trampolineDest != commonDest) {
25442583
return false;
25452584
}
25462585
}
2547-
if (!commonDest)
2586+
if (!commonDest) {
25482587
return false;
2549-
2550-
assert(commonDest->getNumArguments() == 0 &&
2551-
"getTrampolineDest should have checked that commonDest has no args");
2552-
2588+
}
25532589
TermInst *Term = BB->getTerminator();
25542590
LLVM_DEBUG(llvm::dbgs() << "replace term with identical dests: " << *Term);
2555-
SILBuilderWithScope(Term).createBranch(Term->getLoc(), commonDest,
2556-
ArrayRef<SILValue>());
2591+
SILBuilderWithScope(Term).createBranch(Term->getLoc(), commonDest.destBB,
2592+
commonDest.newSourceBranchArgs);
25572593
Term->eraseFromParent();
25582594
addToWorklist(BB);
2559-
addToWorklist(commonDest);
2595+
addToWorklist(commonDest.destBB);
25602596
return true;
25612597
}
25622598

test/SILOptimizer/abcopts.sil

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,19 +1148,21 @@ sil [_semantics "array.check_subscript"] @checkbounds3 : $@convention(method) (I
11481148

11491149
// CHECK-LABEL: sil @rangeCheck
11501150
// CHECK: bb0
1151-
// CHECK: cond_br {{.*}}, bb2, bb1
1151+
// CHECK: cond_br {{.*}}, bb1, bb2
11521152
// CHECK: bb1
1153-
// CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1
11541153
// CHECK: br bb3
11551154
// CHECK: bb2
1155+
// CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1
1156+
// CHECK: br bb4
1157+
// CHECK: bb3
11561158
// CHECK: return
1157-
// CHECK: bb3([[IV:%.*]] : $Builtin.Int64):
1159+
// CHECK: bb4([[IV:%.*]] : $Builtin.Int64):
11581160
// CHECK: builtin "xor_Int1"([[TRUE]]
11591161
// CHECK: builtin "xor_Int1"([[TRUE]]
11601162
// CHECK: cond_fail
1161-
// CHECK: cond_br {{.*}}, bb2, bb4
1162-
// CHECK: bb4:
1163-
// CHECK: br bb3
1163+
// CHECK: cond_br {{.*}}, bb6, bb5
1164+
// CHECK: bb5:
1165+
// CHECK: br bb4
11641166
// CHECK: }
11651167
sil @rangeCheck : $@convention(thin) (Builtin.Int64) -> () {
11661168
bb0(%0 : $Builtin.Int64):
@@ -1197,21 +1199,23 @@ bb3(%15 : $Builtin.Int64):
11971199

11981200
// CHECK-LABEL: sil @rangeCheck2
11991201
// CHECK: bb0
1200-
// CHECK: cond_br {{.*}}, bb2, bb1
1202+
// CHECK: cond_br {{.*}}, bb1, bb2
12011203
// CHECK: bb1
1202-
// CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1
1203-
// CHECK: [[FALSE:%.*]] = integer_literal $Builtin.Int1, 0
12041204
// CHECK: br bb3
12051205
// CHECK: bb2
1206+
// CHECK: [[TRUE:%.*]] = integer_literal $Builtin.Int1, -1
1207+
// CHECK: [[FALSE:%.*]] = integer_literal $Builtin.Int1, 0
1208+
// CHECK: br bb4
1209+
// CHECK: bb3
12061210
// CHECK: return
1207-
// CHECK: bb3([[IV:%.*]] : $Builtin.Int64):
1211+
// CHECK: bb4([[IV:%.*]] : $Builtin.Int64):
12081212
// CHECK: builtin "xor_Int1"([[TRUE]]
12091213
// CHECK: builtin "xor_Int1"([[TRUE]]
12101214
// CHECK: builtin "or_Int1"([[FALSE]]
12111215
// CHECK: cond_fail
1212-
// CHECK: cond_br {{.*}}, bb2, bb4
1213-
// CHECK: bb4:
1214-
// CHECK: br bb3
1216+
// CHECK: cond_br {{.*}}, bb6, bb5
1217+
// CHECK: bb5:
1218+
// CHECK: br bb4
12151219
// CHECK: }
12161220
sil @rangeCheck2 : $@convention(thin) (Builtin.Int64) -> () {
12171221
bb0(%0 : $Builtin.Int64):

0 commit comments

Comments
 (0)