mirror of
https://github.com/apple/swift.git
synced 2026-10-02 10:58:03 +02:00
Add a SIL regression test for processKeyPath's bailout when a removed key-path user consumes a lexical owned value defined outside the dead graph. The pass must leave the function unchanged rather than shortening that value's lifetime.
670 lines
25 KiB
Plaintext
670 lines
25 KiB
Plaintext
// RUN: %target-sil-opt -enable-sil-verify-all -deadobject-elim %s | %FileCheck %s
|
|
|
|
import Swift
|
|
import Builtin
|
|
import SwiftShims
|
|
|
|
//////////
|
|
// Data //
|
|
//////////
|
|
|
|
class TrivialDestructor {
|
|
var int : Builtin.Int32
|
|
var ptr : Builtin.NativeObject
|
|
init()
|
|
deinit { }
|
|
}
|
|
|
|
class Kl {}
|
|
|
|
class NontrivialDestructor {
|
|
@_hasStorage var p : Kl
|
|
@_hasStorage var i : Int
|
|
init()
|
|
}
|
|
|
|
class ArrayStorage {
|
|
@_hasStorage var bodyField : Kl
|
|
init()
|
|
}
|
|
|
|
class Kl2 {
|
|
@_hasStorage var i: Int64
|
|
}
|
|
|
|
|
|
protocol P { }
|
|
struct X : P { }
|
|
|
|
sil @$s4main17TrivialDestructorCfD : $@convention(method) (@owned TrivialDestructor) -> () {
|
|
bb0(%0 : $TrivialDestructor):
|
|
// Alloc/Dealloc stack should not disrupt elimination of the
|
|
// alloc_ref.
|
|
%1 = alloc_stack $Builtin.Int64
|
|
dealloc_stack %1 : $*Builtin.Int64
|
|
|
|
// Storing into the struct should not disrupt elimination of the
|
|
// alloc_ref.
|
|
%2 = ref_element_addr %0 : $TrivialDestructor, #TrivialDestructor.int
|
|
%3 = integer_literal $Builtin.Int32, 1
|
|
store %3 to %2 : $*Builtin.Int32
|
|
|
|
// Calling a builtin without side effects should not disrupt
|
|
// elimination of the alloc_ref.
|
|
%4 = integer_literal $Builtin.Int32, 0
|
|
%6 = builtin "xor_Int32" (%4 : $Builtin.Int32, %3 : $Builtin.Int32) : $Builtin.Int32
|
|
|
|
// Calling dealloc ref on self should not disrupt elimination of the
|
|
// alloc_ref.
|
|
dealloc_ref %0 : $TrivialDestructor
|
|
|
|
%7 = tuple()
|
|
return %7 : $()
|
|
}
|
|
|
|
// Fake destructor for NontrivialDestructor
|
|
sil [ossa] @$s4main20NontrivialDestructorCfD : $@convention(method) (@owned NontrivialDestructor) -> () {
|
|
bb0(%0 : @owned $NontrivialDestructor):
|
|
dealloc_ref %0 : $NontrivialDestructor
|
|
%7 = tuple()
|
|
return %7 : $()
|
|
}
|
|
|
|
sil [ossa] @$s4main3Kl2CfD : $@convention(method) (@owned Kl2) -> () {
|
|
bb0(%0 : @owned $Kl2):
|
|
%1 = begin_borrow %0
|
|
%2 = unchecked_ref_cast %1 to $Builtin.NativeObject
|
|
%3 = unchecked_ownership_conversion %2, @guaranteed to @owned
|
|
end_borrow %1
|
|
end_lifetime %0
|
|
%6 = unchecked_ref_cast %3 to $Kl2
|
|
dealloc_ref %6
|
|
%8 = tuple ()
|
|
return %8
|
|
}
|
|
|
|
sil @ptr_user : $@convention(thin) (Builtin.NativeObject) -> ()
|
|
sil @int_user : $@convention(thin) (Builtin.Int32) -> ()
|
|
|
|
///////////
|
|
// Tests //
|
|
///////////
|
|
|
|
// Trivial case. Remove the alloc_ref.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_trivial : $@convention(thin) () -> () {
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil [ossa] @trivial_destructor_trivial : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
destroy_value %0 : $TrivialDestructor
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
// If the destructor is not called, we don't care about it.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @devirtualized_destructor : $@convention(thin) () -> () {
|
|
// CHECK: bb0
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-NEXT: return
|
|
sil [ossa] @devirtualized_destructor : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $NontrivialDestructor
|
|
%1 = begin_dealloc_ref %0 : $NontrivialDestructor of %0 : $NontrivialDestructor
|
|
fix_lifetime %1 : $NontrivialDestructor
|
|
dealloc_ref %1 : $NontrivialDestructor
|
|
%4 = tuple()
|
|
return %4 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @store_to_non_trivial_property1
|
|
// CHECK-NOT: alloc_ref [stack] $NontrivialDestructor
|
|
// CHECK: } // end sil function 'store_to_non_trivial_property1'
|
|
sil [ossa] @store_to_non_trivial_property1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = apply undef () : $@convention(thin) () -> @owned Kl
|
|
%1 = alloc_ref [stack] $NontrivialDestructor
|
|
%2 = begin_borrow %1 : $NontrivialDestructor
|
|
%3 = ref_element_addr %2 : $NontrivialDestructor, #NontrivialDestructor.p
|
|
store %0 to [init] %3 : $*Kl
|
|
end_borrow %2 : $NontrivialDestructor
|
|
%6 = begin_dealloc_ref %1 : $NontrivialDestructor of %1 : $NontrivialDestructor
|
|
destroy_value %6 : $NontrivialDestructor
|
|
dealloc_stack_ref %1 : $NontrivialDestructor
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @store_to_trivial_property
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK: } // end sil function 'store_to_trivial_property'
|
|
sil [ossa] @store_to_trivial_property : $@convention(thin) (Int) -> () {
|
|
bb0(%0 : $Int):
|
|
%1 = alloc_ref [stack] $NontrivialDestructor
|
|
%2 = begin_borrow %1 : $NontrivialDestructor
|
|
%3 = ref_element_addr %2 : $NontrivialDestructor, #NontrivialDestructor.i
|
|
%4 = begin_access [modify] [dynamic] %3 : $*Int
|
|
store %0 to [trivial] %4 : $*Int
|
|
end_access %4 : $*Int
|
|
end_borrow %2 : $NontrivialDestructor
|
|
%6 = begin_dealloc_ref %1 : $NontrivialDestructor of %1 : $NontrivialDestructor
|
|
dealloc_ref %6 : $NontrivialDestructor
|
|
dealloc_stack_ref %1 : $NontrivialDestructor
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
|
|
// We load/use a pointer from the alloc_ref, do nothing.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_load : $@convention(thin) () -> () {
|
|
// CHECK: alloc_ref
|
|
sil [ossa] @trivial_destructor_load : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
%1 = begin_borrow %0 : $TrivialDestructor
|
|
%2 = ref_element_addr %1 : $TrivialDestructor, #TrivialDestructor.int
|
|
%3 = load [trivial] %2 : $*Builtin.Int32
|
|
%4 = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> ()
|
|
apply %4 (%3) : $@convention(thin) (Builtin.Int32) -> ()
|
|
end_borrow %1 : $TrivialDestructor
|
|
destroy_value %0 : $TrivialDestructor
|
|
%r = tuple()
|
|
return %r : $()
|
|
}
|
|
|
|
// We store into the alloc_ref, eliminate it!
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_store_into : $@convention(thin) () -> () {
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK-LABEL: } // end sil function 'trivial_destructor_store_into'
|
|
sil [ossa] @trivial_destructor_store_into : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
%b = begin_borrow %0 : $TrivialDestructor
|
|
%1 = ref_element_addr %b : $TrivialDestructor, #TrivialDestructor.int
|
|
%2 = integer_literal $Builtin.Int32, 5
|
|
store %2 to [trivial] %1 : $*Builtin.Int32
|
|
end_borrow %b : $TrivialDestructor
|
|
destroy_value %0 : $TrivialDestructor
|
|
%4 = tuple()
|
|
return %4 : $()
|
|
}
|
|
|
|
// We store a pointer from the alloc_ref, don't do anything!
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_store_ptr
|
|
// CHECK: alloc_ref
|
|
// CHECK-LABEL: } // end sil function 'trivial_destructor_store_ptr'
|
|
sil [ossa] @trivial_destructor_store_ptr : $@convention(thin) (@inout Builtin.RawPointer) -> () {
|
|
bb0(%0 : $*Builtin.RawPointer):
|
|
%1 = alloc_ref $TrivialDestructor
|
|
%b = begin_borrow %1 : $TrivialDestructor
|
|
%2 = ref_element_addr %b : $TrivialDestructor, #TrivialDestructor.int
|
|
%3 = address_to_pointer %2 : $*Builtin.Int32 to $Builtin.RawPointer
|
|
store %3 to [trivial] %0 : $*Builtin.RawPointer
|
|
end_borrow %b : $TrivialDestructor
|
|
destroy_value %1 : $TrivialDestructor
|
|
%4 = tuple()
|
|
return %4 : $()
|
|
}
|
|
|
|
// We are returning the alloc_ref, do nothing.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_return_value_use :
|
|
// CHECK: alloc_ref
|
|
// CHECK-LABEL: } // end sil function 'trivial_destructor_return_value_use'
|
|
sil [ossa] @trivial_destructor_return_value_use : $@convention(thin) () -> @owned TrivialDestructor {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
return %0 : $TrivialDestructor
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_refcount_inst_on_value : $@convention(thin) () -> () {
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK-LABEL: } // end sil function 'trivial_destructor_refcount_inst_on_value'
|
|
sil [ossa] @trivial_destructor_refcount_inst_on_value : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
%copy = copy_value %0 : $TrivialDestructor
|
|
destroy_value %copy : $TrivialDestructor
|
|
destroy_value %0 : $TrivialDestructor
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
class NonTrivialDestructor {
|
|
var ptr : Builtin.NativeObject
|
|
|
|
init()
|
|
deinit { }
|
|
}
|
|
|
|
sil [ossa] @_TFC4main17NonTrivialDestructorD : $@convention(method) (@owned NonTrivialDestructor) -> () {
|
|
bb0(%0 : @owned $NonTrivialDestructor):
|
|
%b = begin_borrow %0 : $NonTrivialDestructor
|
|
%1 = ref_element_addr %b : $NonTrivialDestructor, #NonTrivialDestructor.ptr
|
|
%2 = load [copy] %1 : $*Builtin.NativeObject
|
|
end_borrow %b : $NonTrivialDestructor
|
|
destroy_value %2 : $Builtin.NativeObject
|
|
dealloc_ref %0 : $NonTrivialDestructor
|
|
%3 = tuple()
|
|
return %3 : $()
|
|
}
|
|
|
|
class NonTrivialDestructor2 {
|
|
var ptr : NonTrivialDestructor
|
|
|
|
init()
|
|
deinit { }
|
|
}
|
|
|
|
// Test if dealloc_ref on a different value disrupts the algorithm.
|
|
sil [ossa] @_TFC4main17NonTrivialDestructor2D : $@convention(method) (@owned NonTrivialDestructor2) -> () {
|
|
bb0(%0 : @owned $NonTrivialDestructor2):
|
|
%b = begin_borrow %0 : $NonTrivialDestructor2
|
|
%1 = ref_element_addr %b : $NonTrivialDestructor2, #NonTrivialDestructor2.ptr
|
|
%2 = load [copy] %1 : $*NonTrivialDestructor
|
|
end_borrow %b : $NonTrivialDestructor2
|
|
dealloc_ref %2 : $NonTrivialDestructor
|
|
dealloc_ref %0 : $NonTrivialDestructor2
|
|
%3 = tuple()
|
|
return %3 : $()
|
|
}
|
|
|
|
class NonTrivialDestructor3 {
|
|
var ptr : Builtin.NativeObject
|
|
|
|
init()
|
|
deinit { }
|
|
}
|
|
|
|
// Make sure a non-builtin apply disrupts the algorithm.
|
|
sil [ossa] @_TFC4main17NonTrivialDestructor3D : $@convention(method) (@owned NonTrivialDestructor3) -> () {
|
|
bb0(%0 : @owned $NonTrivialDestructor3):
|
|
%b = begin_borrow %0 : $NonTrivialDestructor3
|
|
%1 = ref_element_addr %b : $NonTrivialDestructor3, #NonTrivialDestructor3.ptr
|
|
%2 = load [take] %1 : $*Builtin.NativeObject
|
|
%3 = function_ref @ptr_user : $@convention(thin) (Builtin.NativeObject) -> ()
|
|
apply %3 (%2) : $@convention(thin) (Builtin.NativeObject) -> ()
|
|
destroy_value %2 : $Builtin.NativeObject
|
|
end_borrow %b : $NonTrivialDestructor3
|
|
dealloc_ref %0 : $NonTrivialDestructor3
|
|
%4 = tuple()
|
|
return %4 : $()
|
|
}
|
|
|
|
class NonTrivialObjCDestructor {
|
|
var ptr : Builtin.NativeObject
|
|
|
|
init() { }
|
|
deinit { }
|
|
}
|
|
|
|
// Make sure we do not handle objc_methods
|
|
sil [ossa] @_TFC4main17NonTrivialObjCDestructorD : $@convention(objc_method) (@owned NonTrivialObjCDestructor) -> () {
|
|
bb0(%0 : @owned $NonTrivialObjCDestructor):
|
|
dealloc_ref %0 : $NonTrivialObjCDestructor
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @non_trivial_destructor_objc_destructor : $@convention(thin) () -> () {
|
|
// CHECK: alloc_ref
|
|
// CHECK-LABEL: } // end sil function 'non_trivial_destructor_objc_destructor'
|
|
sil [ossa] @non_trivial_destructor_objc_destructor : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $NonTrivialObjCDestructor
|
|
destroy_value %0 : $NonTrivialObjCDestructor
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @non_trivial_destructor_on_stack : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK-NEXT: %0 = tuple ()
|
|
// CHECK-NEXT: return %0
|
|
sil [ossa] @non_trivial_destructor_on_stack : $@convention(thin) () -> () {
|
|
%0 = alloc_stack $NonTrivialDestructor
|
|
dealloc_stack %0 : $*NonTrivialDestructor
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
sil [ossa] @non_trivial_destructor_on_stack_with_stores1 : $@convention(thin) (@owned NonTrivialDestructor) -> () {
|
|
bb0(%0 : @owned $NonTrivialDestructor):
|
|
%copy = copy_value %0 : $NonTrivialDestructor
|
|
%1 = alloc_stack $NonTrivialDestructor
|
|
store %0 to [init] %1 : $*NonTrivialDestructor
|
|
store %copy to [assign] %1 : $*NonTrivialDestructor
|
|
destroy_addr %1 : $*NonTrivialDestructor
|
|
dealloc_stack %1 : $*NonTrivialDestructor
|
|
%2 = tuple()
|
|
return %2 : $()
|
|
}
|
|
|
|
sil [ossa] @non_trivial_destructor_on_stack_with_stores2 : $@convention(thin) (@owned NonTrivialDestructor, @owned NonTrivialDestructor) -> () {
|
|
bb0(%0 : @owned $NonTrivialDestructor, %1 : @owned $NonTrivialDestructor):
|
|
%2 = alloc_stack $NonTrivialDestructor
|
|
store %0 to [init] %2 : $*NonTrivialDestructor
|
|
store %1 to [assign] %2 : $*NonTrivialDestructor
|
|
destroy_addr %2 : $*NonTrivialDestructor
|
|
dealloc_stack %2 : $*NonTrivialDestructor
|
|
%3 = tuple()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_on_stack : $@convention(thin) () -> () {
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: return
|
|
sil [ossa] @trivial_destructor_on_stack : $@convention(thin) () -> () {
|
|
%0 = alloc_stack $Int
|
|
destroy_addr %0 : $*Int
|
|
dealloc_stack %0 : $*Int
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_dead_enum_stackloc :
|
|
// CHECK: bb0:
|
|
// CHECK-NEXT: %0 = tuple ()
|
|
// CHECK-NEXT: return %0
|
|
sil [ossa] @remove_dead_enum_stackloc : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $FloatingPointRoundingRule
|
|
inject_enum_addr %0 : $*FloatingPointRoundingRule, #FloatingPointRoundingRule.toNearestOrEven!enumelt
|
|
destroy_addr %0 : $*FloatingPointRoundingRule
|
|
dealloc_stack %0 : $*FloatingPointRoundingRule
|
|
%1 = tuple ()
|
|
return %1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_dead_optional_existential :
|
|
// CHECK: bb0(%0 : $X):
|
|
// CHECK-NEXT: %1 = tuple ()
|
|
// CHECK-NEXT: return %1
|
|
sil [ossa] @remove_dead_optional_existential : $@convention(thin) (X) -> () {
|
|
bb0(%0 : $X):
|
|
%3 = alloc_stack $Optional<P>
|
|
%4 = init_enum_data_addr %3 : $*Optional<P>, #Optional.some!enumelt
|
|
%5 = init_existential_addr %4 : $*P, $X
|
|
store %0 to [trivial] %5 : $*X
|
|
inject_enum_addr %3 : $*Optional<P>, #Optional.some!enumelt
|
|
destroy_addr %3 : $*Optional<P>
|
|
dealloc_stack %3 : $*Optional<P>
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// If we have an instruction that mayTrap that uses the alloc_ref, we can't
|
|
// eliminate it.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @trivial_destructor_may_trap : $@convention(thin) () -> () {
|
|
// CHECK: alloc_ref
|
|
sil [ossa] @trivial_destructor_may_trap : $@convention(thin) () -> () {
|
|
%0 = alloc_ref $TrivialDestructor
|
|
%1 = unconditional_checked_cast %0 : $TrivialDestructor to AnyObject
|
|
destroy_value %1 : $AnyObject
|
|
%4 = tuple()
|
|
return %4 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_dead_array_with_destroy_simple
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK: } // end sil function 'remove_dead_array_with_destroy_simple'
|
|
sil [ossa] @remove_dead_array_with_destroy_simple : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = apply undef () : $@convention(thin) () -> @owned Kl
|
|
%copy = copy_value %0 : $Kl
|
|
%3 = integer_literal $Builtin.Word, 2
|
|
%4 = alloc_ref [tail_elems $Kl * %3 : $Builtin.Word] $ArrayStorage
|
|
%5 = begin_borrow %4 : $ArrayStorage
|
|
%11 = ref_tail_addr %5 : $ArrayStorage, $Kl
|
|
store %0 to [init] %11 : $*Kl
|
|
%27 = integer_literal $Builtin.Word, 1
|
|
%28 = index_addr %11 : $*Kl, %27 : $Builtin.Word
|
|
store %copy to [init] %28 : $*Kl
|
|
end_borrow %5 : $ArrayStorage
|
|
%6 = begin_dealloc_ref %4 : $ArrayStorage of %4 : $ArrayStorage
|
|
%7 = begin_borrow %6 : $ArrayStorage
|
|
%8 = ref_tail_addr %7 : $ArrayStorage, $Kl
|
|
%65 = address_to_pointer %8 : $*Kl to $Builtin.RawPointer
|
|
%66 = metatype $@thick Kl.Type
|
|
%67 = builtin "destroyArray"<Kl>(%66 : $@thick Kl.Type, %65 : $Builtin.RawPointer, %3 : $Builtin.Word) : $()
|
|
end_borrow %7 : $ArrayStorage
|
|
dealloc_ref %6 : $ArrayStorage
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_dead_array_with_destroy_complex
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK: } // end sil function 'remove_dead_array_with_destroy_complex'
|
|
sil [ossa] @remove_dead_array_with_destroy_complex : $@convention(thin) (@guaranteed String, Int, UInt) -> () {
|
|
bb0(%0 : @guaranteed $String, %1 : $Int, %2 : $UInt):
|
|
%copy0 = copy_value %0 : $String
|
|
%copy1 = copy_value %0 : $String
|
|
%3 = integer_literal $Builtin.Word, 2
|
|
%4 = alloc_ref [stack] [tail_elems $(Int, String) * %3 : $Builtin.Word] $_ContiguousArrayStorage<(Int, String)>
|
|
%5 = upcast %4 : $_ContiguousArrayStorage<(Int, String)> to $__ContiguousArrayStorageBase
|
|
%6 = struct $_SwiftArrayBodyStorage (%1 : $Int, %2 : $UInt)
|
|
%7 = struct $_ArrayBody (%6 : $_SwiftArrayBodyStorage)
|
|
%b = begin_borrow %5 : $__ContiguousArrayStorageBase
|
|
%9 = ref_element_addr %b : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
|
|
store %7 to [trivial] %9 : $*_ArrayBody
|
|
%11 = ref_tail_addr %b : $__ContiguousArrayStorageBase, $(Int, String)
|
|
%12 = tuple_element_addr %11 : $*(Int, String), 0
|
|
%13 = tuple_element_addr %11 : $*(Int, String), 1
|
|
store %1 to [trivial] %12 : $*Int
|
|
store %copy0 to [init] %13 : $*String
|
|
%27 = integer_literal $Builtin.Word, 1
|
|
%28 = index_addr %11 : $*(Int, String), %27 : $Builtin.Word
|
|
%29 = tuple_element_addr %28 : $*(Int, String), 0
|
|
%30 = tuple_element_addr %28 : $*(Int, String), 1
|
|
store %1 to [trivial] %29 : $*Int
|
|
store %copy1 to [init] %30 : $*String
|
|
end_borrow %b : $__ContiguousArrayStorageBase
|
|
%31 = begin_dealloc_ref %5 : $__ContiguousArrayStorageBase of %4 : $_ContiguousArrayStorage<(Int, String)>
|
|
%b2 = begin_borrow %31 : $__ContiguousArrayStorageBase
|
|
%64 = ref_tail_addr %b2 : $__ContiguousArrayStorageBase, $(Int, String)
|
|
%65 = address_to_pointer %64 : $*(Int, String) to $Builtin.RawPointer
|
|
%66 = metatype $@thick (Int, String).Type
|
|
%67 = builtin "destroyArray"<(Int, String)>(%66 : $@thick (Int, String).Type, %65 : $Builtin.RawPointer, %3 : $Builtin.Word) : $()
|
|
end_borrow %b2 : $__ContiguousArrayStorageBase
|
|
dealloc_ref %31 : $__ContiguousArrayStorageBase
|
|
dealloc_stack_ref %4 : $_ContiguousArrayStorage<(Int, String)>
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
struct II { }
|
|
|
|
struct I {
|
|
@_hasStorage public let x: II { get }
|
|
}
|
|
|
|
struct S2 { }
|
|
|
|
sil @getter1 : $@convention(method) (@guaranteed KeyPath<I, II>, S2) -> @out II
|
|
sil @kpgetter1 : $@convention(keypath_accessor_getter) (@in_guaranteed S2, @in_guaranteed KeyPath<I, II>) -> @out II
|
|
sil @getter2 : $@convention(keypath_accessor_getter) (@guaranteed String, Foo) -> @owned String
|
|
sil @kpgetter2 : $@convention(keypath_accessor_getter) (@in_guaranteed Foo, @in_guaranteed KeyPath<I, II>) -> @out String
|
|
sil @equ : $@convention(keypath_accessor_equals) (@in_guaranteed KeyPath<I, II>, @in_guaranteed KeyPath<I, II>) -> Bool
|
|
sil @hsh : $@convention(keypath_accessor_hash) (@in_guaranteed KeyPath<I, II>) -> Int
|
|
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_dead_keypath
|
|
// CHECK-NOT: keypath
|
|
// CHECK: } // end sil function 'remove_dead_keypath'
|
|
sil [ossa] @remove_dead_keypath: $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = keypath $KeyPath<I, II>, (root $I; stored_property #I.x : $II)
|
|
%1 = keypath $KeyPath<S2, II>, (root $S2; gettable_property $II, id @getter1 : $@convention(method) (@guaranteed KeyPath<I, II>, S2) -> @out II, getter @kpgetter1: $@convention(keypath_accessor_getter) (@in_guaranteed S2, @in_guaranteed KeyPath<I, II>) -> @out II, indices [%$0 : $KeyPath<I, II> : $KeyPath<I, II>], indices_equals @equ : $@convention(keypath_accessor_equals) (@in_guaranteed KeyPath<I, II>, @in_guaranteed KeyPath<I, II>) -> Bool, indices_hash @hsh : $@convention(keypath_accessor_hash) (@in_guaranteed KeyPath<I, II>) -> Int) (%0)
|
|
destroy_value %1 : $KeyPath<S2, II>
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
struct Foo {
|
|
subscript(x: String) -> String { get }
|
|
init()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @remove_nontrivial_dead_keypath
|
|
// CHECK-NOT: keypath
|
|
// CHECK: destroy_value %0
|
|
// CHECK-NOT: keypath
|
|
// CHECK: } // end sil function 'remove_nontrivial_dead_keypath'
|
|
sil [ossa] @remove_nontrivial_dead_keypath : $@convention(thin) (@owned String) -> () {
|
|
bb0(%0 : @owned $String):
|
|
%3 = keypath $KeyPath<Foo, String>, (root $Foo; gettable_property $String, id @getter2 : $@convention(keypath_accessor_getter) (@guaranteed String, Foo) -> @owned String, getter @kpgetter2 : $@convention(keypath_accessor_getter) (@in_guaranteed Foo, @in_guaranteed KeyPath<I, II>) -> @out String, indices [%$0 : $String : $String], indices_equals @equ : $@convention(keypath_accessor_equals) (@in_guaranteed KeyPath<I, II>, @in_guaranteed KeyPath<I, II>) -> Bool, indices_hash @hsh : $@convention(keypath_accessor_hash) (@in_guaranteed KeyPath<I, II>) -> Int) (%0) // user: %4
|
|
destroy_value %3 : $KeyPath<Foo, String>
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
struct KeyPathRoot {
|
|
@_hasStorage var int: Int { get set }
|
|
}
|
|
|
|
struct KeyPathWrapper<Root, Value> {
|
|
@_hasStorage var object: Kl { get set }
|
|
@_hasStorage var value: Optional<Value> { get set }
|
|
@_hasStorage var keyPath: KeyPath<Root, Value> { get set }
|
|
}
|
|
|
|
sil @createit : $@convention(thin) () -> @owned Kl
|
|
|
|
// https://github.com/swiftlang/swift/issues/89791
|
|
// A removed keypath user may consume an owned value defined outside the dead
|
|
// object graph. DeadObjectElimination must replace that consume with an
|
|
// explicit destroy.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @dead_keypath_user_with_consumed_operand
|
|
// CHECK: %[[OBJECT:[0-9]+]] = apply
|
|
// CHECK-NEXT: destroy_value %[[OBJECT]]
|
|
// CHECK-NOT: keypath
|
|
// CHECK-NOT: struct $KeyPathWrapper
|
|
// CHECK: } // end sil function 'dead_keypath_user_with_consumed_operand'
|
|
sil [ossa] @dead_keypath_user_with_consumed_operand : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = function_ref @createit : $@convention(thin) () -> @owned Kl
|
|
%1 = apply %0() : $@convention(thin) () -> @owned Kl
|
|
%2 = keypath $any KeyPath<KeyPathRoot, Int> & Sendable, (root $KeyPathRoot; stored_property #KeyPathRoot.int : $Int)
|
|
%3 = open_existential_ref %2 to $@opened("11111111-1111-1111-1111-111111111111", any KeyPath<KeyPathRoot, Int> & Sendable) Self
|
|
%4 = upcast %3 to $KeyPath<KeyPathRoot, Int>
|
|
%5 = enum $Optional<Int>, #Optional.none!enumelt
|
|
%6 = struct $KeyPathWrapper<KeyPathRoot, Int> (%1, %5, %4)
|
|
destroy_value %6 : $KeyPathWrapper<KeyPathRoot, Int>
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
struct LexicalKeyPathWrapper<Root, Value> {
|
|
@_hasStorage var object: Kl { get set }
|
|
@_hasStorage var keyPath: KeyPath<Root, Value> { get set }
|
|
}
|
|
|
|
// A removed keypath user that consumes a lexical owned value defined outside
|
|
// the dead object graph must make DeadObjectElimination bail out. This keeps
|
|
// the function unchanged rather than shortening the lexical value's lifetime.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @dead_keypath_user_with_lexical_operand
|
|
// CHECK: move_value [lexical]
|
|
// CHECK: keypath
|
|
// CHECK: struct $LexicalKeyPathWrapper
|
|
// CHECK: } // end sil function 'dead_keypath_user_with_lexical_operand'
|
|
sil [ossa] @dead_keypath_user_with_lexical_operand : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = function_ref @createit : $@convention(thin) () -> @owned Kl
|
|
%1 = apply %0() : $@convention(thin) () -> @owned Kl
|
|
%2 = move_value [lexical] %1 : $Kl
|
|
%3 = keypath $KeyPath<KeyPathRoot, Int>, (root $KeyPathRoot; stored_property #KeyPathRoot.int : $Int)
|
|
%4 = struct $LexicalKeyPathWrapper<KeyPathRoot, Int> (%2, %3)
|
|
destroy_value %4 : $LexicalKeyPathWrapper<KeyPathRoot, Int>
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// Check that we don't crash here.
|
|
sil [ossa] @not_dominating_stores : $@convention(thin) () -> () {
|
|
bb0:
|
|
%7 = integer_literal $Builtin.Word, 1
|
|
%10 = alloc_ref [stack] [tail_elems $Kl * %7 : $Builtin.Word] $_ContiguousArrayStorage<Kl>
|
|
%b = begin_borrow %10 : $_ContiguousArrayStorage<Kl>
|
|
%11 = upcast %b : $_ContiguousArrayStorage<Kl> to $__ContiguousArrayStorageBase
|
|
%18 = ref_tail_addr %11 : $__ContiguousArrayStorageBase, $Kl
|
|
%35 = function_ref @createit : $@convention(thin) () -> @owned Kl
|
|
cond_br undef, bb1, bb2
|
|
bb1:
|
|
%36 = apply %35() : $@convention(thin) () -> @owned Kl
|
|
store %36 to [init] %18 : $*Kl
|
|
br bb3
|
|
bb2:
|
|
%46 = apply %35() : $@convention(thin) () -> @owned Kl
|
|
store %46 to [init] %18 : $*Kl
|
|
br bb3
|
|
bb3:
|
|
end_borrow %b : $_ContiguousArrayStorage<Kl>
|
|
destroy_value %10 : $_ContiguousArrayStorage<Kl>
|
|
dealloc_stack_ref %10 : $_ContiguousArrayStorage<Kl>
|
|
%1 = tuple()
|
|
return %1 : $()
|
|
}
|
|
|
|
sil [ossa] @$ss23_ContiguousArrayStorageCfD : $@convention(method) <Element> (@owned _ContiguousArrayStorage<Element>) -> () {
|
|
bb0(%0 : @owned $_ContiguousArrayStorage<Element>):
|
|
%b = begin_borrow %0 : $_ContiguousArrayStorage<Element>
|
|
%1 = ref_tail_addr %b : $_ContiguousArrayStorage<Element>, $Element
|
|
%2 = address_to_pointer %1 : $*Element to $Builtin.RawPointer
|
|
%10 = metatype $@thick Element.Type
|
|
%12 = builtin "destroyArray"<Element>(%10 : $@thick Element.Type, %2 : $Builtin.RawPointer, undef : $Builtin.Word) : $()
|
|
end_borrow %b : $_ContiguousArrayStorage<Element>
|
|
dealloc_ref %0 : $_ContiguousArrayStorage<Element>
|
|
%15 = tuple ()
|
|
return %15 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @dead_alloc_with_complex_ossa_destructor
|
|
// CHECK-NOT: alloc_ref
|
|
// CHECK: } // end sil function 'dead_alloc_with_complex_ossa_destructor'
|
|
sil [ossa] @dead_alloc_with_complex_ossa_destructor : $@convention(thin) () -> Int64 {
|
|
bb0:
|
|
%0 = alloc_ref $Kl2
|
|
%1 = move_value [lexical] %0
|
|
%2 = end_init_let_ref %1
|
|
%3 = begin_borrow %2
|
|
%4 = integer_literal $Builtin.Int64, 27
|
|
%5 = struct $Int64 (%4)
|
|
%6 = ref_element_addr %3, #Kl2.i
|
|
store %5 to [trivial] %6
|
|
end_borrow %3
|
|
%9 = move_value [lexical] [var_decl] %2
|
|
debug_value %9, let, name "k"
|
|
destroy_value %9
|
|
return %5
|
|
}
|
|
|
|
struct UInt64 {
|
|
var _value: Builtin.Int64
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_remove_store_read_via_address_to_pointer :
|
|
// CHECK: [[S:%[0-9]+]] = alloc_stack
|
|
// CHECK: tuple_element_addr [[S]]{{.*}}, 0
|
|
// CHECK: store
|
|
// CHECK: tuple_element_addr [[S]]{{.*}}, 1
|
|
// CHECK: store
|
|
// CHECK: } // end sil function 'dont_remove_store_read_via_address_to_pointer'
|
|
sil @dont_remove_store_read_via_address_to_pointer : $@convention(thin) (UInt64, UInt64) -> Builtin.Int64 {
|
|
bb0(%0 : $UInt64, %1 : $UInt64):
|
|
%2 = alloc_stack $(UInt64, UInt64)
|
|
%3 = tuple_element_addr %2 : $*(UInt64, UInt64), 0
|
|
store %0 to %3 : $*UInt64
|
|
%5 = tuple_element_addr %2 : $*(UInt64, UInt64), 1
|
|
store %1 to %5 : $*UInt64
|
|
%7 = address_to_pointer [stack_protection] %2 : $*(UInt64, UInt64) to $Builtin.RawPointer
|
|
%8 = pointer_to_address %7 : $Builtin.RawPointer to $*UInt64
|
|
%9 = struct_element_addr %8 : $*UInt64, #UInt64._value
|
|
%10 = load %9 : $*Builtin.Int64
|
|
dealloc_stack %2 : $*(UInt64, UInt64)
|
|
return %10 : $Builtin.Int64
|
|
}
|