Skip to content

Commit a9ed8bb

Browse files
authored
Merge pull request #23431 from eeckstein/fix-cowarrayopt-5.1
[5.1] SILOptimier: Fix a miscompile in COWArrayOpt.
2 parents 50a69b0 + a71d182 commit a9ed8bb

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,7 @@ class COWArrayOpt {
410410
// analyzing.
411411
SILValue CurrentArrayAddr;
412412
public:
413-
COWArrayOpt(RCIdentityFunctionInfo *RCIA, SILLoop *L,
414-
DominanceAnalysis *DA)
413+
COWArrayOpt(RCIdentityFunctionInfo *RCIA, SILLoop *L, DominanceAnalysis *DA)
415414
: RCIA(RCIA), Function(L->getHeader()->getParent()), Loop(L),
416415
Preheader(L->getLoopPreheader()), DomTree(DA->get(Function)),
417416
ColdBlocks(DA), CachedSafeLoop(false, false) {}
@@ -427,7 +426,8 @@ class COWArrayOpt {
427426
bool checkSafeArrayValueUses(UserList &ArrayValueUsers);
428427
bool checkSafeArrayElementUse(SILInstruction *UseInst, SILValue ArrayVal);
429428
bool checkSafeElementValueUses(UserOperList &ElementValueUsers);
430-
bool hoistMakeMutable(ArraySemanticsCall MakeMutable);
429+
bool hoistMakeMutable(ArraySemanticsCall MakeMutable, bool dominatesExits);
430+
bool dominatesExitingBlocks(SILBasicBlock *BB);
431431
void hoistAddressProjections(Operand &ArrayOp);
432432
bool hasLoopOnlyDestructorSafeArrayOperations();
433433
SILValue getArrayAddressBase(SILValue V);
@@ -1037,7 +1037,8 @@ void COWArrayOpt::hoistAddressProjections(Operand &ArrayOp) {
10371037

10381038
/// Check if this call to "make_mutable" is hoistable, and move it, or delete it
10391039
/// if it's already hoisted.
1040-
bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable) {
1040+
bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable,
1041+
bool dominatesExits) {
10411042
LLVM_DEBUG(llvm::dbgs() << " Checking mutable array: " <<CurrentArrayAddr);
10421043

10431044
// We can hoist address projections (even if they are only conditionally
@@ -1056,7 +1057,12 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable) {
10561057

10571058
// Check whether we can hoist make_mutable based on the operations that are
10581059
// in the loop.
1059-
if (hasLoopOnlyDestructorSafeArrayOperations()) {
1060+
// Note that in this case we don't verify that the array buffer is not aliased
1061+
// and therefore we must be conservative if the make_mutable is executed
1062+
// conditionally (i.e. doesn't dominate all exit blocks).
1063+
// The test SILOptimizer/cowarray_opt.sil: dont_hoist_if_executed_conditionally
1064+
// shows the problem.
1065+
if (hasLoopOnlyDestructorSafeArrayOperations() && dominatesExits) {
10601066
// Done. We can hoist the make_mutable.
10611067
// We still need the array uses later to check if we can add loads to
10621068
// HoistableLoads.
@@ -1106,6 +1112,16 @@ bool COWArrayOpt::hoistMakeMutable(ArraySemanticsCall MakeMutable) {
11061112
return true;
11071113
}
11081114

1115+
bool COWArrayOpt::dominatesExitingBlocks(SILBasicBlock *BB) {
1116+
llvm::SmallVector<SILBasicBlock *, 8> ExitingBlocks;
1117+
Loop->getExitingBlocks(ExitingBlocks);
1118+
for (SILBasicBlock *Exiting : ExitingBlocks) {
1119+
if (!DomTree->dominates(BB, Exiting))
1120+
return false;
1121+
}
1122+
return true;
1123+
}
1124+
11091125
bool COWArrayOpt::run() {
11101126
LLVM_DEBUG(llvm::dbgs() << " Array Opts in Loop " << *Loop);
11111127

@@ -1123,6 +1139,7 @@ bool COWArrayOpt::run() {
11231139
for (auto *BB : Loop->getBlocks()) {
11241140
if (ColdBlocks.isCold(BB))
11251141
continue;
1142+
bool dominatesExits = dominatesExitingBlocks(BB);
11261143
for (auto II = BB->begin(), IE = BB->end(); II != IE;) {
11271144
// Inst may be moved by hoistMakeMutable.
11281145
SILInstruction *Inst = &*II;
@@ -1134,7 +1151,7 @@ bool COWArrayOpt::run() {
11341151
CurrentArrayAddr = MakeMutableCall.getSelf();
11351152
auto HoistedCallEntry = ArrayMakeMutableMap.find(CurrentArrayAddr);
11361153
if (HoistedCallEntry == ArrayMakeMutableMap.end()) {
1137-
if (!hoistMakeMutable(MakeMutableCall)) {
1154+
if (!hoistMakeMutable(MakeMutableCall, dominatesExits)) {
11381155
ArrayMakeMutableMap[CurrentArrayAddr] = nullptr;
11391156
continue;
11401157
}

test/SILOptimizer/cowarray_opt.sil

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,39 @@ bb2:
195195
return %7 : $()
196196
}
197197

198+
// CHECK-LABEL: sil @dont_hoist_if_executed_conditionally
199+
// CHECK: bb0({{.*}}):
200+
// CHECK-NOT: apply
201+
// CHECK: bb1({{.*}}):
202+
// CHECK: bb2:
203+
// CHECK: apply
204+
// CHECK: bb3:
205+
sil @dont_hoist_if_executed_conditionally : $@convention(thin) (@inout MyArray<MyStruct>, @inout Builtin.Int1) -> MyArray<MyStruct> {
206+
bb0(%0 : $*MyArray<MyStruct>, %1 : $*Builtin.Int1):
207+
debug_value_addr %0 : $*MyArray<MyStruct>
208+
%2 = load %0 : $*MyArray<MyStruct>
209+
br bb1(%2 : $MyArray<MyStruct>)
210+
211+
bb1(%p1 : $MyArray<MyStruct>):
212+
cond_br undef, bb2, bb3
213+
214+
bb2:
215+
// If this block is never taken, then hoisting to bb0 would change the value of %p3.
216+
%5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray<MyStruct>) -> ()
217+
%6 = apply %5(%0) : $@convention(method) (@inout MyArray<MyStruct>) -> ()
218+
%7 = load %0 : $*MyArray<MyStruct>
219+
br bb4(%7 : $MyArray<MyStruct>)
220+
221+
bb3:
222+
br bb4(%p1 : $MyArray<MyStruct>)
223+
224+
bb4(%p2 : $MyArray<MyStruct>):
225+
cond_br undef, bb1(%p2 : $MyArray<MyStruct>), bb5(%p2 : $MyArray<MyStruct>)
226+
227+
bb5(%p3 : $MyArray<MyStruct>):
228+
return %p3 : $MyArray<MyStruct>
229+
}
230+
198231
// CHECK-LABEL: sil @cow_should_ignore_mark_dependence_addrproj_use : $@convention(thin) (@inout MyArray<MyStruct>, @inout Builtin.Int1) -> () {
199232
// CHECK: bb0(
200233
// CHECK-NOT: br bb
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -O %s -o %t/a.out
3+
// RUN: %target-run %t/a.out | %FileCheck %s
4+
5+
// REQUIRES: executable_test
6+
7+
// Check that the compiled code does not crash because of an over-release.
8+
// End-to-end test for rdar://problem/48906146.
9+
10+
struct LCRNG: RandomNumberGenerator {
11+
private var state: UInt64
12+
13+
init(seed: Int) {
14+
state = UInt64(truncatingIfNeeded: seed)
15+
for _ in 0..<10 { _ = next() }
16+
}
17+
18+
mutating func next() -> UInt64 {
19+
state = 2862933555777941757 &* state &+ 3037000493
20+
return state
21+
}
22+
}
23+
24+
25+
func test(_ body: () -> Void) {
26+
body()
27+
}
28+
29+
test {
30+
var rng = LCRNG(seed: 42)
31+
let v = Array(0..<3).shuffled(using: &rng)
32+
// CHECK: 3
33+
print(v.count)
34+
}
35+

0 commit comments

Comments
 (0)