| // RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -deadobject-elim %s | %FileCheck %s |
| |
| import Swift |
| import Builtin |
| |
| ////////// |
| // Data // |
| ////////// |
| |
| class TrivialDestructor { |
| var int : Builtin.Int32 |
| var ptr : Builtin.NativeObject |
| init() |
| deinit { } |
| } |
| |
| sil @_T04main17TrivialDestructorCfD : $@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 : $() |
| } |
| |
| // 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: integer_literal |
| // 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: alloc_stack |
| // CHECK: return |
| 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 : $() |
| } |