mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The premise of CopyForwarding was that memory locations have their own ownership lifetime. We knew this was unmaintainable at the time, and that was the original incentive for SIL opaque values, aided by OSSA. In the meantime, we've been relying on SILGen producing reasonable SIL patterns. Unfortunately, the CopyForwarding pass is still with us while we make major changes to SIL ownership patterns and agressively optimize ownership. That introduces risk. Ultimately, the entire CopyForwarding pass should be redesigned for OSSA-only and destroy hoisting should be a simple OSSA utility where most of the work is done by AccessPath::collectUses. But in the meantime, we should remove the source of risk by limiting the CopyForwarding pass to OSSA. Any performance regressions will be recovered as OSSA moves later in the pipeline. After that, opaque values will improve even more over the current state by handling generic SIL using the more powerful CopyPropagation pass. Fixes rdar://71584600 (miscompile in CopyForwarding's release hoisting) Here's an example of the kind of SIL the CopyForwarding does not anticipate (although it only actually miscompiles in much more obscure scenarios, which is why it's so dangerous): bb0(%0 : $AnyObject): %alloc1 = alloc_stack $AnyObject store %0 to %objaddr : $*AnyObject %ref = load %objaddr : $*AnyObject %alloc2 = alloc_stack $ObjWrapper # The in-memory reference is destroyed before retaining the loaded ref. copy_addr [take] %alloc1 to [initialization] %alloc2 : $*ObjWrapper retain_value %ref : $AnyObject
58 lines
2.1 KiB
Plaintext
58 lines
2.1 KiB
Plaintext
// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -enable-destroyhoisting | %FileCheck %s
|
|
|
|
// CopyForwarding currently only runs on OSSA. This file only contains
|
|
// tests that will break is CopyForwarding were to run on non-OSSA SIL.
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
struct ObjWrapper {
|
|
var obj: AnyObject
|
|
}
|
|
|
|
// If CopyForwarding (with destroy-hoisting) runs on non-OSSA SIL it
|
|
// may result in a use-after-free in this case.
|
|
//
|
|
// In this example, some other pass, such as retain-sinking, has
|
|
// already moved an object's retain (bb1) below the destroy of the
|
|
// memory from which the object was originally loaded (copy_addr
|
|
// [take] in bb0). This seems crazy but still follows non-OSSA SIL
|
|
// rules.
|
|
//
|
|
// If destroy hoisting reorders the retain and destroy_addr in bb1,
|
|
// then the object passed as a guaranteed argument will be incorrectly
|
|
// destroyed.
|
|
// CHECK-LABEL: sil @testDestroyHoistOverRetain : $@convention(thin) (@guaranteed AnyObject) -> () {
|
|
// CHECK: bb1:
|
|
// CHECK: retain_value %{{.*}} : $AnyObject
|
|
// CHECK: destroy_addr %{{.*}} : $*ObjWrapper
|
|
// CHECK-LABEL: } // end sil function 'testDestroyHoistOverRetain'
|
|
sil @testDestroyHoistOverRetain : $@convention(thin) (@guaranteed AnyObject) -> () {
|
|
bb0(%0 : $AnyObject):
|
|
%alloc1 = alloc_stack $ObjWrapper
|
|
%objaddr = struct_element_addr %alloc1 : $*ObjWrapper, #ObjWrapper.obj
|
|
store %0 to %objaddr : $*AnyObject
|
|
%ref = load %objaddr : $*AnyObject
|
|
%alloc2 = alloc_stack $ObjWrapper
|
|
// The location loaded from is destroyed here with no corresponding
|
|
// retain.
|
|
copy_addr [take] %alloc1 to [initialization] %alloc2 : $*ObjWrapper
|
|
cond_br undef, bb1, bb2
|
|
bb1:
|
|
// This retain and destroy are not obviously related but need to
|
|
// execute in this order to cancel each other out.
|
|
retain_value %ref : $AnyObject
|
|
destroy_addr %alloc2 : $*ObjWrapper
|
|
br bb3
|
|
bb2:
|
|
copy_addr [take] %alloc2 to [initialization] %alloc1 : $*ObjWrapper
|
|
br bb3
|
|
bb3:
|
|
dealloc_stack %alloc2 : $*ObjWrapper
|
|
dealloc_stack %alloc1 : $*ObjWrapper
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|