Skip to content

Commit aa00865

Browse files
committed
[simplify-cfg] Add a visitedBlocks set to hasSameUltimateSuccessor to prevent infinite loops.
Previously, we were not handling properly blocks that we could visit multiple times. In this commit, I added a SmallPtrSet to ensure that we handle all of the same cases that we handled previously. The key reason that we want to follow this approach rather than something else is that the previous algorithm on purpose allowed for side-entrances from other checks since often times when we have multiple checks, all of the .none branches funnel together into a single ultimate block. This can be seen by the need of this code to support the test two_chained_calls in simplify_switch_enum_objc.sil. rdar://55861081
1 parent fab7752 commit aa00865

File tree

2 files changed

+98
-19
lines changed

2 files changed

+98
-19
lines changed

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,43 +1964,88 @@ static bool onlyForwardsNone(SILBasicBlock *noneBB, SILBasicBlock *someBB,
19641964
return true;
19651965
}
19661966

1967-
/// Check whether \p noneBB has the same ultimate successor as the successor to someBB.
1968-
/// someBB noneBB
1969-
/// \ |
1970-
/// \ ... (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)
19711974
/// \ /
19721975
/// 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.
19731988
static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someBB) {
19741989
// Make sure that both our some, none blocks both have single successors that
19751990
// are not themselves (which can happen due to single block loops).
19761991
auto *someSuccessorBB = someBB->getSingleSuccessorBlock();
19771992
if (!someSuccessorBB || someSuccessorBB == someBB)
19781993
return false;
1994+
19791995
auto *noneSuccessorBB = noneBB->getSingleSuccessorBlock();
19801996
if (!noneSuccessorBB || noneSuccessorBB == noneBB)
19811997
return false;
19821998

1983-
// If we immediately find a diamond, return true. We are done.
1999+
// If we immediately find a simple diamond, return true. We are done.
19842000
if (noneSuccessorBB == someSuccessorBB)
19852001
return true;
19862002

1987-
// Otherwise, lets keep looking down the none case.
1988-
auto *next = noneSuccessorBB;
1989-
while (next != someSuccessorBB) {
1990-
noneSuccessorBB = next;
1991-
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);
19922012

1993-
// If we find another single successor and it is not our own block (due to a
1994-
// self-loop), continue.
1995-
if (next && next != noneSuccessorBB)
1996-
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;
19972027

1998-
// Otherwise, we either have multiple successors or a self-loop. We do not
1999-
// support this, return false.
2000-
return false;
2001-
}
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);
20022048

2003-
// At this point, we know that next must be someSuccessorBB.
20042049
return true;
20052050
}
20062051

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)