// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -specdevirt -code-sinking -dce -early-codemotion -disable-sil-cm-rr-cm=0 -retain-sinking | %FileCheck %s sil_stage canonical import Builtin import Swift class A { func ping(x: Builtin.Int32) -> Builtin.Int32 } class B : A { override func ping(x: Builtin.Int32) -> Builtin.Int32 } class C : A { override func ping(x: Builtin.Int32) -> Builtin.Int32 } class D : C { override func ping(x: Builtin.Int32) -> Builtin.Int32 } sil @external_func : $@convention(thin) (A) -> Builtin.Int32 sil @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 sil @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed B) -> Builtin.Int32 sil @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed C) -> Builtin.Int32 sil @_TFC25polymorphic_inline_caches1D4pingfS0_FBi32_Bi32_ : $@convention(method) (Builtin.Int32, @guaranteed D) -> Builtin.Int32 // CHECK-LABEL: sil @_TF25polymorphic_inline_caches3fooFTCS_1ABi32__Bi32_ sil @_TF25polymorphic_inline_caches3fooFTCS_1ABi32__Bi32_ : $@convention(thin) (@owned A, Builtin.Int32) -> Builtin.Int32 { // CHECK: bb0 bb0(%0 : $A, %1 : $Builtin.Int32): // CHECK-NOT: strong_retain %0 strong_retain %0 : $A // CHECK-NOT: class_method %5 = class_method %0 : $A, #A.ping!1 : (A) -> (Builtin.Int32) -> Builtin.Int32, $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 // CHECK-NOT: apply %6 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 // CHECK: checked_cast_br [exact] %0 : $A to $A, bb2, bb3 // CHECK: bb1 strong_release %0 : $A // CHECK: return return %6 : $Builtin.Int32 // CHECK: bb2 // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ // CHECK: apply [[APING]] // CHECK: bb3 // CHECK: checked_cast_br [exact] %0 : $A to $B, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ // CHECK: apply [[BPING]] // CHECK: bb6 // CHECK: checked_cast_br [exact] %0 : $A to $C, bb8, bb9 // CHECK: bb7 // CHECK: br bb4 // CHECK: bb8 // CHECK: [[CPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ // CHECK: apply [[CPING]] // CHECK: bb9 // CHECK: [[PING:%.*]] = class_method %0 : $A, #A.ping!1 // CHECK: apply [[PING]] } // CHECK-LABEL: sil @polymorphic_inline_caches_dont_sink_retain sil @polymorphic_inline_caches_dont_sink_retain : $@convention(thin) (@owned A, Builtin.Int32) -> Builtin.Int32 { // CHECK: bb0 bb0(%0 : $A, %1 : $Builtin.Int32): // CHECK: strong_retain %0 %4 = function_ref @external_func : $@convention(thin) (A) -> Builtin.Int32 %5 = class_method %0 : $A, #A.ping!1 : (A) -> (Builtin.Int32) -> Builtin.Int32, $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 // Don't sink this retain, because its argument is used by %6 (apply of @external_func) strong_retain %0 : $A // CHECK-NOT: class_method // CHECK: apply // CHECK-NOT: apply %6 = apply %4(%0) : $@convention(thin) (A) -> Builtin.Int32 %7 = apply %5(%1, %0) : $@convention(method) (Builtin.Int32, @guaranteed A) -> Builtin.Int32 // CHECK: checked_cast_br [exact] %0 : $A to $A, bb2, bb3 // CHECK: bb1 strong_release %0 : $A // CHECK: return return %6 : $Builtin.Int32 // CHECK: bb2 // CHECK: [[APING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ // CHECK: apply [[APING]] // CHECK: bb3 // CHECK: checked_cast_br [exact] %0 : $A to $B, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 // CHECK: [[BPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ // CHECK: apply [[BPING]] // CHECK: bb6 // CHECK: checked_cast_br [exact] %0 : $A to $C, bb8, bb9 // CHECK: bb7 // CHECK: br bb4 // CHECK: bb8 // CHECK: [[CPING:%.*]] = function_ref @_TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ // CHECK: apply [[CPING]] // CHECK: bb9 // CHECK: [[PING:%.*]] = class_method %0 : $A, #A.ping!1 // CHECK: apply [[PING]] } sil_vtable A { #A.ping!1: _TFC25polymorphic_inline_caches1A4pingfS0_FBi32_Bi32_ } sil_vtable B { #A.ping!1: _TFC25polymorphic_inline_caches1B4pingfS0_FBi32_Bi32_ } sil_vtable C { #A.ping!1: _TFC25polymorphic_inline_caches1C4pingfS0_FBi32_Bi32_ } sil_vtable D { #A.ping!1: _TFC25polymorphic_inline_caches1D4pingfS0_FBi32_Bi32_ } protocol P { func foo() } public class E : P { @inline(never) func foo() deinit init() } public class F : E { @inline(never) override func foo() deinit override init() } public final class G : E { @inline(never) override final func foo() deinit override init() } sil [noinline] @_TFC5casts1E3foofS0_FT_T_ : $@convention(method) (@guaranteed E) -> () sil [noinline] @_TFC5casts1F3foofS0_FT_T_ : $@convention(method) (@guaranteed F) -> () sil [noinline] @_TFC5casts1G3foofS0_FT_T_ : $@convention(method) (@guaranteed G) -> () // CHECK-LABEL: sil [noinline] @_TF5casts18unchecked_ref_castFBoT_ sil [noinline] @_TF5casts18unchecked_ref_castFBoT_ : $@convention(thin) (@owned Builtin.NativeObject) -> () { // CHECK: bb0 bb0(%0 : $Builtin.NativeObject): strong_retain %0 : $Builtin.NativeObject // CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Builtin.NativeObject to $E %3 = unchecked_ref_cast %0 : $Builtin.NativeObject to $E // CHECK-NOT: class_method %4 = class_method %3 : $E, #E.foo!1 : (E) -> () -> (), $@convention(method) (@guaranteed E) -> () %5 = apply %4(%3) : $@convention(method) (@guaranteed E) -> () strong_release %0 : $Builtin.NativeObject %7 = tuple () return %7 : $() // CHECK: checked_cast_br [exact] [[CAST]] : $E to $E, bb2, bb3 // CHECK: bb1 // CHECK: strong_release %0 // CHECK: return // CHECK: bb2 // CHECK: [[EFOO:%.*]] = function_ref @_TFC5casts1E3foofS0_FT_T_ // CHECK: strong_retain %0 // CHECK: apply [[EFOO]] // CHECK: bb3 // CHECK: checked_cast_br [exact] [[CAST]] : $E to $F, bb5, bb6 // CHECK: bb4 // CHECK: br bb1 // CHECK: bb5 // CHECK: [[FFOO:%.*]] = function_ref @_TFC5casts1F3foofS0_FT_T_ // CHECK: strong_retain %0 // CHECK: apply [[FFOO]] // CHECK: bb6 // CHECK: checked_cast_br [exact] [[CAST]] : $E to $G, bb8, bb9 // CHECK: bb7 // CHECK: bb8 // CHECK: [[GFOO:%.*]] = function_ref @_TFC5casts1G3foofS0_FT_T_ // CHECK: strong_retain %0 // CHECK: apply [[GFOO]] // CHECK: bb9 // CHECK: [[DYNAMIC:%.*]] = class_method [[CAST]] : $E, #E.foo!1 // CHECK: strong_retain %0 // CHECK: apply [[DYNAMIC]] } // CHECK-LABEL: sil [noinline] @_TF5casts22unchecked_ref_castFCS_1ET_ sil [noinline] @_TF5casts22unchecked_ref_castFCS_1ET_ : $@convention(thin) (@owned E) -> () { // CHECK: bb0 bb0(%0 : $E): strong_retain %0 : $E // CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $E to $F %3 = unchecked_ref_cast %0 : $E to $F // CHECK-NOT: class_method %4 = class_method %3 : $F, #F.foo!1 : (F) -> () -> (), $@convention(method) (@guaranteed F) -> () %5 = apply %4(%3) : $@convention(method) (@guaranteed F) -> () strong_release %0 : $E %7 = tuple () return %7 : $() // CHECK: checked_cast_br [exact] [[CAST]] : $F to $F, bb2, bb3 // CHECK: bb1 // CHECK: strong_release %0 // CHECK: return // CHECK: bb2 // CHECK: [[FFOO:%.*]] = function_ref @_TFC5casts1F3foofS0_FT_T_ // CHECK: strong_retain %0 // CHECK: apply [[FFOO]] // CHECK: bb3 // CHECK: [[DYNAMIC:%.*]] = class_method [[CAST]] : $F, #F.foo!1 // CHECK: strong_retain %0 // CHECK: apply [[DYNAMIC]] } sil_vtable E { #E.foo!1: _TFC5casts1E3foofS0_FT_T_ } sil_vtable F { #E.foo!1: _TFC5casts1F3foofS0_FT_T_ } sil_vtable G { #E.foo!1: _TFC5casts1G3foofS0_FT_T_ }