Files
swift-mirror/test/SILOptimizer/dead_array_elim_ossa.sil
Erik Eckstein 18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.

The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
2026-01-22 17:41:48 +01:00

216 lines
9.1 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-debuginfo -enable-sil-verify-all -deadobject-elim %s | %FileCheck %s
// Linux doesn't have the same symbol name for _ArrayBuffer, which is part of
// the ObjC runtime interop. Use `_ContiguousArrayBuffer instead.
// REQUIRES: objc_interop
import Swift
import Builtin
//////////
// Data //
//////////
class TrivialDestructor {
var int : Builtin.Int32
var ptr : Builtin.NativeObject
init()
deinit { }
}
class Klass {}
struct NonTrivialStruct {
var k : Klass
init()
}
// Remove a dead array.
// rdar://20980377 Add dead array elimination to DeadObjectElimination
// Swift._allocateUninitializedArray <A> (Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer)
sil [_semantics "array.uninitialized_intrinsic"] @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
sil [_semantics "array.uninitialized"] @adoptStorageSpecializedForInt : $@convention(method) (@guaranteed _ContiguousArrayStorage<Int>, Builtin.Word, @thin Array<Int>.Type) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
sil [_semantics "array.uninitialized"] @allocUninitialized : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
sil [_semantics "array.finalize_intrinsic"] @finalize : $@convention(thin) (@owned Array<Int>) -> @owned Array<Int>
// CHECK-LABEL: sil [ossa] @deadarrayWithAdoptStorage : $@convention(thin) () -> () {
// CHECK-NOT: apply
// CHECK: } // end sil function 'deadarrayWithAdoptStorage'
sil [ossa] @deadarrayWithAdoptStorage : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Word, 3
%6 = alloc_ref [tail_elems $Int * %0 : $Builtin.Word] $_ContiguousArrayStorage<Int>
%7 = metatype $@thin Array<Int>.Type
%8 = function_ref @adoptStorageSpecializedForInt : $@convention(method) (@guaranteed _ContiguousArrayStorage<Int>, Builtin.Word, @thin Array<Int>.Type) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
%9 = apply %8(%6, %0, %7) : $@convention(method) (@guaranteed _ContiguousArrayStorage<Int>, Builtin.Word, @thin Array<Int>.Type) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
(%10, %11) = destructure_tuple %9 : $(Array<Int>, UnsafeMutablePointer<Int>)
%f = function_ref @finalize : $@convention(thin) (@owned Array<Int>) -> @owned Array<Int>
%a = apply %f(%10) : $@convention(thin) (@owned Array<Int>) -> @owned Array<Int>
fix_lifetime %a : $Array<Int>
destroy_value %a : $Array<Int>
dealloc_ref %6 : $_ContiguousArrayStorage<Int>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @deadarray :
// CHECK-NOT: apply
// CHECK-NOT: store
// CHECK-LABEL: } // end sil function 'deadarray'
sil [ossa] @deadarray : $@convention(thin) (@owned TrivialDestructor) -> () {
bb0(%0 : @owned $TrivialDestructor):
%copy0 = copy_value %0 : $TrivialDestructor
%2 = integer_literal $Builtin.Word, 2
%3 = function_ref @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
%4 = apply %3<TrivialDestructor>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
(%5, %6) = destructure_tuple %4 : $(Array<TrivialDestructor>, Builtin.RawPointer)
%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*TrivialDestructor
store %0 to [init] %7 : $*TrivialDestructor
%9 = integer_literal $Builtin.Word, 1
%10 = index_addr %7 : $*TrivialDestructor, %9 : $Builtin.Word
store %copy0 to [init] %10 : $*TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil [ossa] @not_all_paths_release_the_dead_array :
// CHECK-NOT: apply
//} // end sil function 'not_all_paths_release_the_dead_array'
sil [ossa] @not_all_paths_release_the_dead_array : $@convention(thin) (@owned TrivialDestructor) -> () {
bb0(%0 : @owned $TrivialDestructor):
%2 = integer_literal $Builtin.Word, 2
// function_ref Swift._allocateUninitializedArray <A> (Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer)
%3 = function_ref @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
%4 = apply %3<TrivialDestructor>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
(%5, %6) = destructure_tuple %4 : $(Array<TrivialDestructor>, Builtin.RawPointer)
cond_br undef, bb1, bb2
bb1:
destroy_value [dead_end] %5
destroy_value [dead_end] %0
unreachable
bb2:
%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*TrivialDestructor
store %0 to [init] %7 : $*TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil [ossa] @tuple_extract_in_different_block :
// CHECK-NOT: apply
// CHECK-LABEL: } // end sil function 'tuple_extract_in_different_block'
sil [ossa] @tuple_extract_in_different_block : $@convention(thin) (@owned TrivialDestructor) -> () {
bb0(%0 : @owned $TrivialDestructor):
%2 = integer_literal $Builtin.Word, 2
// function_ref Swift._allocateUninitializedArray <A> (Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer)
%3 = function_ref @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
%4 = apply %3<TrivialDestructor>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
br bb1
bb1:
(%5, %6) = destructure_tuple %4 : $(Array<TrivialDestructor>, Builtin.RawPointer)
%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*TrivialDestructor
store %0 to [init] %7 : $*TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil [ossa] @release_dead_array_on_two_branches :
// CHECK-NOT: apply
// CHECK-LABEL: } // end sil function 'release_dead_array_on_two_branches'
sil [ossa] @release_dead_array_on_two_branches : $@convention(thin) (@owned TrivialDestructor, @owned TrivialDestructor) -> () {
bb0(%0 : @owned $TrivialDestructor, %1 : @owned $TrivialDestructor):
%2 = integer_literal $Builtin.Word, 2
// function_ref Swift._allocateUninitializedArray <A> (Builtin.Word) -> (Swift.Array<A>, Builtin.RawPointer)
%3 = function_ref @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
%4 = apply %3<TrivialDestructor>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
(%5, %6) = destructure_tuple %4 : $(Array<TrivialDestructor>, Builtin.RawPointer)
%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*TrivialDestructor
cond_br undef, bb1, bb2
bb1:
store %0 to [init] %7 : $*TrivialDestructor
destroy_value %1 : $TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
br bb3
bb2:
store %1 to [init] %7 : $*TrivialDestructor
destroy_value %0 : $TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
br bb3
bb3:
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil [ossa] @dead_array_in_single_cycle_loop
// CHECK-NOT: apply
// CHECK-LABEL: } // end sil function 'dead_array_in_single_cycle_loop'
sil [ossa] @dead_array_in_single_cycle_loop : $@convention(thin) (@guaranteed TrivialDestructor) -> () {
bb0(%0 : @guaranteed $TrivialDestructor):
br bb1
bb1:
%2 = integer_literal $Builtin.Word, 2
%3 = function_ref @allocArray : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
%4 = apply %3<TrivialDestructor>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> @owned (Array<τ_0_0>, Builtin.RawPointer)
(%5, %6) = destructure_tuple %4 : $(Array<TrivialDestructor>, Builtin.RawPointer)
%7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*TrivialDestructor
%copy0 = copy_value %0 : $TrivialDestructor
store %copy0 to [init] %7 : $*TrivialDestructor
destroy_value %5 : $Array<TrivialDestructor>
cond_br undef, bb3, bb2
bb3:
br bb1
bb2:
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil [ossa] @dead_debuginfo
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'dead_debuginfo'
sil [ossa] @dead_debuginfo : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] [var_decl] $NonTrivialStruct, var, name "x", type $NonTrivialStruct
%2 = integer_literal $Builtin.Int64, 0
%4 = struct $NonTrivialStruct (%0 : $Klass)
apply undef(%4) : $@convention(thin) (@guaranteed NonTrivialStruct) -> ()
store %4 to [init] %1 : $*NonTrivialStruct
destroy_addr %1 : $*NonTrivialStruct
dealloc_stack %1 : $*NonTrivialStruct
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: sil [ossa] @dead_array_with_borrow :
// CHECK-NOT: apply
// CHECK: %1 = tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'dead_array_with_borrow'
sil [ossa] @dead_array_with_borrow : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%2 = function_ref @allocUninitialized : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
%3 = apply %2(%0) : $@convention(thin) (Int) -> (@owned Array<Int>, UnsafeMutablePointer<Int>)
(%4, %5) = destructure_tuple %3
debug_value %4, let, name "a"
%7 = begin_borrow [lexical] %4
fix_lifetime %7
end_borrow %7
destroy_value %4
%11 = tuple ()
return %11
}