blob: 5eb5de5e0c08df3ea8183306c76bdba36f05d36c [file] [log] [blame]
// RUN: %target-sil-opt -wmo -enable-sil-verify-all %s -enable-sil-existential-specializer -existential-specializer -inline -sil-combine -generic-specializer -devirtualizer
// This file tests existential specializer transformation followed by concrete type propagation and generic specialization leading to a devirtualization of a witness method call.
sil_stage canonical
import Builtin
import Swift
import SwiftShims
protocol RP {
func getThres() -> Int32
}
class RC : RP {
@inline(never) func getThres() -> Int32
}
sil hidden [noinline] @$s6simple2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32 {
bb0(%0 : $RC):
%1 = integer_literal $Builtin.Int32, 10
%2 = struct $Int32 (%1 : $Builtin.Int32)
return %2 : $Int32
}
// Note: The function_ref of "function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool" in the transformed code was originally a "function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool" taking an existential parameter and has been generic specialized after existential specialization.
// CHECK-LABEL: sil hidden [noinline] @$s6simple12find_wrapperSbyF : $@convention(thin) () -> Bool {
// CHECK: bb0:
// CHECK: alloc_ref
// CHECK: integer_literal
// CHECK: struct
// CHECK: alloc_stack
// CHECK: store
// CHECK: strong_retain
// CHECK: function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool
// CHECK: load
// CHECK: apply
// CHECK: destroy_addr
// CHECK: dealloc_stack
// CHECK: strong_release
// CHECK: return
// CHECK-LABEL: }
sil hidden [noinline] @$s6simple12find_wrapperSbyF : $@convention(thin) () -> Bool {
bb0:
%0 = alloc_ref $RC
%3 = integer_literal $Builtin.Int32, 0
%4 = struct $Int32 (%3 : $Builtin.Int32)
%5 = alloc_stack $RP
%6 = init_existential_addr %5 : $*RP, $RC
store %0 to %6 : $*RC
%8 = function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
strong_retain %0 : $RC
%10 = apply %8(%4, %5) : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
destroy_addr %5 : $*RP
dealloc_stack %5 : $*RP
strong_release %0 : $RC
return %10 : $Bool
}
sil private [transparent] [thunk] @$s6simple2RCCAA2RPA2aDP8getThress5Int32VyFTW : $@convention(witness_method: RP) (@in_guaranteed RC) -> Int32 {
bb0(%0 : $*RC):
%1 = load %0 : $*RC
%2 = class_method %1 : $RC, #RC.getThres!1 : (RC) -> () -> Int32, $@convention(method) (@guaranteed RC) -> Int32
%3 = apply %2(%1) : $@convention(method) (@guaranteed RC) -> Int32
return %3 : $Int32
}
// Note 1: The function_ref of "function_ref @$s6simple2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32" in the transformed code was originally a witness method "witness_method $@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP, #RP.getThres!1 : <Self where Self : RP> (Self) -> () -> Int32, %4 : $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP : $@convention(witness_method: RP) <τ_0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32" and has been devirtualized after existential specialization.
// Note 2: The function_ref of "function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool" in the transformed code was originally a "function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool" taking an existential parameter and has been generic specialized after existential specialization.
// CHECK-LABEL: sil shared [noinline] @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool {
// CHECK: bb0
// CHECK: alloc_stack
// CHECK: store
// CHECK: alloc_stack
// CHECK: init_existential_addr
// CHECK: copy_addr
// CHECK: open_existential_addr
// CHECK: unchecked_addr_cast
// CHECK: load
// CHECK: function_ref @$s6simple2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32
// CHECK: apply
// CHECK: struct_extract
// CHECK: struct_extract
// CHECK: builtin
// CHECK: cond_br
// CHECK: bb1:
// CHECK: integer_literal
// CHECK: integer_literal
// CHECK: builtin
// CHECK: tuple_extract
// CHECK: struct
// CHECK: function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool
// CHECK: load
// CHECK: apply
// CHECK: br
// CHECK: bb2:
// CHECK: integer_literal
// CHECK: struct
// CHECK: br
// CHECK: bb3
// CHECK: dealloc_stack
// CHECK: dealloc_stack
// CHECK: return
// CHECK-LABEL: }
sil hidden [noinline] @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool {
bb0(%0 : $Int32, %1 : $*RP):
%4 = open_existential_addr immutable_access %1 : $*RP to $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP
%5 = witness_method $@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP, #RP.getThres!1 : <Self where Self : RP> (Self) -> () -> Int32, %4 : $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP : $@convention(witness_method: RP) <τ_0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32
%6 = apply %5<@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP>(%4) : $@convention(witness_method: RP) _0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32
%7 = struct_extract %0 : $Int32, #Int32._value
%8 = struct_extract %6 : $Int32, #Int32._value
%9 = builtin "cmp_slt_Int32"(%7 : $Builtin.Int32, %8 : $Builtin.Int32) : $Builtin.Int1
cond_br %9, bb2, bb1
bb1:
%11 = integer_literal $Builtin.Int1, -1
%12 = struct $Bool (%11 : $Builtin.Int1)
br bb3(%12 : $Bool)
bb2:
%14 = integer_literal $Builtin.Int32, 1
%15 = integer_literal $Builtin.Int1, -1
%16 = builtin "sadd_with_overflow_Int32"(%7 : $Builtin.Int32, %14 : $Builtin.Int32, %15 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%17 = tuple_extract %16 : $(Builtin.Int32, Builtin.Int1), 0
%18 = struct $Int32 (%17 : $Builtin.Int32)
%19 = function_ref @$s6simple4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
%20 = apply %19(%18, %1) : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
br bb3(%20 : $Bool)
bb3(%22 : $Bool):
return %22 : $Bool
}
sil_vtable RC {
#RC.getThres!1: (RC) -> () -> Int32 : @$s6simple2RCC8getThress5Int32VyF
}
sil_witness_table hidden RC: RP module simple {
method #RP.getThres!1: <Self where Self : RP> (Self) -> () -> Int32 : @$s6simple2RCCAA2RPA2aDP8getThress5Int32VyFTW
}
// <rdar://problem/49336444> SILCombine infinite loop.
//
// Test a apply argument from an init_existential with a sole
// conforming type. We currently bail on rewriting the apply because
// it returns the same substituted type. Avoid infinitely iterating in
// SILCombine due to repeatedly creating an destroying the same cast.
public protocol BaseProtocol {
func testProtocolMethod() -> Self
}
extension BaseProtocol {
func testProtocolMethod() -> Self {
return self
}
}
protocol SubProtocol : BaseProtocol {}
final class ClassImpl : SubProtocol {}
extension ClassImpl {
final func testProtocolMethod() -> ClassImpl
}
sil @testProtocolMethod : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
// Verify that the optimization was not performance and that we don't hang as a result.
// CHECK-LABEL: sil hidden @testConcreteInitExistential : $@convention(method) (@in SubProtocol) -> () {
// CHECK: [[E:%.*]] = init_existential_addr %{{.*}} : $*SubProtocol, $@opened("{{.*}}") SubProtocol
// CHECK: apply %{{.*}}<@opened("{{.*}}") SubProtocol>([[E]], %{{.*}}) : $@convention(method) <τ_0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
// CHECK-LABEL: } // end sil function 'testConcreteInitExistential'
sil hidden @testConcreteInitExistential : $@convention(method) (@in SubProtocol) -> () {
bb0(%0 : $*SubProtocol):
%10 = open_existential_addr immutable_access %0 : $*SubProtocol to $*@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol
%11 = alloc_stack $SubProtocol
%15 = function_ref @testProtocolMethod : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
%16 = init_existential_addr %11 : $*SubProtocol, $@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol
%17 = apply %15<@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol>(%16, %10) : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
dealloc_stack %11 : $*SubProtocol
%80 = tuple ()
return %80 : $()
}