mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Handle calling conventions and cleanups in all the places (hopefully). - when ExistentialSpecializer copies the specialized concrete arg into the original existential value - when ExistentialSpecializer generates a think - when SILCombine substitutes concrete values in place of the opened existential. One particularly nasty problem is the existential boxes need to be destroyed. It is not ok to simply destroy their value. The "leaks" tool does not catch this problem. Ownership SIL will make this all much more robust. Fixes <rdar://problem/50595630> Multiple leaks detected - Swift Perf
465 lines
15 KiB
Swift
465 lines
15 KiB
Swift
// RUN: %target-swift-frontend -O -Xllvm -enable-existential-specializer -Xllvm -sil-disable-pass=GenericSpecializer -Xllvm -sil-disable-pass=FunctionSignatureOpts -Xllvm -sil-disable-pass=SILCombine -emit-sil -sil-verify-all %s | %FileCheck %s
|
|
|
|
internal protocol SomeProtocol : class {
|
|
func foo() -> Int
|
|
}
|
|
internal class SomeClass: SomeProtocol {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
|
|
@inline(never) internal func wrap_foo_cp(a:SomeProtocol ) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform2cpyyF : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: init_existential_ref
|
|
// CHECK: debug_value
|
|
// CHECK: function_ref @$s21existential_transform11wrap_foo_cp1aSiAA12SomeProtocol_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeProtocol> (@guaranteed τ_0_0) -> Int
|
|
// CHECK: open_existential_ref
|
|
// CHECK: apply
|
|
// CHECK: set_deallocating
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: dealloc_ref
|
|
// CHECK: dealloc_ref
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform2cpyyF'
|
|
@inline(never) func cp() {
|
|
let magic1:SomeProtocol = SomeClass()
|
|
let _ = wrap_foo_cp(a:magic1)
|
|
}
|
|
|
|
/// Non Class Protocol
|
|
internal protocol SomeNoClassProtocol {
|
|
func foo() -> Int
|
|
}
|
|
internal class SomeNoClass: SomeNoClassProtocol {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
|
|
@inline(never) internal func wrap_foo_ncp(a:SomeNoClassProtocol) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_ref $SomeNoClass
|
|
// CHECK: debug_value
|
|
// CHECK: init_existential_addr
|
|
// CHECK: store
|
|
// CHECK: function_ref @$s21existential_transform12wrap_foo_ncp1aSiAA19SomeNoClassProtocol_p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeNoClassProtocol> (@in_guaranteed τ_0_0) -> Int
|
|
// CHECK: open_existential_addr
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK: dealloc_stack
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform3ncpyyF'
|
|
@inline(never) func ncp() {
|
|
let magic2:SomeNoClassProtocol = SomeNoClass()
|
|
let _ = wrap_foo_ncp(a:magic2)
|
|
}
|
|
|
|
|
|
/// Class Protocol Composition
|
|
internal protocol SomeClassProtocolComp : class {
|
|
func foo() -> Int
|
|
}
|
|
internal protocol SomeOtherClassProtocolComp : class {
|
|
func bar() -> Int
|
|
}
|
|
internal class SomeClassComp: SomeClassProtocolComp, SomeOtherClassProtocolComp {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
@inline(never) func bar() -> Int {
|
|
return 20
|
|
}
|
|
}
|
|
@inline(never) internal func wrap_foo_bar_cpc(a:SomeClassProtocolComp & SomeOtherClassProtocolComp) -> Int{
|
|
return a.foo() + a.bar()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3cpcyyF : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: init_existential_ref
|
|
// CHECK: debug_value
|
|
// CHECK: function_ref @$s21existential_transform16wrap_foo_bar_cpc1aSiAA21SomeClassProtocolComp_AA0g5OtherhiJ0p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeClassProtocolComp, τ_0_0 : SomeOtherClassProtocolComp> (@guaranteed τ_0_0) -> Int
|
|
// CHECK: open_existential_ref
|
|
// CHECK: apply
|
|
// CHECK: set_deallocating
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: dealloc_ref
|
|
// CHECK: dealloc_ref
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform3cpcyyF'
|
|
@inline(never) func cpc() {
|
|
let magic3:SomeClassProtocolComp & SomeOtherClassProtocolComp = SomeClassComp()
|
|
let _ = wrap_foo_bar_cpc(a:magic3)
|
|
}
|
|
|
|
/// Non Class Protocol Comp
|
|
internal protocol SomeNoClassProtocolComp {
|
|
func foo() -> Int
|
|
}
|
|
internal protocol SomeOtherNoClassProtocolComp {
|
|
func bar() -> Int
|
|
}
|
|
internal class SomeNoClassComp: SomeNoClassProtocolComp, SomeOtherNoClassProtocolComp {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
@inline(never) func bar() -> Int {
|
|
return 20
|
|
}
|
|
}
|
|
@inline(never) internal func wrap_no_foo_bar_comp_ncpc(a:SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp) -> Int{
|
|
return a.foo() + a.bar()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: init_existential_addr
|
|
// CHECK: store
|
|
// CHECK: function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1aSiAA23SomeNoClassProtocolComp_AA0i5OtherjklM0p_tFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : SomeNoClassProtocolComp, τ_0_0 : SomeOtherNoClassProtocolComp> (@in_guaranteed τ_0_0) -> Int
|
|
// CHECK: open_existential_addr
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK: dealloc_stack
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform4ncpcyyF'
|
|
@inline(never) func ncpc() {
|
|
let magic4:SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp = SomeNoClassComp()
|
|
let _ = wrap_no_foo_bar_comp_ncpc(a:magic4)
|
|
}
|
|
|
|
internal protocol P : class {
|
|
func foo() -> Int
|
|
}
|
|
internal class K : P {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform18do_not_optimize_cp1aSiAA1P_p_tF : $@convention(thin) (@guaranteed P) -> Int {
|
|
// CHECK: bb0(%0 : $P):
|
|
// CHECK: debug_value
|
|
// CHECK: [[O1:%.*]] = open_existential_ref
|
|
// CHECK: witness_method $@opened("{{.*}}") P, #P.foo!1 : <Self where Self : P> (Self) -> () -> Int, [[O1]] : $@opened("{{.*}}") P : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> Int
|
|
// CHECK: apply
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform18do_not_optimize_cp1aSiAA1P_p_tF'
|
|
@inline(never) internal func do_not_optimize_cp(a:P ) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
internal protocol PP : class {
|
|
func foo() -> Int
|
|
}
|
|
internal class KK : PP {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int {
|
|
// CHECK: bb0(%0 : $*PP):
|
|
// CHECK: debug_value_addr
|
|
// CHECK: load
|
|
// CHECK: [[O1:%.*]] = open_existential_ref
|
|
// CHECK: witness_method $@opened("{{.*}}") PP, #PP.foo!1 : <Self where Self : PP> (Self) -> () -> Int, %3 : $@opened("{{.*}}PP : $@convention(witness_method: PP) <τ_0_0 where τ_0_0 : PP> (@guaranteed τ_0_0) -> Int
|
|
// CHECK: apply
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF'
|
|
@inline(never) internal func wrap_inout_cp(a: inout PP ) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform24do_not_optimize_inout_cpyyF : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: alloc_stack
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: init_existential_ref
|
|
// CHECK: store
|
|
// CHECK: function_ref @$s21existential_transform13wrap_inout_cp1aSiAA2PP_pz_tF : $@convention(thin) (@inout PP) -> Int
|
|
// CHECK: apply
|
|
// CHECK: set_deallocating
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: dealloc_ref
|
|
// CHECK: dealloc_ref
|
|
// CHECK: dealloc_stack
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform24do_not_optimize_inout_cpyyF'
|
|
@inline(never) func do_not_optimize_inout_cp() {
|
|
var magic5:PP = KK()
|
|
let _ = wrap_inout_cp(a: &magic5)
|
|
}
|
|
|
|
internal protocol PPP {
|
|
func foo() -> Int
|
|
}
|
|
internal class KKKClass : PPP {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
|
|
@inline(never) internal func wrap_inout_ncp(a: inout PPP ) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
// Cannot specialize an @inout argument.
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform9inout_ncpyyF : $@convention(thin) () -> () {
|
|
// CHECK: function_ref @$s21existential_transform14wrap_inout_ncp1aSiAA3PPP_pz_tF : $@convention(thin) (@inout PPP) -> Int
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform9inout_ncpyyF'
|
|
@inline(never) func inout_ncp() {
|
|
var magic6:PPP = KKKClass()
|
|
let _ = wrap_inout_ncp(a: &magic6)
|
|
}
|
|
|
|
internal protocol PPPP {
|
|
func foo() -> Int
|
|
}
|
|
internal struct SSSS : PPPP {
|
|
init() {
|
|
}
|
|
@inline(never) func foo() -> Int {
|
|
return 10
|
|
}
|
|
}
|
|
@inline(never) internal func wrap_struct_inout_ncp(a: inout PPPP ) -> Int{
|
|
return a.foo()
|
|
}
|
|
|
|
// Cannot specialize an @inout argument.
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform16struct_inout_ncpyyF : $@convention(thin) () -> () {
|
|
// CHECK: function_ref @$s21existential_transform21wrap_struct_inout_ncp1aSiAA4PPPP_pz_tF : $@convention(thin) (@inout PPPP) -> Int
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform16struct_inout_ncpyyF'
|
|
@inline(never) func struct_inout_ncp() {
|
|
var magic7:PPPP = SSSS()
|
|
let _ = wrap_struct_inout_ncp(a: &magic7)
|
|
}
|
|
|
|
protocol GP {
|
|
func foo() -> Int
|
|
}
|
|
|
|
class GC: GP {
|
|
@inline(never) func foo() ->Int {
|
|
return 10
|
|
}
|
|
}
|
|
func wrap_gcp<T:GP>(_ a:T,_ b:GP) -> Int {
|
|
return a.foo() + b.foo()
|
|
}
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3gcpySixAA2GPRzlF : $@convention(thin) <T where T : GP> (@in_guaranteed T) -> Int {
|
|
// CHECK: bb0(%0 : $*T):
|
|
// CHECK: debug_value_addr
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: alloc_stack
|
|
// CHECK: init_existential_addr
|
|
// CHECK: store
|
|
// CHECK: function_ref @$s21existential_transform8wrap_gcpySix_AA2GP_ptAaCRzlFTf4ne_n : $@convention(thin) <τ_0_0 where τ_0_0 : GP><τ_1_0 where τ_1_0 : GP> (@in_guaranteed τ_0_0, @in_guaranteed τ_1_0) -> Int
|
|
// CHECK: open_existential_addr
|
|
// CHECK: strong_retain
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK: dealloc_stack
|
|
// CHECK: strong_release
|
|
// CHECK: return
|
|
// CHECK: } // end sil function '$s21existential_transform3gcpySixAA2GPRzlF'
|
|
@inline(never) func gcp<T:GP>(_ a:T) -> Int {
|
|
let k:GC = GC()
|
|
return wrap_gcp(a, k)
|
|
}
|
|
|
|
func wrap_gcp_arch<T:GP>(_ a:T,_ b:GP, _ c:inout Array<T>) -> Int {
|
|
return a.foo() + b.foo() + c[0].foo()
|
|
}
|
|
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform8gcp_archySix_SayxGztAA2GPRzlF : $@convention(thin) <T where T : GP> (@in_guaranteed T, @inout Array<T>) -> Int {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*Array<T>):
|
|
// CHECK: debug_value_addr
|
|
// CHECK: debug_value_addr
|
|
// CHECK: alloc_ref
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: alloc_stack
|
|
// CHECK: init_existential_addr
|
|
// CHECK: store
|
|
// CHECK: function_ref @$s21existential_transform13wrap_gcp_archySix_AA2GP_pSayxGztAaCRzlFTf4nen_n : $@convention(thin) <τ_0_0 where τ_0_0 : GP><τ_1_0 where τ_1_0 : GP> (@in_guaranteed τ_0_0, @in_guaranteed τ_1_0, @inout Array<τ_0_0>) -> Int
|
|
// CHECK: open_existential_addr
|
|
// CHECK: strong_retain
|
|
// CHECK: apply
|
|
// CHECK: destroy_addr
|
|
// CHECK: dealloc_stack
|
|
// CHECK: strong_release
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform8gcp_archySix_SayxGztAA2GPRzlF'
|
|
@inline(never) func gcp_arch<T:GP>(_ a:T, _ b:inout Array<T>) -> Int {
|
|
let k:GC = GC()
|
|
return wrap_gcp_arch(a, k, &b)
|
|
}
|
|
|
|
protocol Foo {
|
|
var myName: String { get }
|
|
}
|
|
|
|
struct MyURL {
|
|
}
|
|
|
|
extension MyURL : Foo {
|
|
var myName : String { return "MyURL" }
|
|
}
|
|
|
|
struct MyStruct : Foo {
|
|
var myName : String { return "MyStruct" }
|
|
}
|
|
|
|
// CHECK-LABEL: sil shared [noinline] @$s21existential_transform7getNameySSAA3Foo_pFTf4e_n : $@convention(thin) <τ_0_0 where τ_0_0 : Foo> (@in_guaranteed τ_0_0) -> @owned String {
|
|
// CHECK: bb0(%0 : $*τ_0_0):
|
|
// CHECK: alloc_stack
|
|
// CHECK: init_existential_addr
|
|
// CHECK: copy_addr
|
|
// CHECK: debug_value_addr
|
|
// CHECK: open_existential_addr
|
|
// CHECK: witness_method
|
|
// CHECK: apply
|
|
// CHECK: dealloc_stack
|
|
// CHECK: return
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform7getNameySSAA3Foo_pFTf4e_n'
|
|
@inline(never) func getName(_ f: Foo) -> String {
|
|
return f.myName
|
|
}
|
|
|
|
@inline(never) func getName_wrapper() -> Int32{
|
|
let u = MyURL()
|
|
return getName(u) == "MyStruct" ? 0 : 1
|
|
}
|
|
|
|
protocol RP {
|
|
var val:Int32 {get set}
|
|
}
|
|
class RC: RP {
|
|
var val:Int32
|
|
init(val:Int32) { self.val = val }
|
|
}
|
|
|
|
// Note: The checks below must ensure that the function signature "@inline(never) func find(base:Int32, mult:Int32, Obj1: RP) -> Bool" has been turned into a protocol-constrained generic function via existential specialization, i.e., "function_ref @$s21existential_transform4find4base4mult4Obj1Sbs5Int32V_AgA2RP_ptFTf4nne_n : $@convention(thin) <τ_0_0 where τ_0_0 : RP> (Int32, Int32, @in_guaranteed τ_0_0) -> Bool". Same is true for the recursive function call for "return find (base: base, mult: mult+1, Obj1: Obj1)". Please refer to existential_specializer_soletype.sil test for SIL level testing. This test makes sure that nothing else breaks when we run end-to-end.
|
|
// CHECK-LABEL: sil shared [noinline] @$s21existential_transform4find4base4mult4Obj1Sbs5Int32V_AgA2RP_ptFTf4nne_n : $@convention(thin) <τ_0_0 where τ_0_0 : RP> (Int32, Int32, @in_guaranteed τ_0_0) -> Bool {
|
|
// CHECK: bb0
|
|
// CHECK: alloc_stack $RP
|
|
// CHECK: init_existential_addr
|
|
// CHECK: copy_addr
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value
|
|
// CHECK: debug_value_addr
|
|
// CHECK: struct_extract
|
|
// CHECK: struct_extract
|
|
// CHECK: integer_literal
|
|
// CHECK: builtin
|
|
// CHECK: tuple_extract
|
|
// CHECK: tuple_extract
|
|
// CHECK: cond_fail
|
|
// CHECK: open_existential_addr
|
|
// CHECK: witness_method
|
|
// CHECK: apply
|
|
// CHECK: struct_extract
|
|
// CHECK: builtin
|
|
// CHECK: cond_br
|
|
// CHECK: bb1:
|
|
// CHECK: integer_literal
|
|
// CHECK: struct
|
|
// CHECK: br
|
|
// CHECK: bb2:
|
|
// CHECK: open_existential_addr
|
|
// CHECK: witness_method
|
|
// CHECK: apply
|
|
// CHECK: struct_extract
|
|
// CHECK: builtin
|
|
// CHECK: cond_br
|
|
// CHECK: bb3
|
|
// CHECK: dealloc_stack
|
|
// CHECK: return
|
|
// CHECK: bb4:
|
|
// CHECK: struct
|
|
// CHECK: br
|
|
// CHECK: bb5:
|
|
// CHECK: integer_literal
|
|
// CHECK: builtin
|
|
// CHECK: tuple_extract
|
|
// CHECK: tuple_extract
|
|
// CHECK: cond_fail
|
|
// CHECK: struct
|
|
// CHECK: function_ref @$s21existential_transform4find4base4mult4Obj1Sbs5Int32V_AgA2RP_ptFTf4nne_n : $@convention(thin) <τ_0_0 where τ_0_0 : RP> (Int32, Int32, @in_guaranteed τ_0_0) -> Bool
|
|
// CHECK: open_existential_addr
|
|
// CHECK: apply
|
|
// CHECK: br
|
|
// CHECK-LABEL: } // end sil function '$s21existential_transform4find4base4mult4Obj1Sbs5Int32V_AgA2RP_ptFTf4nne_n'
|
|
@inline(never) func find(base:Int32, mult:Int32, Obj1: RP) -> Bool {
|
|
if base * mult > Obj1.val {
|
|
return false
|
|
} else if base * mult == Obj1.val {
|
|
return true
|
|
} else {
|
|
return find (base: base, mult: mult+1, Obj1: Obj1)
|
|
}
|
|
}
|
|
@inline(never) func find_wrapper() -> Bool {
|
|
let ab = RC(val: 100)
|
|
return find(base: 3, mult: 1, Obj1: ab)
|
|
}
|
|
@_optimize(none) public func foo() -> Int {
|
|
cp()
|
|
ncp()
|
|
cpc()
|
|
ncpc()
|
|
let p:P = K()
|
|
let x:Int = do_not_optimize_cp(a:p)
|
|
do_not_optimize_inout_cp()
|
|
inout_ncp()
|
|
struct_inout_ncp()
|
|
let y:Int = gcp(GC())
|
|
var a:Array<GC> = [GC()]
|
|
let z:Int = gcp_arch(GC(), &a)
|
|
let zz:Int32 = getName_wrapper()
|
|
let _ = find_wrapper()
|
|
return x + y + z + Int(zz)
|
|
}
|