You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[silgen] Eliminate lifetime gap caused by AutoreleasingUnsafeMutablePointer's LValue component set.
The problem here is that the AutoreleasingUnsafeMutablePointer's LValue
component would given the following Swift:
```
public class C {}
@inline(never)
func updateC(_ p: AutoreleasingUnsafeMutablePointer<C>) -> () {
p.pointee = C()
}
public func test() -> C {
var cVar = C()
updateC(&cVar)
return cVar
}
```
generate the following SIL as part of setting cVar after returning from updateC.
```
%18 = function_ref @$s11autorelease7updateCyySAyAA1CCGF : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> () // user: %19
%19 = apply %18(%17) : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> ()
%20 = load [trivial] %8 : $*@sil_unmanaged C // user: %21
%21 = unmanaged_to_ref %20 : $@sil_unmanaged C to $C // user: %22
%22 = copy_value %21 : $C // user: %23
assign %22 to %7 : $*C // id: %23
end_access %7 : $*C // id: %24
```
Once we are in Canonical SIL, we get the following SIL:
```
%18 = function_ref @$s11autorelease7updateCyySAyAA1CCGF : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> () // user: %19
%19 = apply %18(%17) : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> ()
%20 = load [trivial] %8 : $*@sil_unmanaged C // user: %21
%21 = unmanaged_to_ref %20 : $@sil_unmanaged C to $C // user: %22
%22 = copy_value %21 : $C // user: %23
store %22 to [assign] %7 : $*C // id: %23
end_access %7 : $*C // id: %24
```
which destroy hoisting then modifies by hoisting the destroy by the store assign
before the copy_value:
```
%18 = function_ref @$s11autorelease7updateCyySAyAA1CCGF : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> () // user: %19
%19 = apply %18(%17) : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> ()
destroy_addr %7 : $*C
%20 = load [trivial] %8 : $*@sil_unmanaged C // user: %21
%21 = unmanaged_to_ref %20 : $@sil_unmanaged C to $C // user: %22
%22 = copy_value %21 : $C // user: %23
store %22 to [init] %7 : $*C // id: %23
end_access %7 : $*C // id: %24
```
Given the appropriate Swift code, one could have that %21 is actually already
stored in %7 and has a ref count of 1. In such a case, the destroy_addr would
cause %21 to be deallocated before we can retain the value.
In order to fix this edge case as a bug fix, in the setter for
AutoreleasingUnsafeMutablePointer, we introduce a mark_dependence from the
copied value onto the memory. This ensures that destroy hoisting does not hoist
the destroy_addr past the mark_dependence, yielding correctness. That is we
generate the following SIL:
```
%18 = function_ref @$s11autorelease7updateCyySAyAA1CCGF : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> () // user: %19
%19 = apply %18(%17) : $@convention(thin) (AutoreleasingUnsafeMutablePointer<C>) -> ()
%20 = load [trivial] %8 : $*@sil_unmanaged C // user: %21
%21 = unmanaged_to_ref %20 : $@sil_unmanaged C to $C // user: %22
%22 = copy_value %21 : $C // user: %23
%23 = mark_dependence %22 : $C on %7 : $*C
assign %23 to %7 : $*C // id: %23
end_access %7 : $*C // id: %24
```
For those unfamiliar, mark_dependence specifies that any destroy of the memory
in %7 can not move before any use of %23.
rdar://99402398
0 commit comments