Skip to content

Commit 1d38d61

Browse files
authored
Merge pull request #27468 from gottesmm/swift-5.1-branch-rdar55861081
[simplify-cfg] Add a visitedBlocks set to hasSameUltimateSuccessor to prevent infinite loops.
2 parents 60cfb65 + fc95906 commit 1d38d61

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
@@ -1209,9 +1209,17 @@ static bool isReachable(SILBasicBlock *Block) {
12091209
}
12101210
#endif
12111211

1212+
static llvm::cl::opt<bool> SimplifyUnconditionalBranches(
1213+
"simplify-cfg-simplify-unconditional-branches", llvm::cl::init(true));
1214+
12121215
/// simplifyBranchBlock - Simplify a basic block that ends with an unconditional
12131216
/// branch.
12141217
bool SimplifyCFG::simplifyBranchBlock(BranchInst *BI) {
1218+
// If we are asked to not simplify unconditional branches (for testing
1219+
// purposes), exit early.
1220+
if (!SimplifyUnconditionalBranches)
1221+
return false;
1222+
12151223
// First simplify instructions generating branch operands since that
12161224
// can expose CFG simplifications.
12171225
bool Simplified = simplifyBranchOperands(BI->getArgs());
@@ -1872,43 +1880,88 @@ static bool onlyForwardsNone(SILBasicBlock *noneBB, SILBasicBlock *someBB,
18721880
return true;
18731881
}
18741882

1875-
/// Check whether \p noneBB has the same ultimate successor as the successor to someBB.
1876-
/// someBB noneBB
1877-
/// \ |
1878-
/// \ ... (more bbs?)
1883+
/// Check whether the \p someBB has only one single successor and that successor
1884+
/// post-dominates \p noneBB.
1885+
///
1886+
/// (maybe otherNoneBB)
1887+
/// someBB noneBB /
1888+
/// \ | v
1889+
/// \ ... more bbs? (A)
18791890
/// \ /
18801891
/// ulimateBB
1892+
///
1893+
/// This routine does not support diverging control flow in (A). This means that
1894+
/// there must not be any loops or diamonds beginning in that region. We do
1895+
/// support side-entrances from blocks not reachable from noneBB in order to
1896+
/// ensure that we properly handle other failure cases where the failure case
1897+
/// merges into .noneBB before ultimate BB.
1898+
///
1899+
/// DISCUSSION: We allow this side-entrance pattern to handle iterative
1900+
/// conditional checks which all feed the failing case through the .none
1901+
/// path. This is a common pattern in swift code. As an example consider a
1902+
/// switch statement with multiple pattern binding matching that use the same
1903+
/// cleanup code upon failure.
18811904
static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someBB) {
18821905
// Make sure that both our some, none blocks both have single successors that
18831906
// are not themselves (which can happen due to single block loops).
18841907
auto *someSuccessorBB = someBB->getSingleSuccessorBlock();
18851908
if (!someSuccessorBB || someSuccessorBB == someBB)
18861909
return false;
1910+
18871911
auto *noneSuccessorBB = noneBB->getSingleSuccessorBlock();
18881912
if (!noneSuccessorBB || noneSuccessorBB == noneBB)
18891913
return false;
18901914

1891-
// If we immediately find a diamond, return true. We are done.
1915+
// If we immediately find a simple diamond, return true. We are done.
18921916
if (noneSuccessorBB == someSuccessorBB)
18931917
return true;
18941918

1895-
// Otherwise, lets keep looking down the none case.
1896-
auto *next = noneSuccessorBB;
1897-
while (next != someSuccessorBB) {
1898-
noneSuccessorBB = next;
1899-
next = noneSuccessorBB->getSingleSuccessorBlock();
1919+
// Otherwise, lets begin a traversal along the successors of noneSuccessorBB,
1920+
// searching for someSuccessorBB, being careful to only allow for blocks to be
1921+
// visited once. This enables us to guarantee that there are not any loops or
1922+
// any sub-diamonds in the part of the CFG we are traversing. This /does/
1923+
// allow for side-entrances to the region from blocks not reachable from
1924+
// noneSuccessorBB. See function level comment above.
1925+
SILBasicBlock *iter = noneSuccessorBB;
1926+
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
1927+
visitedBlocks.insert(iter);
19001928

1901-
// If we find another single successor and it is not our own block (due to a
1902-
// self-loop), continue.
1903-
if (next && next != noneSuccessorBB)
1904-
continue;
1929+
do {
1930+
// First try to grab our single successor if we have only one. If we have no
1931+
// successor or more than one successor, bail and do not optimize.
1932+
//
1933+
// DISCUSSION: Trivially, if we do not have a successor, then we have
1934+
// reached either a return/unreachable and this path will never merge with
1935+
// the ultimate block. If we have more than one successor, then for our
1936+
// condition to pass, we must have that both successors eventually join into
1937+
// someSuccessorBB. But this would imply that either someSuccessorBB has
1938+
// more than two predecessors and or that we merge the two paths before we
1939+
// visit someSuccessorBB.
1940+
auto *succBlock = iter->getSingleSuccessorBlock();
1941+
if (!succBlock)
1942+
return false;
19051943

1906-
// Otherwise, we either have multiple successors or a self-loop. We do not
1907-
// support this, return false.
1908-
return false;
1909-
}
1944+
// Then check if our single successor block has been visited already. If so,
1945+
// we have some sort of loop or have some sort of merge point that is not
1946+
// the final merge point.
1947+
//
1948+
// NOTE: We do not need to worry about someSuccessorBB being in
1949+
// visitedBlocks since before we begin the loop, we check that
1950+
// someSuccessorBB != iter and also check that in the do-while condition. So
1951+
// we can never have visited someSuccessorBB on any previous iteration
1952+
// meaning that the only time we can have succBlock equal to someSuccessorBB
1953+
// is on the last iteration before we exit the loop.
1954+
if (!visitedBlocks.insert(succBlock).second)
1955+
return false;
1956+
1957+
// Otherwise, set iter to succBlock.
1958+
iter = succBlock;
1959+
1960+
// And then check if this new successor block is someSuccessorBB. If so, we
1961+
// break and then return true since we have found our target. Otherwise, we
1962+
// need to visit further successors, so go back around the loop.
1963+
} while (iter != someSuccessorBB);
19101964

1911-
// At this point, we know that next must be someSuccessorBB.
19121965
return true;
19131966
}
19141967

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)