Skip to content

Commit 4ce6fcf

Browse files
committed
Fix SIL function side effects to handle unapplied escaping
Fixes rdar://113339972 DeadStoreElimination causes uninitialized closure context Before this fix, the recently enabled function side effect implementation would return no side effects for a partial apply that is not applied in the same function. This resulted in DeadStoreElimination incorrectly eliminating the initialization of the closure context. The fix is to model the effects of capturing the arguments for the closure context. The effects of running the closure body will be considered later, at the point that the closure is applied. Running the closure does, however, depend on the captured values to be valid. If the value being captured is addressible, then we need to model the effect of reading from that memory. In this case, the capture reads from a local stack slot: %stack = alloc_stack $Klass store %ref to %stack : $*Klass %closure = partial_apply [callee_guaranteed] %f(%stack) : $@convention(thin) (@in_guaranteed Klass) -> () Later, when the closure is applied, we won't have any reference back to the original stack slot. The application may not even happen in a caller. Note that, even if the closure will be applied in the current function, the side effects of the application are insufficient to cover the side effects of the capture. For example, the closure body itself may not read from an argument, but the context must still be valid in case it is copied or if the capture itself was not a bitwise-move. As an optimization, we ignore the effect of captures for on-stack partial applies. Such captures are always either a bitwise-move or, more commonly, capture the source value by address. In these cases, the side effects of applying the closure are sufficient to cover the effects of the captures. And, if an on-stack closure is not invoked in the current function (or passed to a callee) then it will never be invoked, so the captures never have effects.
1 parent 9ad5598 commit 4ce6fcf

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,24 @@ private struct CollectedEffects {
142142
handleApply(pa)
143143
checkedIfDeinitBarrier = true
144144
}
145+
// In addition to the effects of the apply, also consider the
146+
// effects of the capture, which reads the captured value in
147+
// order to move it into the context. This only applies to
148+
// addressible values, because capturing does not dereference
149+
// any class objects.
150+
//
151+
// Ignore captures for on-stack partial applies. They only
152+
// bitwise-move or capture by address, so the call to
153+
// handleApply above is sufficient. And, if they are not applied
154+
// in this function, then they are never applied.
155+
if !pa.isOnStack {
156+
// the callee and its arguments are all captured...
157+
for operand in pa.operands {
158+
if operand.value.type.isAddress {
159+
addEffects(.read, to: operand.value)
160+
}
161+
}
162+
}
145163

146164
case let fl as FixLifetimeInst:
147165
// A fix_lifetime instruction acts like a read on the operand to prevent

test/SILOptimizer/dead_store_elim.sil

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-sil-opt %s -dead-store-elimination -enable-sil-verify-all | %FileCheck %s
2+
// RUN: %target-sil-opt %s -compute-side-effects -dead-store-elimination -enable-sil-verify-all | %FileCheck %s --check-prefix=CHECK-SEA-DSE
23

34
// REQUIRES: swift_in_compiler
45

@@ -1631,3 +1632,31 @@ bb0(%0 : $UInt64, %1 : $Builtin.Word):
16311632
return %76 : $Builtin.Int8
16321633
}
16331634

1635+
class Klass {}
1636+
1637+
sil @klassClosure : $@convention(thin) (@in_guaranteed Klass) -> ()
1638+
1639+
sil @test_pa_without_apply : $@convention(thin) (@in Klass) -> @callee_guaranteed () -> () {
1640+
bb0(%0 : $*Klass):
1641+
%3 = function_ref @klassClosure : $@convention(thin) (@in_guaranteed Klass) -> ()
1642+
%4 = partial_apply [callee_guaranteed] %3(%0) : $@convention(thin) (@in_guaranteed Klass) -> ()
1643+
return %4 : $@callee_guaranteed () -> ()
1644+
}
1645+
1646+
// CHECK-SEA-DSE: sil @dont_dead_store_capture :
1647+
// CHECK-SEA-DSE: store
1648+
// CHECK-SEA-DSE: } // end sil function 'dont_dead_store_capture'
1649+
sil @dont_dead_store_capture : $@convention(thin) (@in_guaranteed (Klass, Klass)) -> () {
1650+
bb0(%0 : $*(Klass, Klass)):
1651+
%ele = tuple_element_addr %0 : $*(Klass, Klass), 1
1652+
%1 = load %ele : $*Klass
1653+
%3 = alloc_stack $Klass
1654+
store %1 to %3 : $*Klass
1655+
strong_retain %1 : $Klass
1656+
%4 = function_ref @test_pa_without_apply : $@convention(thin) (@in Klass) -> @callee_guaranteed () -> ()
1657+
%5 = apply %4(%3) : $@convention(thin) (@in Klass) -> @callee_guaranteed () -> ()
1658+
%6 = apply %5() : $@callee_guaranteed () -> ()
1659+
dealloc_stack %3 : $*Klass
1660+
%7 = tuple ()
1661+
return %7 : $()
1662+
}

test/SILOptimizer/side_effects.sil

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ bb0(%0 : $X, %1 : $*Int32):
138138

139139
// CHECK-LABEL: sil @partial_apply_load_store_to_args
140140
// CHECK-NEXT: [%0: read v**]
141-
// CHECK-NEXT: [%1: write v**]
141+
// CHECK-NEXT: [%1: read v**, write v**]
142142
// CHECK-NEXT: [%2: write c0.v**, destroy c*.v**]
143143
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
144144
// CHECK-NEXT: {{^[^[]}}
@@ -157,7 +157,7 @@ bb0(%0 : $*Int32, %1 : $*Int32, %2 : $X):
157157

158158
// CHECK-LABEL: sil @guaranteed_partial_apply_load_store_to_args
159159
// CHECK-NEXT: [%0: read v**]
160-
// CHECK-NEXT: [%1: write v**]
160+
// CHECK-NEXT: [%1: read v**, write v**]
161161
// CHECK-NEXT: [%2: write c0.v**]
162162
// CHECK-NEXT: [global: ]
163163
// CHECK-NEXT: {{^[^[]}}
@@ -827,6 +827,7 @@ bb0(%0 : $X):
827827
}
828828

829829
// CHECK-LABEL: sil @not_called_partial_apply
830+
// CHECK-NEXT: [%0: read v**]
830831
// CHECK-NEXT: [global: ]
831832
// CHECK-NEXT: {{^[^[]}}
832833
sil @not_called_partial_apply : $@convention(thin) (@in Int32) -> @owned @callee_owned (Bool) -> Int32 {
@@ -837,6 +838,8 @@ bb0(%0 : $*Int32):
837838
}
838839

839840
// CHECK-LABEL: sil @partial_apply_chain
841+
// CHECK-NEXT: [%0: read v**]
842+
// CHECK-NEXT: [%1: read v**]
840843
// CHECK-NEXT: [global: ]
841844
// CHECK-NEXT: {{^[^[]}}
842845
sil @partial_apply_chain : $@convention(thin) (@in Int32, @in Int32) -> @owned @callee_owned (Bool) -> Int32 {
@@ -849,7 +852,7 @@ bb0(%0 : $*Int32, %1 : $*Int32):
849852

850853
// CHECK-LABEL: sil @two_nonstack_partial_applies
851854
// CHECK-NEXT: [%0: read v**]
852-
// CHECK-NEXT: [%1: write v**]
855+
// CHECK-NEXT: [%1: read v**, write v**]
853856
// CHECK-NEXT: [%2: write c0.v**]
854857
// CHECK-NEXT: [global: ]
855858
// CHECK-NEXT: {{^[^[]}}

0 commit comments

Comments
 (0)