| // 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 : $() |
| } |