Files
swift-mirror/test/SILOptimizer/lifetime_dependence/dependence_insertion.sil
Andrew Trick f984f77d13 Test LifetimeDependenceInsertion: dependence on a sub-object.
The mark_dependence should be on the sub-object, not the parent object, to avoid
exclusivity violations.
2026-07-05 15:15:53 -07:00

267 lines
11 KiB
Plaintext

// RUN: %target-sil-opt \
// RUN: -lifetime-dependence-insertion \
// RUN: -enable-address-dependencies \
// RUN: -sil-verify-all \
// RUN: -enable-experimental-feature Lifetimes \
// RUN: -enable-experimental-feature AddressableParameters \
// RUN: -enable-experimental-feature AddressableTypes \
// RUN: %s | %FileCheck %s
// REQUIRES: swift_feature_Lifetimes
// REQUIRES: swift_feature_AddressableParameters
// REQUIRES: swift_feature_AddressableTypes
sil_stage raw
import Swift
struct NE: ~Escapable {
var p: UnsafeRawPointer
@_lifetime(immortal)
init()
}
struct NCE: ~Escapable, ~Copyable {
var p: UnsafeRawPointer
@_lifetime(immortal)
init()
}
struct TrivialHolder {
var pointer: UnsafeRawPointer
}
struct Holder {
var object: AnyObject
}
@_addressableForDependencies
struct AddressableForDeps {}
protocol PAny : ~Copyable, ~Escapable {}
sil @getPtr : $@convention(thin) () -> @out UnsafeRawPointer
sil @getSpan : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
sil @getInoutSpan : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
sil @useNE : $@convention(thin) (@guaranteed NE) -> ()
sil @getExistential : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @out any PAny & ~Copyable & ~Escapable
sil @useExistential : $@convention(thin) (@in_guaranteed any PAny & ~Copyable & ~Escapable) -> ()
// Check that the inserted dependence is on the 'self' argument, not the temporary borrow.
//
// CHECK-LABEL: sil [available 9999] [ossa] @testSpanProp : $@convention(method) (@guaranteed AnyObject) -> @lifetime(borrow 0) @owned NE {
// CHECK: bb0(%0 : @guaranteed $AnyObject):
// CHECK: [[OUT:%.*]] = alloc_stack $NE
// CHECK: [[TEMP:%.*]] = begin_borrow
// CHECK: [[MV:%.*]] = moveonlywrapper_to_copyable [guaranteed] [[TEMP]]
// CHECK: [[IN:%.*]] = alloc_stack $AnyObject
// CHECK: [[SB:%.*]] = store_borrow [[MV]] to [[IN]]
// CHECK: apply %{{.*}}([[OUT]], [[SB]]) : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
// CHECK: mark_dependence_addr [unresolved] [[OUT]] on %0
// CHECK: end_borrow [[SB]]
// CHECK: end_borrow [[TEMP]]
// CHECK: [[LD:%.*]] = load [take] [[OUT]]
// CHECK: return [[LD]]
// CHECK-LABEL: } // end sil function 'testSpanProp'
sil [available 9999] [ossa] @testSpanProp : $@convention(method) (@guaranteed AnyObject) -> @lifetime(borrow 0) @owned NE {
bb0(%0 : @guaranteed $AnyObject):
// setup the 'self' variable
%1 = copyable_to_moveonlywrapper [guaranteed] %0
%2 = copy_value %1
%3 = mark_unresolved_non_copyable_value [no_consume_or_assign] %2
debug_value %3, let, name "self", argno 1
%5 = alloc_stack $NE
// temporarily borrow 'self' but don't depend on this borrow.
%6 = begin_borrow %3
%7 = moveonlywrapper_to_copyable [guaranteed] %6
%8 = alloc_stack $AnyObject
%9 = store_borrow %7 to %8
%10 = function_ref @getSpan : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
%11 = apply %10(%5, %9) : $@convention(thin) (@in_guaranteed AnyObject) -> @lifetime(borrow 0) @out NE
end_borrow %9
dealloc_stack %8
end_borrow %6
%15 = load [take] %5
dealloc_stack %5
destroy_value %3
return %15
}
// CHECK-LABEL: sil [available 9999] [ossa] @testInoutSpanProp : $@convention(method) (@inout AnyObject) -> @lifetime(borrow 0) @owned NCE {
// CHECK: bb0(%0 : $*AnyObject):
// CHECK: [[ALLOC:%.*]] = alloc_stack $NCE
// CHECK: [[MD:%.*]] = mark_unresolved_non_copyable_value [consumable_and_assignable] [[ALLOC]]
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0
// CHECK: apply %{{.*}}([[MD]], [[ACCESS]]) : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
// CHECK: mark_dependence_addr [unresolved] [[ALLOC]] on [[ACCESS]]
// CHECK: end_access [[ACCESS]]
// CHECK-LABEL: } // end sil function 'testInoutSpanProp'
sil [available 9999] [ossa] @testInoutSpanProp : $@convention(method) (@inout AnyObject) -> @lifetime(borrow 0) @owned NCE {
bb0(%0 : $*AnyObject):
%1 = alloc_stack $NCE
%2 = mark_unresolved_non_copyable_value [consumable_and_assignable] %1
%3 = begin_access [modify] [unknown] %0
%4 = function_ref @getInoutSpan : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
%5 = apply %4(%2, %3) : $@convention(thin) (@inout AnyObject) -> @lifetime(borrow 0) @out NCE
end_access %3
%7 = load [take] %2
dealloc_stack %1
return %7
}
// =============================================================================
// @_addressable
// =============================================================================
sil [ossa] @loadFromPointer : $@convention(thin) <τ_0_0> (UnsafeRawPointer) -> @out τ_0_0
sil [ossa] @addressableTrivialArg : $@convention(thin) (@in_guaranteed TrivialHolder) -> @lifetime(borrow address 0) @owned NE
// CHECK-LABEL: sil hidden [ossa] @testAddressableTrivialArg : $@convention(thin) (TrivialHolder) -> Int {
// CHECK: [[ALLOC:%.*]] = alloc_stack $TrivialHolder
// CHECK: [[APPLY:%.*]] = apply %{{.*}}([[ALLOC]]) : $@convention(thin) (@in_guaranteed TrivialHolder) -> @lifetime(borrow address 0) @owned NE
// CHECK: mark_dependence [unresolved] [[APPLY]] on [[ALLOC]]
// CHECK-LABEL: } // end sil function 'testAddressableTrivialArg'
sil hidden [ossa] @testAddressableTrivialArg : $@convention(thin) (TrivialHolder) -> Int {
bb0(%0 : $TrivialHolder):
debug_value %0, let, name "arg", argno 1
%2 = alloc_stack $TrivialHolder
store %0 to [trivial] %2
%4 = function_ref @addressableTrivialArg : $@convention(thin) (@in_guaranteed TrivialHolder) -> @lifetime(borrow address 0) @owned NE
%5 = apply %4(%2) : $@convention(thin) (@in_guaranteed TrivialHolder) -> @lifetime(borrow address 0) @owned NE
%7 = move_value [var_decl] %5
debug_value %7, let, name "ne"
%9 = alloc_stack $Int
%10 = begin_borrow %7
%11 = struct_extract %10, #NE.p
%16 = function_ref @loadFromPointer : $@convention(thin) <τ_0_0> (UnsafeRawPointer) -> @out τ_0_0
%17 = apply %16<Int>(%9, %11) : $@convention(thin) <τ_0_0> (UnsafeRawPointer) -> @out τ_0_0
end_borrow %10
%19 = load [trivial] %9
dealloc_stack %9
destroy_value %7
dealloc_stack %2
return %19
}
sil [ossa] @addressableArg : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
// CHECK-LABEL: sil hidden [ossa] @testAddressableArg : $@convention(thin) (@guaranteed Holder) -> () {
// CHECK: bb0(%0 : @guaranteed $Holder):
// CHECK: [[ALLOC:%.*]] = alloc_stack $Holder
// CHECK: [[SB:%.*]] = store_borrow %0 to [[ALLOC]]
// CHECK: [[APPLY:%.*]] = apply %{{.*}}([[SB]]) : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
// CHECK: mark_dependence [unresolved] [[APPLY]] on [[SB]]
// CHECK-LABEL: } // end sil function 'testAddressableArg'
sil hidden [ossa] @testAddressableArg : $@convention(thin) (@guaranteed Holder) -> () {
bb0(%0 : @guaranteed $Holder):
debug_value %0, let, name "arg", argno 1
%2 = alloc_stack $Holder
%3 = store_borrow %0 to %2
%4 = function_ref @addressableArg : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
%5 = apply %4(%3) : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address 0) @owned NE
end_borrow %3
%8 = move_value [var_decl] %5
debug_value %8, let, name "ne"
%useNE = function_ref @useNE : $@convention(thin) (@guaranteed NE) -> ()
%18 = apply %useNE(%8) : $@convention(thin) (@guaranteed NE) -> ()
destroy_value %8
dealloc_stack %2
%99 = tuple ()
return %99 : $()
}
// Test that source of a dependency may be a project_box and the mark_dependence will be inserted.
//
// CHECK-LABEL: sil hidden [ossa] @testBoxDependenceSource : $@convention(thin) (@inout Holder) -> () {
// CHECK: [[BOX:%[0-9]+]] = project_box %2, 0
// CHECK: [[ARG:%[0-9]+]] = begin_access [modify] [unknown] %0
// CHECK: apply %{{.*}}([[BOX]], [[ARG]]) : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @out any PAny & ~Copyable & ~Escapable
// CHECK: mark_dependence_addr [unresolved] [[BOX]] on [[ARG]]
// CHECK-LABEL: } // end sil function 'testBoxDependenceSource'
sil hidden [ossa] @testBoxDependenceSource : $@convention(thin) (@inout Holder) -> () {
bb0(%0 : $*Holder):
%1 = alloc_box ${ let any PAny & ~Copyable & ~Escapable }, let, name "span"
%2 = begin_borrow [lexical] [var_decl] %1
%3 = project_box %2, 0
%4 = begin_access [modify] [unknown] %0
%5 = function_ref @getExistential : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @out any PAny & ~Copyable & ~Escapable
%6 = apply %5(%3, %4) : $@convention(thin) (@inout Holder) -> @lifetime(borrow 0) @out any PAny & ~Copyable & ~Escapable
end_access %4
%8 = mark_unresolved_non_copyable_value [no_consume_or_assign] %3
%9 = function_ref @useExistential : $@convention(thin) (@in_guaranteed any PAny & ~Copyable & ~Escapable) -> ()
%10 = apply %9(%8) : $@convention(thin) (@in_guaranteed any PAny & ~Copyable & ~Escapable) -> ()
end_borrow %2
destroy_value %1
%13 = tuple ()
return %13
}
// rdar://181348593 (Exclusivity sub-object analysis is defeated by end_cow_mutation_addr)
//
// Test that mark_dependence is inserted immediately after the apply whose result depends on an
// @_addressableForDependencies field, and that the dependence base is the struct_element_addr into that
// field -- not the enclosing begin_access. Using the enclosing access as the dependence base would extend
// its scope over the unrelated access to 'Agg.bar' below, causing a spurious exclusivity conflict.
@_addressableForDependencies
struct Foo {
var i: Int
}
struct Bar {
var i: Int
func method()
}
struct Agg {
@_hasStorage var foo: Foo { get set }
@_hasStorage var bar: Bar { get set }
}
sil @getDep : $@convention(thin) (@inout Foo) -> @lifetime(borrow address_for_deps 0) @owned NE
sil @$s3BarV6methodyyF : $@convention(method) (@in_guaranteed Bar) -> ()
// CHECK-LABEL: sil hidden [ossa] @run : $@convention(thin) (@inout Agg) -> () {
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0
// CHECK: [[FOO:%.*]] = struct_element_addr [[ACCESS]], #Agg.foo
// CHECK: [[NE:%.*]] = apply %{{.*}}([[FOO]]) : $@convention(thin) (@inout Foo) -> @lifetime(borrow address_for_deps 0) @owned NE
// CHECK: mark_dependence [unresolved] [[NE]] on [[FOO]]
// CHECK: end_access [[ACCESS]]
// CHECK-LABEL: } // end sil function 'run'
sil hidden [ossa] @run : $@convention(thin) (@inout Agg) -> () {
bb0(%0 : $*Agg):
%2 = alloc_box ${ var NE }, var, name "span"
%3 = begin_borrow [lexical] [var_decl] %2
%4 = project_box %3, 0
%5 = begin_access [modify] [unknown] %0
%6 = struct_element_addr %5, #Agg.foo
%7 = function_ref @getDep : $@convention(thin) (@inout Foo) -> @lifetime(borrow address_for_deps 0) @owned NE
%8 = apply %7(%6) : $@convention(thin) (@inout Foo) -> @lifetime(borrow address_for_deps 0) @owned NE
store %8 to [init] %4
end_access %5
%11 = begin_access [read] [unknown] %0
%12 = struct_element_addr %11, #Agg.bar
%13 = alloc_stack $Bar
copy_addr %12 to [init] %13
end_access %11
%16 = function_ref @$s3BarV6methodyyF : $@convention(method) (@in_guaranteed Bar) -> ()
%17 = apply %16(%13) : $@convention(method) (@in_guaranteed Bar) -> ()
destroy_addr %13
dealloc_stack %13
end_borrow %3
destroy_value %2
%36 = tuple ()
return %36
}