Skip to content

Commit 7a28293

Browse files
authored
Merge pull request #37361 from eeckstein/fix-abc-opt-5.4
[5.4] ArrayBoundCheckOpts: introduce a limit for the maximum dominator tree recursion depth
2 parents 549ce67 + 61d6be7 commit 7a28293

File tree

2 files changed

+106
-6
lines changed

2 files changed

+106
-6
lines changed

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ using ArrayAccessDesc = llvm::PointerIntPair<ValueBase *, 1, bool>;
6767
using IndexedArraySet = llvm::DenseSet<std::pair<ValueBase *, ArrayAccessDesc>>;
6868
using InstructionSet = llvm::SmallPtrSet<SILInstruction *, 16>;
6969

70+
// Should be more than enough to cover "usual" functions.
71+
static constexpr int maxRecursionDepth = 500;
72+
7073
/// The effect an instruction can have on array bounds.
7174
enum class ArrayBoundsEffect {
7275
kNone = 0,
@@ -378,12 +381,17 @@ static bool removeRedundantChecksInBlock(SILBasicBlock &BB, ArraySet &Arrays,
378381
static bool removeRedundantChecks(DominanceInfoNode *CurBB,
379382
ABCAnalysis &ABC,
380383
IndexedArraySet &DominatingSafeChecks,
381-
SILLoop *Loop) {
384+
SILLoop *Loop,
385+
int recursionDepth) {
382386
auto *BB = CurBB->getBlock();
383387
if (!Loop->contains(BB))
384388
return false;
385389
bool Changed = false;
386390

391+
// Avoid a stack overflow for very deep dominator trees.
392+
if (recursionDepth >= maxRecursionDepth)
393+
return false;
394+
387395
// When we come back from the dominator tree recursion we need to remove
388396
// checks that we have seen for the first time.
389397
SmallVector<std::pair<ValueBase *, ArrayAccessDesc>, 8> SafeChecksToPop;
@@ -436,7 +444,8 @@ static bool removeRedundantChecks(DominanceInfoNode *CurBB,
436444
// Traverse the children in the dominator tree inside the loop.
437445
for (auto Child: *CurBB)
438446
Changed |=
439-
removeRedundantChecks(Child, ABC, DominatingSafeChecks, Loop);
447+
removeRedundantChecks(Child, ABC, DominatingSafeChecks, Loop,
448+
recursionDepth + 1);
440449

441450
// Remove checks we have seen for the first time.
442451
std::for_each(SafeChecksToPop.begin(), SafeChecksToPop.end(),
@@ -930,7 +939,12 @@ static bool hasArrayType(SILValue Value, SILModule &M) {
930939
static bool hoistChecksInLoop(DominanceInfo *DT, DominanceInfoNode *DTNode,
931940
ABCAnalysis &ABC, InductionAnalysis &IndVars,
932941
SILBasicBlock *Preheader, SILBasicBlock *Header,
933-
SILBasicBlock *SingleExitingBlk) {
942+
SILBasicBlock *SingleExitingBlk,
943+
int recursionDepth) {
944+
945+
// Avoid a stack overflow for very deep dominator trees.
946+
if (recursionDepth >= maxRecursionDepth)
947+
return false;
934948

935949
bool Changed = false;
936950
auto *CurBB = DTNode->getBlock();
@@ -1029,7 +1043,7 @@ static bool hoistChecksInLoop(DominanceInfo *DT, DominanceInfoNode *DTNode,
10291043
// Traverse the children in the dominator tree.
10301044
for (auto Child: *DTNode)
10311045
Changed |= hoistChecksInLoop(DT, Child, ABC, IndVars, Preheader,
1032-
Header, SingleExitingBlk);
1046+
Header, SingleExitingBlk, recursionDepth + 1);
10331047

10341048
return Changed;
10351049
}
@@ -1127,7 +1141,8 @@ static bool hoistBoundsChecks(SILLoop *Loop, DominanceInfo *DT, SILLoopInfo *LI,
11271141
// check for safety outside the loop (with ABCAnalysis).
11281142
IndexedArraySet DominatingSafeChecks;
11291143
bool Changed = removeRedundantChecks(DT->getNode(Header), ABC,
1130-
DominatingSafeChecks, Loop);
1144+
DominatingSafeChecks, Loop,
1145+
/*recursionDepth*/ 0);
11311146

11321147
if (!EnableABCHoisting)
11331148
return Changed;
@@ -1233,7 +1248,8 @@ static bool hoistBoundsChecks(SILLoop *Loop, DominanceInfo *DT, SILLoopInfo *LI,
12331248

12341249
// Hoist bounds checks.
12351250
Changed |= hoistChecksInLoop(DT, DT->getNode(Header), ABC, IndVars,
1236-
Preheader, Header, SingleExitingBlk);
1251+
Preheader, Header, SingleExitingBlk,
1252+
/*recursionDepth*/ 0);
12371253
if (Changed) {
12381254
Preheader->getParent()->verify();
12391255
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %gyb %s > %t/main.sil
3+
4+
// Check that the optimization does not crash due to a stack overflow.
5+
6+
// RUN: %target-sil-opt -sil-verify-none -abcopts %t/main.sil | %FileCheck %s
7+
8+
sil_stage canonical
9+
10+
import Builtin
11+
import Swift
12+
import SwiftShims
13+
14+
struct ArrayIntBuffer {
15+
var storage : Builtin.NativeObject
16+
}
17+
18+
struct ArrayInt{
19+
var buffer : ArrayIntBuffer
20+
}
21+
22+
sil [ossa] @take_array : $@convention(thin) (@inout ArrayInt) -> () {
23+
bb0(%0 : $*ArrayInt):
24+
unreachable
25+
}
26+
27+
28+
sil public_external [ossa] [_semantics "array.check_subscript"] @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken {
29+
bb0(%0: $Int32, %1: $Bool, %2: @owned $ArrayInt):
30+
unreachable
31+
}
32+
33+
// CHECK-LABEL: sil [ossa] @test_very_deep_domtree :
34+
35+
// Currently there is nothing ot check here, because the optimization bails in
36+
// this case.
37+
// In future we might check that even with a deep domtree it can hoist the check.
38+
39+
// CHECK-LABEL: } // end sil function 'test_very_deep_domtree'
40+
sil [ossa] @test_very_deep_domtree : $@convention(thin) (Int32, @inout ArrayInt) -> Int32 {
41+
bb0(%0 : $Int32, %1 : $*ArrayInt):
42+
%%2 = integer_literal $Builtin.Int1, -1
43+
%%3 = struct $Bool (%2 : $Builtin.Int1)
44+
%%4 = struct_extract %0 : $Int32, #Int32._value
45+
%%5 = integer_literal $Builtin.Int32, 0
46+
br bb1(%5 : $Builtin.Int32)
47+
48+
bb1(%10 : $Builtin.Int32):
49+
50+
% for i in range(50000):
51+
br bb${i+2}
52+
bb${i+2}:
53+
% end
54+
55+
br bb200000
56+
57+
bb200000:
58+
%%11 = struct $Int32 (%10 : $Builtin.Int32)
59+
%%12 = function_ref @checkbounds : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
60+
%%13 = load [copy] %1 : $*ArrayInt
61+
%%17 = apply %12(%11, %3, %13) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
62+
%%18 = integer_literal $Builtin.Int32, 1
63+
%%19 = integer_literal $Builtin.Int1, -1
64+
%%20 = builtin "sadd_with_overflow_Int32"(%10 : $Builtin.Int32, %18 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
65+
%%21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0
66+
%%22 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 1
67+
cond_fail %22 : $Builtin.Int1, ""
68+
%%24 = builtin "cmp_eq_Int32"(%21 : $Builtin.Int32, %4 : $Builtin.Int32) : $Builtin.Int1
69+
cond_br %24, bb200002, bb200001
70+
71+
bb200001:
72+
br bb1(%21 : $Builtin.Int32)
73+
74+
bb200002:
75+
%%27 = function_ref @take_array : $@convention(thin) (@inout ArrayInt) -> ()
76+
%%28 = apply %27(%1) : $@convention(thin) (@inout ArrayInt) -> ()
77+
%%29 = load [copy] %1 : $*ArrayInt
78+
%%30 = apply %12(%11, %3, %29) : $@convention(method) (Int32, Bool, @owned ArrayInt) -> _DependenceToken
79+
%%33 = struct $Int32 (%21 : $Builtin.Int32)
80+
return %33 : $Int32
81+
}
82+
83+
84+

0 commit comments

Comments
 (0)