blob: 097596898827a07a643edc65c523b0bb21dabbe2 [file] [log] [blame]
// RUN: %target-sil-opt -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 { }
}
class NontrivialDestructor {
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
set_deallocating %0 : $NontrivialDestructor
fix_lifetime %0 : $NontrivialDestructor
dealloc_ref %0 : $NontrivialDestructor
%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: 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 : $()
}