// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -deadobject-elim %s | %FileCheck %s // REQUIRES: swift_in_compiler 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() } 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 : $() } sil @ptr_user : $@convention(thin) (Builtin.NativeObject) -> () sil @int_user : $@convention(thin) (Builtin.Int32) -> () /////////// // Tests // /////////// // Trivial case. Remove the alloc_ref. // // CHECK-LABEL: sil @trivial_destructor_trivial : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_trivial : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor strong_release %0 : $TrivialDestructor %1 = tuple() return %1 : $() } // If the destructor is not called, we don't care about it. // // CHECK-LABEL: sil @devirtualized_destructor : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: tuple // CHECK-NEXT: return sil @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 : $() } // In non-OSSA we cannot remove alloc_refs with stores to non-trivial properties. // // CHECK-LABEL: sil @store_to_non_trivial_property1 // CHECK: alloc_ref [stack] $NontrivialDestructor // CHECK: } // end sil function 'store_to_non_trivial_property1' sil @store_to_non_trivial_property1 : $@convention(thin) (@owned Kl) -> () { bb0(%0 : $Kl): %20 = alloc_ref [stack] $NontrivialDestructor %21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.p store %0 to %21 : $*Kl %4 = begin_dealloc_ref %20 : $NontrivialDestructor of %20 : $NontrivialDestructor destroy_addr %21 : $*Kl dealloc_stack_ref %20 : $NontrivialDestructor %r = tuple () return %r : $() } // CHECK-LABEL: sil @store_to_non_trivial_property2 // CHECK: alloc_ref [stack] $NontrivialDestructor // CHECK: } // end sil function 'store_to_non_trivial_property2' sil @store_to_non_trivial_property2 : $@convention(thin) (@owned Kl) -> () { bb0(%0 : $Kl): %20 = alloc_ref [stack] $NontrivialDestructor %21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.p store %0 to %21 : $*Kl strong_release %20 : $NontrivialDestructor dealloc_stack_ref %20 : $NontrivialDestructor %r = tuple () return %r : $() } // CHECK-LABEL: sil @store_to_trivial_property // CHECK-NOT: alloc_ref // CHECK: } // end sil function 'store_to_trivial_property' sil @store_to_trivial_property : $@convention(thin) (Int) -> () { bb0(%0 : $Int): %20 = alloc_ref [stack] $NontrivialDestructor %21 = ref_element_addr %20 : $NontrivialDestructor, #NontrivialDestructor.i %22 = begin_access [modify] [dynamic] %21 : $*Int store %0 to %22 : $*Int end_access %22 : $*Int %6 = begin_dealloc_ref %20 : $NontrivialDestructor of %20 : $NontrivialDestructor dealloc_ref %6 : $NontrivialDestructor dealloc_stack_ref %20 : $NontrivialDestructor %r = tuple () return %r : $() } // We load/use a pointer from the alloc_ref, do nothing. // // CHECK-LABEL: sil @trivial_destructor_load : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: ref_element_addr // CHECK-NEXT: load // CHECK-NEXT: function_ref // CHECK-NEXT: function_ref // CHECK-NEXT: apply // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_load : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor %1 = ref_element_addr %0 : $TrivialDestructor, #TrivialDestructor.int %2 = load %1 : $*Builtin.Int32 %3 = function_ref @int_user : $@convention(thin) (Builtin.Int32) -> () apply %3 (%2) : $@convention(thin) (Builtin.Int32) -> () strong_release %0 : $TrivialDestructor %4 = tuple() return %4 : $() } // We store into the alloc_ref, eliminate it! // // CHECK-LABEL: sil @trivial_destructor_store_into : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_store_into : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor %1 = ref_element_addr %0 : $TrivialDestructor, #TrivialDestructor.int %2 = integer_literal $Builtin.Int32, 5 store %2 to %1 : $*Builtin.Int32 strong_release %0 : $TrivialDestructor %4 = tuple() return %4 : $() } // We store a pointer from the alloc_ref, don't do anything! // // CHECK-LABEL: sil @trivial_destructor_store_ptr // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: ref_element_addr // CHECK-NEXT: address_to_pointer // CHECK-NEXT: store // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_store_ptr : $@convention(thin) (@inout Builtin.RawPointer) -> () { bb0(%0 : $*Builtin.RawPointer): %1 = alloc_ref $TrivialDestructor %2 = ref_element_addr %1 : $TrivialDestructor, #TrivialDestructor.int %3 = address_to_pointer %2 : $*Builtin.Int32 to $Builtin.RawPointer store %3 to %0 : $*Builtin.RawPointer strong_release %1 : $TrivialDestructor %4 = tuple() return %4 : $() } // We are returning the alloc_ref, do nothing. // // CHECK-LABEL: sil @trivial_destructor_return_value_use : $@convention(thin) () -> TrivialDestructor { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: return sil @trivial_destructor_return_value_use : $@convention(thin) () -> TrivialDestructor { %0 = alloc_ref $TrivialDestructor return %0 : $TrivialDestructor } // Retains/releases on the alloc_ref can be ignored. // // CHECK-LABEL: sil @trivial_destructor_refcount_inst_on_value : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_refcount_inst_on_value : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor strong_retain %0 : $TrivialDestructor strong_release %0 : $TrivialDestructor strong_release %0 : $TrivialDestructor %1 = tuple() return %1 : $() } // Retains/Releases on a different value in the use list implies abort. // // CHECK-LABEL: sil @trivial_destructor_refcount_inst_on_diff_value : $@convention(thin) (Builtin.NativeObject) -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: ref_element_addr // CHECK-NEXT: store // CHECK-NEXT: load // CHECK-NEXT: strong_release // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @trivial_destructor_refcount_inst_on_diff_value : $@convention(thin) (Builtin.NativeObject) -> () { bb0(%0 : $Builtin.NativeObject): %1 = alloc_ref $TrivialDestructor %2 = ref_element_addr %1 : $TrivialDestructor, #TrivialDestructor.ptr store %0 to %2 : $*Builtin.NativeObject %3 = load %2 : $*Builtin.NativeObject strong_release %3 : $Builtin.NativeObject strong_release %1 : $TrivialDestructor %4 = tuple() return %4 : $() } /* sil @trivial_destructor_apply_use sil @trivial_destructor_side_effect_use */ //sil @non_trivial_destructor class NonTrivialDestructor { var ptr : Builtin.NativeObject init() deinit { } } sil @_TFC4main17NonTrivialDestructorD : $@convention(method) (@owned NonTrivialDestructor) -> () { bb0(%0 : $NonTrivialDestructor): %1 = ref_element_addr %0 : $NonTrivialDestructor, #NonTrivialDestructor.ptr %2 = load %1 : $*Builtin.NativeObject strong_retain %2 : $Builtin.NativeObject dealloc_ref %0 : $NonTrivialDestructor %3 = tuple() return %3 : $() } // Make sure that a ref count operation on a different value in the // destructor disrupts the algorithm. // // CHECK-LABEL: sil @non_trivial_destructor_refcount_on_different_value : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @non_trivial_destructor_refcount_on_different_value : $@convention(thin) () -> () { %0 = alloc_ref $NonTrivialDestructor strong_release %0 : $NonTrivialDestructor %1 = tuple() return %1 : $() } class NonTrivialDestructor2 { var ptr : NonTrivialDestructor init() deinit { } } // Test if dealloc_ref on a different value disrupts the algorithm. sil @_TFC4main17NonTrivialDestructor2D : $@convention(method) (@owned NonTrivialDestructor2) -> () { bb0(%0 : $NonTrivialDestructor2): %1 = ref_element_addr %0 : $NonTrivialDestructor2, #NonTrivialDestructor2.ptr %2 = load %1 : $*NonTrivialDestructor dealloc_ref %2 : $NonTrivialDestructor dealloc_ref %0 : $NonTrivialDestructor2 %3 = tuple() return %3 : $() } // CHECK-LABEL: sil @non_trivial_destructor_deallocref_on_different_value : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @non_trivial_destructor_deallocref_on_different_value : $@convention(thin) () -> () { %0 = alloc_ref $NonTrivialDestructor strong_release %0 : $NonTrivialDestructor %1 = tuple() return %1 : $() } class NonTrivialDestructor3 { var ptr : Builtin.NativeObject init() deinit { } } // Make sure a non-builtin apply disrupts the algorithm. sil @_TFC4main17NonTrivialDestructor3D : $@convention(method) (@owned NonTrivialDestructor3) -> () { bb0(%0 : $NonTrivialDestructor3): %1 = ref_element_addr %0 : $NonTrivialDestructor3, #NonTrivialDestructor3.ptr %2 = load %1 : $*Builtin.NativeObject %3 = function_ref @ptr_user : $@convention(thin) (Builtin.NativeObject) -> () apply %3 (%2) : $@convention(thin) (Builtin.NativeObject) -> () dealloc_ref %0 : $NonTrivialDestructor3 %4 = tuple() return %4 : $() } // CHECK-LABEL: sil @non_trivial_destructor_unknown_apply : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @non_trivial_destructor_unknown_apply : $@convention(thin) () -> () { %0 = alloc_ref $NonTrivialDestructor strong_release %0 : $NonTrivialDestructor %1 = tuple() return %1 : $() } class NonTrivialObjCDestructor { var ptr : Builtin.NativeObject init() { } deinit { } } // Make sure we do not handle objc_methods sil @_TFC4main17NonTrivialObjCDestructorD : $@convention(objc_method) (@owned NonTrivialObjCDestructor) -> () { bb0(%0 : $NonTrivialObjCDestructor): dealloc_ref %0 : $NonTrivialObjCDestructor %1 = tuple() return %1 : $() } // CHECK-LABEL: sil @non_trivial_destructor_objc_destructor : $@convention(thin) () -> () { // CHECK: bb0 // CHECK-NEXT: alloc_ref // CHECK-NEXT: strong_release // CHECK-NEXT: tuple // CHECK-NEXT: return sil @non_trivial_destructor_objc_destructor : $@convention(thin) () -> () { %0 = alloc_ref $NonTrivialObjCDestructor strong_release %0 : $NonTrivialObjCDestructor %1 = tuple() return %1 : $() } // CHECK-LABEL: sil @non_trivial_destructor_on_stack : $@convention(thin) () -> () { // CHECK: bb0: // CHECK-NEXT: %0 = tuple () // CHECK-NEXT: return %0 sil @non_trivial_destructor_on_stack : $@convention(thin) () -> () { %0 = alloc_stack $NonTrivialDestructor dealloc_stack %0 : $*NonTrivialDestructor %1 = tuple() return %1 : $() } // CHECK-LABEL: sil @trivial_destructor_on_stack : $@convention(thin) () -> () { // CHECK-NOT: alloc_stack // CHECK-NOT: dealloc_stack // CHECK: return sil @trivial_destructor_on_stack : $@convention(thin) () -> () { %0 = alloc_stack $Int destroy_addr %0 : $*Int dealloc_stack %0 : $*Int %1 = tuple() return %1 : $() } // If we have an instruction that mayTrap that uses the alloc_ref, we can't // eliminate it. // // CHECK-LABEL: sil @trivial_destructor_may_trap : $@convention(thin) () -> () { // CHECK: alloc_ref sil @trivial_destructor_may_trap : $@convention(thin) () -> () { %0 = alloc_ref $TrivialDestructor %1 = unconditional_checked_cast %0 : $TrivialDestructor to AnyObject strong_release %0 : $TrivialDestructor %4 = tuple() return %4 : $() } // CHECK-LABEL: sil @remove_dead_enum_stackloc // CHECK: bb0: // CHECK-NEXT: %0 = tuple () // CHECK-NEXT: return %0 sil @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 : $() } protocol P { } struct X : P { } // CHECK-LABEL: sil @remove_dead_optional_existential // CHECK: bb0(%0 : $X): // CHECK-NEXT: %1 = tuple () // CHECK-NEXT: return %1 sil @remove_dead_optional_existential : $@convention(thin) (X) -> () { bb0(%0 : $X): %3 = alloc_stack $Optional
%4 = init_enum_data_addr %3 : $*Optional
, #Optional.some!enumelt %5 = init_existential_addr %4 : $*P, $X store %0 to %5 : $*X inject_enum_addr %3 : $*Optional
, #Optional.some!enumelt destroy_addr %3 : $*Optional
dealloc_stack %3 : $*Optional
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil @remove_dead_array_with_destroy_simple
// CHECK-NOT: alloc_ref
// CHECK-NOT: dealloc_ref
// CHECK: strong_release %0 : $Kl
// CHECK-NEXT: strong_release %0 : $Kl
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK: } // end sil function 'remove_dead_array_with_destroy_simple'
sil @remove_dead_array_with_destroy_simple : $@convention(thin) (@owned Kl) -> () {
bb0(%0 : $Kl):
%3 = integer_literal $Builtin.Word, 2
%4 = alloc_ref [tail_elems $Kl * %3 : $Builtin.Word] $ArrayStorage
%11 = ref_tail_addr %4 : $ArrayStorage, $Kl
store %0 to %11 : $*Kl
%27 = integer_literal $Builtin.Word, 1
%28 = index_addr %11 : $*Kl, %27 : $Builtin.Word
retain_value %0 : $Kl
store %0 to %28 : $*Kl
%6 = begin_dealloc_ref %4 : $ArrayStorage of %4 : $ArrayStorage
%65 = address_to_pointer %11 : $*Kl to $Builtin.RawPointer
%66 = metatype $@thick Kl.Type
%67 = builtin "destroyArray"