Skip to content

Commit 1a1f731

Browse files
authored
Merge pull request #27449 from gottesmm/pr-65212fe82c5dfab8b4f1b86bdd9f513ecc63393c
[simplify-cfg] Add a visitedBlocks set to hasSameUltimateSuccessor to prevent infinite loops.
2 parents 3afbe31 + aa00865 commit 1a1f731

File tree

2 files changed

+106
-19
lines changed

2 files changed

+106
-19
lines changed

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,9 +1293,17 @@ static bool isReachable(SILBasicBlock *Block) {
12931293
}
12941294
#endif
12951295

1296+
static llvm::cl::opt<bool> SimplifyUnconditionalBranches(
1297+
"simplify-cfg-simplify-unconditional-branches", llvm::cl::init(true));
1298+
12961299
/// simplifyBranchBlock - Simplify a basic block that ends with an unconditional
12971300
/// branch.
12981301
bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
1302+
// If we are asked to not simplify unconditional branches (for testing
1303+
// purposes), exit early.
1304+
if (!SimplifyUnconditionalBranches)
1305+
return false;
1306+
12991307
// First simplify instructions generating branch operands since that
13001308
// can expose CFG simplifications.
13011309
bool Simplified = simplifyBranchOperands(BI->getArgs());
@@ -1956,43 +1964,88 @@ static bool onlyForwardsNone(SILBasicBlock *noneBB, SILBasicBlock *someBB,
19561964
return true;
19571965
}
19581966

1959-
/// Check whether \p noneBB has the same ultimate successor as the successor to someBB.
1960-
/// someBB noneBB
1961-
/// \ |
1962-
/// \ ... (more bbs?)
1967+
/// Check whether the \p someBB has only one single successor and that successor
1968+
/// post-dominates \p noneBB.
1969+
///
1970+
/// (maybe otherNoneBB)
1971+
/// someBB noneBB /
1972+
/// \ | v
1973+
/// \ ... more bbs? (A)
19631974
/// \ /
19641975
/// ulimateBB
1976+
///
1977+
/// This routine does not support diverging control flow in (A). This means that
1978+
/// there must not be any loops or diamonds beginning in that region. We do
1979+
/// support side-entrances from blocks not reachable from noneBB in order to
1980+
/// ensure that we properly handle other failure cases where the failure case
1981+
/// merges into .noneBB before ultimate BB.
1982+
///
1983+
/// DISCUSSION: We allow this side-entrance pattern to handle iterative
1984+
/// conditional checks which all feed the failing case through the .none
1985+
/// path. This is a common pattern in swift code. As an example consider a
1986+
/// switch statement with multiple pattern binding matching that use the same
1987+
/// cleanup code upon failure.
19651988
static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someBB) {
19661989
// Make sure that both our some, none blocks both have single successors that
19671990
// are not themselves (which can happen due to single block loops).
19681991
auto *someSuccessorBB = someBB->getSingleSuccessorBlock();
19691992
if (!someSuccessorBB || someSuccessorBB == someBB)
19701993
return false;
1994+
19711995
auto *noneSuccessorBB = noneBB->getSingleSuccessorBlock();
19721996
if (!noneSuccessorBB || noneSuccessorBB == noneBB)
19731997
return false;
19741998

1975-
// If we immediately find a diamond, return true. We are done.
1999+
// If we immediately find a simple diamond, return true. We are done.
19762000
if (noneSuccessorBB == someSuccessorBB)
19772001
return true;
19782002

1979-
// Otherwise, lets keep looking down the none case.
1980-
auto *next = noneSuccessorBB;
1981-
while (next != someSuccessorBB) {
1982-
noneSuccessorBB = next;
1983-
next = noneSuccessorBB->getSingleSuccessorBlock();
2003+
// Otherwise, lets begin a traversal along the successors of noneSuccessorBB,
2004+
// searching for someSuccessorBB, being careful to only allow for blocks to be
2005+
// visited once. This enables us to guarantee that there are not any loops or
2006+
// any sub-diamonds in the part of the CFG we are traversing. This /does/
2007+
// allow for side-entrances to the region from blocks not reachable from
2008+
// noneSuccessorBB. See function level comment above.
2009+
SILBasicBlock *iter = noneSuccessorBB;
2010+
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
2011+
visitedBlocks.insert(iter);
19842012

1985-
// If we find another single successor and it is not our own block (due to a
1986-
// self-loop), continue.
1987-
if (next && next != noneSuccessorBB)
1988-
continue;
2013+
do {
2014+
// First try to grab our single successor if we have only one. If we have no
2015+
// successor or more than one successor, bail and do not optimize.
2016+
//
2017+
// DISCUSSION: Trivially, if we do not have a successor, then we have
2018+
// reached either a return/unreachable and this path will never merge with
2019+
// the ultimate block. If we have more than one successor, then for our
2020+
// condition to pass, we must have that both successors eventually join into
2021+
// someSuccessorBB. But this would imply that either someSuccessorBB has
2022+
// more than two predecessors and or that we merge the two paths before we
2023+
// visit someSuccessorBB.
2024+
auto *succBlock = iter->getSingleSuccessorBlock();
2025+
if (!succBlock)
2026+
return false;
19892027

1990-
// Otherwise, we either have multiple successors or a self-loop. We do not
1991-
// support this, return false.
1992-
return false;
1993-
}
2028+
// Then check if our single successor block has been visited already. If so,
2029+
// we have some sort of loop or have some sort of merge point that is not
2030+
// the final merge point.
2031+
//
2032+
// NOTE: We do not need to worry about someSuccessorBB being in
2033+
// visitedBlocks since before we begin the loop, we check that
2034+
// someSuccessorBB != iter and also check that in the do-while condition. So
2035+
// we can never have visited someSuccessorBB on any previous iteration
2036+
// meaning that the only time we can have succBlock equal to someSuccessorBB
2037+
// is on the last iteration before we exit the loop.
2038+
if (!visitedBlocks.insert(succBlock).second)
2039+
return false;
2040+
2041+
// Otherwise, set iter to succBlock.
2042+
iter = succBlock;
2043+
2044+
// And then check if this new successor block is someSuccessorBB. If so, we
2045+
// break and then return true since we have found our target. Otherwise, we
2046+
// need to visit further successors, so go back around the loop.
2047+
} while (iter != someSuccessorBB);
19942048

1995-
// At this point, we know that next must be someSuccessorBB.
19962049
return true;
19972050
}
19982051

test/SILOptimizer/simplify_switch_enum_objc.sil

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s
22

3+
// Just make sure that we do not infinite loop when compiling without block merging.
4+
//
5+
// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -simplify-cfg -simplify-cfg-simplify-unconditional-branches=0
6+
37
// REQUIRES: objc_interop
48

59
import Swift
@@ -421,3 +425,33 @@ bb5:
421425
release_value %16 : $Optional<NSObject>
422426
switch_enum %16 : $Optional<NSObject>, case #Optional.none!enumelt: bb4, default bb3
423427
}
428+
429+
// Just make sure that we do not infinite loop here.
430+
//
431+
// CHECK-LABEL: sil @infinite_loop_2 : $@convention(thin) () -> @owned Optional<NSObject> {
432+
// CHECK: } // end sil function 'infinite_loop_2'
433+
sil @infinite_loop_2 : $@convention(thin) () -> @owned Optional<NSObject> {
434+
bb0:
435+
%0 = function_ref @infinite_loop_get_optional_nsobject : $@convention(thin) () -> @autoreleased Optional<NSObject>
436+
%1 = apply %0() : $@convention(thin) () -> @autoreleased Optional<NSObject>
437+
br bb1(%1 : $Optional<NSObject>)
438+
439+
bb1(%3 : $Optional<NSObject>):
440+
switch_enum %3 : $Optional<NSObject>, case #Optional.some!enumelt.1: bb3, case #Optional.none!enumelt: bb2
441+
442+
bb2:
443+
br bb5(%3 : $Optional<NSObject>)
444+
445+
bb3(%7 : $NSObject):
446+
br bb4(%3 : $Optional<NSObject>)
447+
448+
bb4(%9 : $Optional<NSObject>):
449+
%11 = enum $Optional<NSObject>, #Optional.none!enumelt
450+
return %11 : $Optional<NSObject>
451+
452+
bb5(%14 : $Optional<NSObject>):
453+
br bb6
454+
455+
bb6:
456+
br bb5(%14 : $Optional<NSObject>)
457+
}

0 commit comments

Comments
 (0)