mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Type annotations for instruction operands are omitted, e.g. ``` %3 = struct $S(%1, %2) ``` Operand types are redundant anyway and were only used for sanity checking in the SIL parser. But: operand types _are_ printed if the definition of the operand value was not printed yet. This happens: * if the block with the definition appears after the block where the operand's instruction is located * if a block or instruction is printed in isolation, e.g. in a debugger The old behavior can be restored with `-Xllvm -sil-print-types`. This option is added to many existing test files which check for operand types in their check-lines.
517 lines
20 KiB
Plaintext
517 lines
20 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -allow-critical-edges=false | %FileCheck %s
|
|
|
|
// This is the ossa version of CopyForwarding tests
|
|
sil_stage raw
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
class AClass {}
|
|
|
|
struct NonTrivialStruct {
|
|
var cls : AClass
|
|
}
|
|
|
|
sil @get_nontrivialstruct : $@convention(thin) () -> @out NonTrivialStruct
|
|
sil @use_aclass : $@convention(thin) (@in_guaranteed AClass) -> ()
|
|
|
|
sil @f_in : $@convention(thin) <T> (@in T) -> ()
|
|
sil @f_in_guaranteed : $@convention(thin) <T> (@in_guaranteed T) -> ()
|
|
sil @f_out : $@convention(thin) <T> () -> @out T
|
|
sil @f_owned : $@convention(thin) <T> (@owned T) -> ()
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @forward_takeinit :
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'forward_takeinit'
|
|
sil hidden [ossa] @forward_takeinit : $@convention(thin) <T> (@in T) -> () {
|
|
bb0(%0 : $*T):
|
|
debug_value %0 : $*T, expr op_deref
|
|
%l1 = alloc_stack $T
|
|
copy_addr [take] %0 to [init] %l1 : $*T
|
|
%f1 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
%c1 = apply %f1<T>(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
dealloc_stack %l1 : $*T
|
|
%r1 = tuple ()
|
|
return %r1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @forward_takenoinit :
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'forward_takenoinit'
|
|
sil hidden [ossa] @forward_takenoinit : $@convention(thin) <T> (@in T) -> () {
|
|
bb0(%0 : $*T):
|
|
debug_value %0 : $*T, expr op_deref
|
|
%l1 = alloc_stack $T
|
|
%f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c1 = apply %f1<T>(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
copy_addr [take] %0 to %l1 : $*T
|
|
%f2 = function_ref @f_in : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
%c2 = apply %f2<T>(%l1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
dealloc_stack %l1 : $*T
|
|
%r1 = tuple ()
|
|
return %r1 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @backward_noinit :
|
|
// CHECK: copy_addr
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'backward_noinit'
|
|
sil hidden [ossa] @backward_noinit : $@convention(thin) <T> () -> @out T {
|
|
bb0(%0 : $*T):
|
|
%l1 = alloc_stack $T
|
|
%f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c1 = apply %f1<T>(%0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c2 = apply %f1<T>(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
copy_addr %l1 to %0 : $*T
|
|
destroy_addr %l1 : $*T
|
|
dealloc_stack %l1 : $*T
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @backward_takeinit :
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'backward_takeinit'
|
|
sil hidden [ossa] @backward_takeinit : $@convention(thin) <T> () -> @out T {
|
|
bb0(%0 : $*T):
|
|
%l1 = alloc_stack $T
|
|
%f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c1 = apply %f1<T>(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
debug_value %l1 : $*T, expr op_deref
|
|
copy_addr [take] %l1 to [init] %0 : $*T
|
|
debug_value %0 : $*T, expr op_deref
|
|
dealloc_stack %l1 : $*T
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @backward_takenoinit :
|
|
// CHECK: copy_addr
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'backward_takenoinit'
|
|
sil hidden [ossa] @backward_takenoinit : $@convention(thin) <T> () -> @out T {
|
|
bb0(%0 : $*T):
|
|
%l1 = alloc_stack $T
|
|
%f1 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c1 = apply %f1<T>(%0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
%c2 = apply %f1<T>(%l1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
copy_addr [take] %l1 to %0 : $*T
|
|
dealloc_stack %l1 : $*T
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @make_addronly :
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK-LABEL: } // end sil function 'make_addronly'
|
|
sil hidden [ossa] @make_addronly : $@convention(thin) <T> () -> @out T {
|
|
bb0(%0 : $*T):
|
|
%1 = alloc_stack $T, let, name "t" // users: %3, %4, %5
|
|
%2 = function_ref @f_out : $@convention(thin) <τ_0_0> () -> @out τ_0_0 // user: %3
|
|
%3 = apply %2<T>(%1) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
copy_addr [take] %1 to [init] %0 : $*T // id: %4
|
|
dealloc_stack %1 : $*T // id: %5
|
|
%6 = tuple () // user: %7
|
|
return %6 : $() // id: %7
|
|
}
|
|
|
|
sil [ossa] @_TFSq4someU__fMGSqQ__FQ_GSqQ__ : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin Optional<τ_0_0>.Type) -> @out Optional<τ_0_0>
|
|
|
|
sil [ossa] @_TFsoi2neU__FTGSqQ__Vs26_OptionalNilComparisonType_Sb : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @option_init :
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_stack
|
|
// CHECK: copy_addr
|
|
// CHECK: copy_addr
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_stack
|
|
// CHECK: copy_addr
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: alloc_stack
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: copy_addr
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: alloc_stack
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK-LABEL: } // end sil function 'option_init'
|
|
sil hidden [ossa] @option_init : $@convention(thin) <T> (@in AnyObject) -> () {
|
|
bb0(%0 : $*AnyObject):
|
|
%g0 = alloc_stack $IteratorOverOne<AnyObject> // 831
|
|
%s0 = struct_element_addr %g0 : $*IteratorOverOne<AnyObject>, #IteratorOverOne._elements
|
|
|
|
%l0 = alloc_stack $Optional<AnyObject>
|
|
// function_ref Swift.Optional.some <A>(Swift.Optional<A>.Type)(A) -> Swift.Optional<A>
|
|
%f0 = function_ref @_TFSq4someU__fMGSqQ__FQ_GSqQ__ : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin Optional<τ_0_0>.Type) -> @out Optional<τ_0_0>
|
|
%t0 = metatype $@thin Optional<AnyObject>.Type
|
|
%i0 = apply %f0<AnyObject>(%l0, %0, %t0) : $@convention(thin) <τ_0_0> (@in τ_0_0, @thin Optional<τ_0_0>.Type) -> @out Optional<τ_0_0>
|
|
|
|
%g1 = alloc_stack $IteratorOverOne<AnyObject> // 850
|
|
%s1 = struct_element_addr %g1 : $*IteratorOverOne<AnyObject>, #IteratorOverOne._elements
|
|
// We can't backward propagate this yet because we can't analyze struct_element_addr copy dest.
|
|
copy_addr [take] %l0 to [init] %s1 : $*Optional<AnyObject>
|
|
// We ignore this copy because its Def is used by struct_element_addr
|
|
copy_addr [take] %g1 to [init] %g0 : $*IteratorOverOne<AnyObject>
|
|
|
|
%l1 = alloc_stack $Optional<AnyObject> // 869
|
|
|
|
%l2 = alloc_stack $Optional<AnyObject> // 873
|
|
// We ignore this copy because its Def is used by struct_element_addr
|
|
copy_addr %s0 to [init] %l2 : $*Optional<AnyObject>
|
|
|
|
%l3 = alloc_stack $Optional<AnyObject> // 877
|
|
%o1 = enum $Optional<AnyObject>, #Optional.none!enumelt
|
|
store %o1 to [init] %l3 : $*Optional<AnyObject>
|
|
// We can't backward propagate this yet because we can't analyze struct_element_addr copy dest.
|
|
copy_addr [take] %l3 to %s0 : $*Optional<AnyObject>
|
|
dealloc_stack %l3 : $*Optional<AnyObject>
|
|
// We can't forward propagate this because l2 is deallocated, but we can backward propagate l1.
|
|
copy_addr [take] %l2 to [init] %l1 : $*Optional<AnyObject>
|
|
dealloc_stack %l2 : $*Optional<AnyObject>
|
|
%l4 = alloc_stack $Optional<AnyObject> // 889
|
|
%o2 = load [copy] %l1 : $*Optional<AnyObject>
|
|
store %o2 to [init] %l4 : $*Optional<AnyObject>
|
|
%s5 = struct $_OptionalNilComparisonType ()
|
|
|
|
%f1 = function_ref @_TFsoi2neU__FTGSqQ__Vs26_OptionalNilComparisonType_Sb : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool
|
|
%c5 = apply %f1<AnyObject>(%l4, %s5) : $@convention(thin) <τ_0_0> (@in Optional<τ_0_0>, _OptionalNilComparisonType) -> Bool
|
|
dealloc_stack %l4 : $*Optional<AnyObject>
|
|
destroy_addr %l1 : $*Optional<AnyObject>
|
|
dealloc_stack %l1 : $*Optional<AnyObject>
|
|
dealloc_stack %g1 : $*IteratorOverOne<AnyObject>
|
|
dealloc_stack %l0 : $*Optional<AnyObject>
|
|
destroy_addr %g0 : $*IteratorOverOne<AnyObject>
|
|
dealloc_stack %g0 : $*IteratorOverOne<AnyObject>
|
|
%p0 = tuple ()
|
|
return %p0 : $()
|
|
}
|
|
|
|
// Check that destroy is not hoisted above users of transitively related object
|
|
//
|
|
// CHECK-LABEL: sil hidden [ossa] @load_nontrivial :
|
|
// CHECK: load [copy] %0 : $*Optional<AClass>
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: unchecked_enum_data %{{.*}} : $Optional<AClass>
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'load_nontrivial'
|
|
sil hidden [ossa] @load_nontrivial : $@convention(thin) () -> () {
|
|
bb0:
|
|
%v0 = alloc_stack $Optional<AClass>
|
|
%v1 = alloc_stack $Optional<AClass>
|
|
|
|
%f0 = function_ref @f_out : $@convention(thin) <A> () -> @out A
|
|
%c0 = apply %f0<AClass?>(%v0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
|
|
copy_addr %v0 to [init] %v1 : $*Optional<AClass>
|
|
|
|
%f1 = function_ref @f_in : $@convention(thin) <A> (@in A) -> ()
|
|
%c1 = apply %f1<AClass?>(%v1) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
|
|
dealloc_stack %v1 : $*Optional<AClass>
|
|
%l1 = load [copy] %v0 : $*Optional<AClass>
|
|
%d1 = unchecked_enum_data %l1 : $Optional<AClass>, #Optional.some!enumelt
|
|
%f2 = function_ref @f_owned : $@convention(thin) <A> (@owned A) -> ()
|
|
%c2 = apply %f2<AClass>(%d1) : $@convention(thin) <τ_0_0> (@owned τ_0_0) -> ()
|
|
destroy_addr %v0 : $*Optional<AClass>
|
|
%34 = tuple ()
|
|
dealloc_stack %v0 : $*Optional<AClass>
|
|
return %34 : $()
|
|
}
|
|
|
|
sil @use: $@convention(thin) <T> (@inout T) -> ()
|
|
|
|
// We currently don't handle reasoning about multiple copy_addr instructions at
|
|
// once. With the current logic we must not optimize this case (we would have to
|
|
// prove that we can replace both copy_addr to be able to optimize).
|
|
|
|
// CHECK-LABEL: sil [ossa] @not_dominated_uses :
|
|
// CHECK: alloc_stack
|
|
// CHECK: cond_br
|
|
// CHECK: bb1
|
|
// CHECK: copy_addr
|
|
// CHECK: apply
|
|
// CHECK: br bb3
|
|
// CHECK: bb2
|
|
// CHECK: copy_addr
|
|
// CHECK: apply
|
|
// CHECK: br bb3
|
|
// CHECK: bb3
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK-LABEL: } // end sil function 'not_dominated_uses'
|
|
sil [ossa] @not_dominated_uses: $@convention(thin) <T> (@in Optional<T>, @in Optional<T>, Bool) -> () {
|
|
bb0(%0 : $*Optional<T>, %1 : $*Optional<T>, %3 : $Bool):
|
|
%4 = alloc_stack $Optional<T>
|
|
%5 = struct_extract %3 : $Bool, #Bool._value
|
|
%f = function_ref @use : $@convention(thin) <T2> (@inout T2) -> ()
|
|
cond_br %5, bb1, bb2
|
|
|
|
bb1:
|
|
copy_addr [take] %0 to [init] %4 : $*Optional<T>
|
|
%r1 = apply %f<Optional<T>>(%4) : $@convention(thin) <T2> (@inout T2) -> ()
|
|
destroy_addr %1 : $*Optional<T>
|
|
br bb3
|
|
|
|
bb2:
|
|
copy_addr [take] %1 to [init] %4 : $*Optional<T>
|
|
%r2 = apply %f<Optional<T>>(%4) : $@convention(thin) <T2> (@inout T2) -> ()
|
|
destroy_addr %0 : $*Optional<T>
|
|
br bb3
|
|
|
|
bb3:
|
|
%r3 = apply %f<Optional<T>>(%4) : $@convention(thin) <T2> (@inout T2) -> ()
|
|
destroy_addr %4 : $*Optional<T>
|
|
dealloc_stack %4 : $*Optional<T>
|
|
%13 = tuple()
|
|
return %13 : $()
|
|
}
|
|
|
|
public struct S<T> {
|
|
@_hasStorage var f: T { get set }
|
|
@_hasStorage var g: T { get set }
|
|
init(f: T, g: T)
|
|
}
|
|
|
|
// Test a dead copy that initializes a stack local.
|
|
// CHECK-LABEL: sil [ossa] @deadtemp :
|
|
// CHECK: %[[G:.*]] = struct_element_addr %0 : $*S<T>, #S.g
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: debug_value %[[G:.*]] : $*T, expr op_deref
|
|
// CHECK: %[[F:.*]] = struct_element_addr %0 : $*S<T>, #S.f
|
|
// CHECK: copy_addr %[[G]] to %[[F]] : $*T
|
|
// CHECK-LABEL: } // end sil function 'deadtemp'
|
|
sil [ossa] @deadtemp : $@convention(thin) <T> (@inout S<T>) -> () {
|
|
bb0(%0 : $*S<T>):
|
|
%1 = struct_element_addr %0 : $*S<T>, #S.g
|
|
%2 = alloc_stack $T
|
|
copy_addr %1 to [init] %2 : $*T
|
|
debug_value %2 : $*T, expr op_deref
|
|
%4 = struct_element_addr %0 : $*S<T>, #S.f
|
|
copy_addr [take] %2 to %4 : $*T
|
|
dealloc_stack %2 : $*T
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
// Test assigning into a stack local.
|
|
// CHECK-LABEL: sil [ossa] @deadtemp_assign :
|
|
// CHECK: %[[G:.*]] = struct_element_addr %0 : $*S<T>, #S.g
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: %[[F:.*]] = struct_element_addr %0 : $*S<T>, #S.f
|
|
// CHECK: copy_addr %[[G]] to %[[F]] : $*T
|
|
// CHECK-LABEL: } // end sil function 'deadtemp_assign'
|
|
sil [ossa] @deadtemp_assign : $@convention(thin) <T> (@inout S<T>) -> () {
|
|
bb0(%0 : $*S<T>):
|
|
%1 = struct_element_addr %0 : $*S<T>, #S.g
|
|
%2 = alloc_stack $T
|
|
copy_addr %1 to [init] %2 : $*T
|
|
%4 = struct_element_addr %0 : $*S<T>, #S.f
|
|
copy_addr [take] %2 to %4 : $*T
|
|
dealloc_stack %2 : $*T
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
// Test a dead copy that initializes a stack local,
|
|
// taking the source, and initializing the destination.
|
|
// CHECK-LABEL: sil [ossa] @deadtemp_take_init :
|
|
// CHECK-NOT: copy_addr
|
|
// CHECK: copy_addr [take] %1 to [init] %0 : $*T
|
|
// CHECK-LABEL: } // end sil function 'deadtemp_take_init'
|
|
sil [ossa] @deadtemp_take_init : $@convention(thin) <T> (@in T) -> @out T {
|
|
bb0(%0 : $*T, %1 : $*T):
|
|
%2 = alloc_stack $T
|
|
copy_addr [take] %1 to [init] %2 : $*T
|
|
copy_addr [take] %2 to [init] %0 : $*T
|
|
dealloc_stack %2 : $*T
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
struct ObjWrapper {
|
|
var obj: AnyObject
|
|
}
|
|
|
|
// Test that backward copy propagation does not interfere with the previous
|
|
// value of the copy's destination. The `load` is a use of the `alloc` value,
|
|
// but not a direct use. Since it occurs between the initialization of `temp`
|
|
// and the copy from temp into `alloc`, the copy into `alloc` cannot be backward
|
|
// propagated.
|
|
// <rdar://35646292> Swift CI: resilience bot seg faults in stdlib/RangeReplaceable.swift.gyb.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testLoadDestroy : $@convention(thin) (@in_guaranteed ObjWrapper, @in_guaranteed ObjWrapper) -> () {
|
|
// CHECK: bb0(%0 : $*ObjWrapper, %1 : $*ObjWrapper):
|
|
// CHECK: [[ALLOC:%.*]] = alloc_stack $ObjWrapper, var, name "o"
|
|
// CHECK: copy_addr %0 to [init] [[ALLOC]] : $*ObjWrapper
|
|
// CHECK: [[ELT_ADDR:%.*]] = struct_element_addr [[ALLOC]] : $*ObjWrapper, #ObjWrapper.obj
|
|
// CHECK: [[TEMP:%.*]] = alloc_stack $ObjWrapper
|
|
// CHECK: copy_addr %1 to [init] [[TEMP]] : $*ObjWrapper
|
|
// CHECK: [[LD:%.*]] = load [copy] [[ELT_ADDR]] : $*AnyObject
|
|
// CHECK: destroy_value [[LD]] : $AnyObject
|
|
// CHECK: destroy_addr [[ALLOC]] : $*ObjWrapper
|
|
// CHECK: destroy_addr [[TEMP]] : $*ObjWrapper
|
|
// CHECK: dealloc_stack [[TEMP]] : $*ObjWrapper
|
|
// CHECK: dealloc_stack [[ALLOC]] : $*ObjWrapper
|
|
// CHECK: %{{.*}} = tuple ()
|
|
// CHECK: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'testLoadDestroy'
|
|
sil [ossa] @testLoadDestroy : $@convention(thin) (@in_guaranteed ObjWrapper, @in_guaranteed ObjWrapper) -> () {
|
|
bb(%0 : $*ObjWrapper, %1 : $*ObjWrapper):
|
|
// Fully initialize a new stack var to arg0.
|
|
%alloc = alloc_stack $ObjWrapper, var, name "o"
|
|
copy_addr %0 to [init] %alloc : $*ObjWrapper
|
|
%objadr = struct_element_addr %alloc : $*ObjWrapper, #ObjWrapper.obj
|
|
|
|
// Fully initialize a temporary to arg1.
|
|
// Rewriting this to %alloc would alias with the subsequent load.
|
|
%temp = alloc_stack $ObjWrapper
|
|
copy_addr %1 to [init] %temp : $*ObjWrapper
|
|
|
|
// Load and release an reference from arg0 inside the stack var.
|
|
%obj = load [copy] %objadr : $*AnyObject
|
|
destroy_value %obj : $AnyObject
|
|
destroy_addr %alloc : $*ObjWrapper
|
|
// Move `temp` copy of arg1 into the stack var.
|
|
copy_addr [take] %temp to [init] %alloc : $*ObjWrapper
|
|
|
|
destroy_addr %alloc : $*ObjWrapper
|
|
dealloc_stack %temp : $*ObjWrapper
|
|
dealloc_stack %alloc : $*ObjWrapper
|
|
%74 = tuple ()
|
|
return %74 : $()
|
|
}
|
|
|
|
// Helper for multipleUse
|
|
sil [ossa] @multipleArg : $@convention(thin) <T> (@in_guaranteed T, @in_guaranteed T) -> () {
|
|
bb0(%0 : $*T, %1 : $*T):
|
|
%r1 = tuple ()
|
|
return %r1 : $()
|
|
}
|
|
|
|
// Test a corner case of forward copy propagation in which simple substitution
|
|
// does not work (the source is reinitialized) and need to propagate to an
|
|
// instruction with multiple uses of the source.
|
|
// CHECK-LABEL: sil hidden [ossa] @multipleUse : $@convention(thin) <T> (@in T, @in T) -> () {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*T):
|
|
// CHECK: [[A:%.*]] = alloc_stack $T
|
|
// CHECK: copy_addr [take] %0 to [init] [[A]] : $*T
|
|
// CHECK: [[F:%.*]] = function_ref @multipleArg : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> ()
|
|
// CHECK: [[C:%.*]] = apply [[F]]<T>([[A]], [[A]]) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> ()
|
|
// CHECK: destroy_addr [[A]] : $*T
|
|
// CHECK: dealloc_stack [[A]] : $*T
|
|
// CHECK: copy_addr [take] %1 to [init] %0 : $*T
|
|
// CHECK: %{{.*}} = tuple ()
|
|
// CHECK: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'multipleUse'
|
|
sil hidden [ossa] @multipleUse : $@convention(thin) <T> (@in T, @in T) -> () {
|
|
bb0(%0 : $*T, %1 : $*T):
|
|
%l1 = alloc_stack $T
|
|
copy_addr [take] %0 to [init] %l1 : $*T
|
|
%f1 = function_ref @multipleArg : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> ()
|
|
%c1 = apply %f1<T>(%l1, %l1) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> ()
|
|
destroy_addr %l1 : $*T
|
|
dealloc_stack %l1 : $*T
|
|
// Reinitialize copy's source to avoid a fast isSourceDeadAtCopy check.
|
|
copy_addr [take] %1 to [init] %0 : $*T
|
|
destroy_addr %0 : $*T
|
|
%r1 = tuple ()
|
|
return %r1 : $()
|
|
}
|
|
|
|
// rdar://problem/43888666
|
|
// https://github.com/apple/swift/issues/51046
|
|
// Memory leak after switch in release configuration
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testGlobalHoistToStoredValue : $@convention(thin) (@owned AClass, @inout AClass) -> () {
|
|
// CHECK: bb0(%0 : @owned $AClass, %1 : $*AClass):
|
|
// CHECK-NEXT: [[LOCAL:%.*]] = alloc_stack $AClass
|
|
// CHECK-NEXT: [[COPY:%.*]] = copy_value %0
|
|
// CHECK-NEXT: store %0 to [init] [[LOCAL]] : $*AClass
|
|
// CHECK-NEXT: copy_addr [[LOCAL]] to %1 : $*AClass
|
|
// CHECK-NEXT: store [[COPY]] to [assign] %1 : $*AClass
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: destroy_addr [[LOCAL]]
|
|
// CHECK-NEXT: dealloc_stack [[LOCAL]] : $*AClass
|
|
// CHECK-NEXT: tuple ()
|
|
// CHECK-NEXT: return
|
|
// CHECK-LABEL: } // end sil function 'testGlobalHoistToStoredValue'
|
|
sil [ossa] @testGlobalHoistToStoredValue : $@convention(thin) (@owned AClass, @inout AClass) -> () {
|
|
bb0(%obj : @owned $AClass, %ptr : $*AClass ):
|
|
%local = alloc_stack $AClass
|
|
%copy = copy_value %obj : $AClass
|
|
store %obj to [init] %local : $*AClass
|
|
copy_addr %local to %ptr : $*AClass
|
|
store %copy to [assign] %ptr : $*AClass
|
|
br bb1
|
|
bb1:
|
|
destroy_addr %local : $*AClass
|
|
dealloc_stack %local : $*AClass
|
|
%v = tuple ()
|
|
return %v : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_dynamic_lifetime :
|
|
// CHECK: [[STK:%.*]] = alloc_stack [dynamic_lifetime] $NonTrivialStruct
|
|
// CHECK: copy_addr [take] {{.*}} to [init] [[STK]] : $*NonTrivialStruct
|
|
// CHECK-LABEL: } // end sil function 'test_dynamic_lifetime'
|
|
sil [ossa] @test_dynamic_lifetime : $@convention(thin) () -> @out NonTrivialStruct {
|
|
bb0(%0 : $*NonTrivialStruct):
|
|
%1 = alloc_stack $Builtin.Int1
|
|
%2 = alloc_stack [dynamic_lifetime] $NonTrivialStruct
|
|
%3 = integer_literal $Builtin.Int1, 0
|
|
store %3 to [trivial] %1 : $*Builtin.Int1
|
|
br bb1
|
|
|
|
bb1:
|
|
%6 = load [trivial] %1 : $*Builtin.Int1
|
|
cond_br %6, bb2, bb3
|
|
|
|
bb2:
|
|
destroy_addr %2 : $*NonTrivialStruct
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
%11 = integer_literal $Builtin.Int1, -1
|
|
store %11 to [trivial] %1 : $*Builtin.Int1
|
|
%13 = alloc_stack $NonTrivialStruct
|
|
%14 = function_ref @get_nontrivialstruct : $@convention(thin) () -> @out NonTrivialStruct
|
|
%15 = apply %14(%13) : $@convention(thin) () -> @out NonTrivialStruct
|
|
copy_addr [take] %13 to [init] %2 : $*NonTrivialStruct
|
|
dealloc_stack %13 : $*NonTrivialStruct
|
|
%18 = struct_element_addr %2 : $*NonTrivialStruct, #NonTrivialStruct.cls
|
|
%19 = function_ref @use_aclass : $@convention(thin) (@in_guaranteed AClass) -> ()
|
|
apply %19(%18) : $@convention(thin) (@in_guaranteed AClass) -> ()
|
|
cond_br undef, bb5, bb9
|
|
|
|
bb5:
|
|
br bb10
|
|
|
|
bb9:
|
|
br bb10
|
|
|
|
bb10:
|
|
cond_br undef, bb11, bb12
|
|
|
|
bb11:
|
|
br bb1
|
|
|
|
bb12:
|
|
copy_addr [take] %2 to [init] %0 : $*NonTrivialStruct
|
|
dealloc_stack %2 : $*NonTrivialStruct
|
|
%33 = tuple ()
|
|
dealloc_stack %1 : $*Builtin.Int1
|
|
return %33 : $()
|
|
}
|