Skip to content

Commit 8b20984

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.
1 parent 8948f75 commit 8b20984

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;
@@ -1367,8 +1392,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
13671392
for (auto &Succ : remainingBlock->getSuccessors())
13681393
addToWorklist(Succ);
13691394

1370-
if (LoopHeaders.count(deletedBlock))
1371-
LoopHeaders.insert(remainingBlock);
1395+
substitutedBlockPreds(deletedBlock, remainingBlock);
13721396

13731397
auto Iter = JumpThreadingCost.find(deletedBlock);
13741398
if (Iter != JumpThreadingCost.end()) {
@@ -1392,8 +1416,7 @@ bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
13921416
trampolineDest.newSourceBranchArgs);
13931417
// Eliminating the trampoline can expose opportunities to improve the
13941418
// new block we branch to.
1395-
if (LoopHeaders.count(DestBB))
1396-
LoopHeaders.insert(trampolineDest.destBB);
1419+
substitutedBlockPreds(DestBB, trampolineDest.destBB);
13971420

13981421
addToWorklist(trampolineDest.destBB);
13991422
BI->eraseFromParent();
@@ -1578,8 +1601,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15781601
BI->getTrueBBCount(), BI->getFalseBBCount());
15791602
BI->eraseFromParent();
15801603

1581-
if (LoopHeaders.count(TrueSide))
1582-
LoopHeaders.insert(ThisBB);
1604+
substitutedBlockPreds(TrueSide, ThisBB);
15831605
removeIfDead(TrueSide);
15841606
addToWorklist(ThisBB);
15851607
return true;
@@ -1597,8 +1619,7 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
15971619
falseTrampolineDest.destBB, falseTrampolineDest.newSourceBranchArgs,
15981620
BI->getTrueBBCount(), BI->getFalseBBCount());
15991621
BI->eraseFromParent();
1600-
if (LoopHeaders.count(FalseSide))
1601-
LoopHeaders.insert(ThisBB);
1622+
substitutedBlockPreds(FalseSide, ThisBB);
16021623
removeIfDead(FalseSide);
16031624
addToWorklist(ThisBB);
16041625
return true;

0 commit comments

Comments
 (0)