Skip to content

Commit fc95906

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 (cherry picked from commit aa00865)
1 parent 0618de3 commit fc95906

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
@@ -1880,43 +1880,88 @@ static bool onlyForwardsNone(SILBasicBlock *noneBB, SILBasicBlock *someBB,
18801880
return true;
18811881
}
18821882

1883-
/// Check whether \p noneBB has the same ultimate successor as the successor to someBB.
1884-
/// someBB noneBB
1885-
/// \ |
1886-
/// \ ... (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)
18871890
/// \ /
18881891
/// 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.
18891904
static bool hasSameUltimateSuccessor(SILBasicBlock *noneBB, SILBasicBlock *someBB) {
18901905
// Make sure that both our some, none blocks both have single successors that
18911906
// are not themselves (which can happen due to single block loops).
18921907
auto *someSuccessorBB = someBB->getSingleSuccessorBlock();
18931908
if (!someSuccessorBB || someSuccessorBB == someBB)
18941909
return false;
1910+
18951911
auto *noneSuccessorBB = noneBB->getSingleSuccessorBlock();
18961912
if (!noneSuccessorBB || noneSuccessorBB == noneBB)
18971913
return false;
18981914

1899-
// If we immediately find a diamond, return true. We are done.
1915+
// If we immediately find a simple diamond, return true. We are done.
19001916
if (noneSuccessorBB == someSuccessorBB)
19011917
return true;
19021918

1903-
// Otherwise, lets keep looking down the none case.
1904-
auto *next = noneSuccessorBB;
1905-
while (next != someSuccessorBB) {
1906-
noneSuccessorBB = next;
1907-
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);
19081928

1909-
// If we find another single successor and it is not our own block (due to a
1910-
// self-loop), continue.
1911-
if (next && next != noneSuccessorBB)
1912-
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;
19131943

1914-
// Otherwise, we either have multiple successors or a self-loop. We do not
1915-
// support this, return false.
1916-
return false;
1917-
}
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);
19181964

1919-
// At this point, we know that next must be someSuccessorBB.
19201965
return true;
19211966
}
19221967

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)