Skip to content

Commit c6df438

Browse files
committed
Add a safeguard to SimplifyCFG tryJumpThreading to avoid infinite loop peeling
rdar://73644659 (Add a safeguard to SimplifyCFG tryJumpThreading to avoid infinite loop peeling) A case of infinite loop peeling was exposed recently: ([SR-14068]: Compiling with optimisation runs indefinitely for grpc-swift) It was trivially fixed here: --- commit 8948f75 (HEAD -> fix-simplifycfg-tramp, public/fix-simplifycfg-tramp) Author: Andrew Trick <[email protected]> Date: Tue Jan 26 17:02:37 2021 Fix a SimplifyCFG typo that leads to unbounded optimization --- However, that fix isn't a strong guarantee against this behavior. The obvious complete fix is that jump-threading should not affect loop structure. But changing that requires a performance investigation. In the meantime this change introduces a simple mechanism that guarantees that a loop header is not repeatedly cloned. This safeguard is worthwhile because jump-threading across loop boundaries is kicking in more frequently now the critical edges are being split within SimplifyCFG. Note that it is both necessary and desirable to split critical edges between transformations so that SIL remains in a valid state. That allows other code in SimplifyCFG to call arbitrary SIL utilities, allows verifying SimplifyCFG by running verification between transformation, and simplifies the patters that SimplifyCFG itself needs to consider. (cherry picked from commit 8b20984)
1 parent e3e2849 commit c6df438

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class SimplifyCFG {
7878
llvm::SmallDenseMap<SILBasicBlock *, unsigned, 32> WorklistMap;
7979
// Keep track of loop headers - we don't want to jump-thread through them.
8080
SmallPtrSet<SILBasicBlock *, 32> LoopHeaders;
81+
// The set of cloned loop headers to avoid infinite loop peeling. Blocks in
82+
// this set may or may not still be LoopHeaders.
83+
// (ultimately this can be used to eliminate findLoopHeaders)
84+
SmallPtrSet<SILBasicBlock *, 4> ClonedLoopHeaders;
8185
// The cost (~ number of copied instructions) of jump threading per basic
8286
// block. Used to prevent infinite jump threading loops.
8387
llvm::SmallDenseMap<SILBasicBlock *, int, 8> JumpThreadingCost;
@@ -125,6 +129,16 @@ class SimplifyCFG {
125129
}
126130

127131
private:
132+
// Called when \p newBlock inherits the former predecessors of \p
133+
// oldBlock. e.g. if \p oldBlock was a loop header, then newBlock is now a
134+
// loop header.
135+
void substitutedBlockPreds(SILBasicBlock *oldBlock, SILBasicBlock *newBlock) {
136+
if (LoopHeaders.count(oldBlock))
137+
LoopHeaders.insert(newBlock);
138+
if (ClonedLoopHeaders.count(oldBlock))
139+
ClonedLoopHeaders.insert(newBlock);
140+
}
141+
128142
void clearWorklist() {
129143
WorklistMap.clear();
130144
WorklistList.clear();
@@ -170,8 +184,10 @@ class SimplifyCFG {
170184
// Remove it from the map as well.
171185
WorklistMap.erase(It);
172186

173-
if (LoopHeaders.count(BB))
187+
if (LoopHeaders.count(BB)) {
174188
LoopHeaders.erase(BB);
189+
ClonedLoopHeaders.erase(BB);
190+
}
175191
}
176192

177193
bool simplifyBlocks();
@@ -1082,10 +1098,13 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
10821098
return false;
10831099

10841100
// Don't jump thread through a potential header - this can produce irreducible
1085-
// control flow. Still, we make an exception for switch_enum.
1101+
// control flow and lead to infinite loop peeling.
10861102
bool DestIsLoopHeader = (LoopHeaders.count(DestBB) != 0);
10871103
if (DestIsLoopHeader) {
1088-
if (!isa<SwitchEnumInst>(destTerminator))
1104+
// Make an exception for switch_enum, but only if it's block was not already
1105+
// peeled out of it's original loop. In that case, further jump threading
1106+
// can accomplish nothing, and the loop will be infinitely peeled.
1107+
if (!isa<SwitchEnumInst>(destTerminator) || ClonedLoopHeaders.count(DestBB))
10891108
return false;
10901109
}
10911110

@@ -1127,8 +1146,14 @@ bool SimplifyCFG::tryJumpThreading(BranchInst *BI) {
11271146

11281147
// If we jump-thread a switch_enum in the loop header, we have to recalculate
11291148
// the loop header info.
1130-
if (DestIsLoopHeader)
1149+
//
1150+
// FIXME: findLoopHeaders should not be called repeatedly during simplify-cfg
1151+
// iteration. It is a whole-function analysis! It also does no nothing help to
1152+
// avoid infinite loop peeling.
1153+
if (DestIsLoopHeader) {
1154+
ClonedLoopHeaders.insert(Cloner.getNewBB());
11311155
findLoopHeaders();
1156+
}
11321157

11331158
++NumJumpThreads;
11341159
return true;
@@ -1375,8 +1400,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
13751400
for (auto &Succ : remainingBlock->getSuccessors())
13761401
addToWorklist(Succ);
13771402

1378-
if (LoopHeaders.count(deletedBlock))
1379-
LoopHeaders.insert(remainingBlock);
1403+
substitutedBlockPreds(deletedBlock, remainingBlock);
13801404

13811405
auto Iter = JumpThreadingCost.find(deletedBlock);
13821406
if (Iter != JumpThreadingCost.end()) {
@@ -1400,8 +1424,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
14001424
trampolineDest.newSourceBranchArgs);
14011425
// Eliminating the trampoline can expose opportunities to improve the
14021426
// new block we branch to.
1403-
if (LoopHeaders.count(DestBB))
1404-
LoopHeaders.insert(trampolineDest.destBB);
1427+
substitutedBlockPreds(DestBB, trampolineDest.destBB);
14051428

14061429
addToWorklist(trampolineDest.destBB);
14071430
BI->eraseFromParent();
@@ -1586,8 +1609,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15861609
BI->getTrueBBCount(), BI->getFalseBBCount());
15871610
BI->eraseFromParent();
15881611

1589-
if (LoopHeaders.count(TrueSide))
1590-
LoopHeaders.insert(ThisBB);
1612+
substitutedBlockPreds(TrueSide, ThisBB);
15911613
removeIfDead(TrueSide);
15921614
addToWorklist(ThisBB);
15931615
return true;
@@ -1605,8 +1627,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
16051627
falseTrampolineDest.destBB, falseTrampolineDest.newSourceBranchArgs,
16061628
BI->getTrueBBCount(), BI->getFalseBBCount());
16071629
BI->eraseFromParent();
1608-
if (LoopHeaders.count(FalseSide))
1609-
LoopHeaders.insert(ThisBB);
1630+
substitutedBlockPreds(FalseSide, ThisBB);
16101631
removeIfDead(FalseSide);
16111632
addToWorklist(ThisBB);
16121633
return true;

0 commit comments

Comments
 (0)