mirror of
https://github.com/apple/swift.git
synced 2026-10-16 16:50:49 +02:00
The mark_dependence should be on the sub-object, not the parent object, to avoid exclusivity violations.
267 lines
11 KiB
Plaintext
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
|
|
}
|