| // RUN: %target-sil-opt -enable-objc-interop -module-name devirt_speculative -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -specdevirt | %FileCheck %s |
| |
| sil_stage canonical |
| |
| import Builtin |
| import Swift |
| |
| @objc class MyNSObject {} |
| private class Base : MyNSObject { |
| override init() |
| @inline(never) func foo() |
| @inline(never) func exit() -> Never |
| } |
| |
| private class Sub : Base { |
| override init() |
| @inline(never) override func foo() |
| @inline(never) override func exit() -> Never |
| } |
| |
| sil private [noinline] @_TBaseFooFun : $@convention(method) (@guaranteed Base) -> () { |
| bb0(%0 : $Base): |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| sil private [noinline] @_TSubFooFun : $@convention(method) (@guaranteed Sub) -> () { |
| bb0(%0 : $Sub): |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| |
| sil @Base_exit : $@convention(method) (@guaranteed Base) -> Never |
| |
| sil @Sub_exit : $@convention(method) (@guaranteed Sub) -> Never |
| |
| sil_vtable Base { |
| #Base.foo!1: @_TBaseFooFun |
| #Base.exit!1: @Base_exit |
| } |
| |
| sil_vtable Sub { |
| #Base.foo!1: @_TSubFooFun |
| #Base.exit!1: @Sub_exit |
| } |
| |
| sil @test_objc_ancestry : $@convention(thin) (@guaranteed Base) -> () { |
| bb0(%0: $Base): |
| %1 = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () |
| %2 = apply %1(%0) : $@convention(method) (@guaranteed Base) -> () |
| %3 = tuple() |
| return %3 : $() |
| } |
| |
| // Make sure we leave the generic method call because an objc derived class can be extended at runtime. |
| |
| // CHECK-LABEL: sil @test_objc_ancestry |
| // CHECK: bb0 |
| // CHECK: [[METH:%.*]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> () |
| // CHECK: checked_cast_br [exact] %0 : $Base to $Base, bb{{.*}}, bb[[CHECK2:[0-9]+]] |
| // CHECK: bb[[CHECK2]]{{.*}}: |
| // CHECK: checked_cast_br [exact] %0 : $Base to $Sub, bb{{.*}}, bb[[GENCALL:[0-9]+]] |
| // CHECK: bb[[GENCALL]]{{.*}}: |
| // CHECK: apply [[METH]] |
| |
| struct MyValue {} |
| |
| private class Generic<T> : MyNSObject { |
| override init() |
| } |
| |
| private class Base2 : Generic<MyValue> { |
| override init() |
| @inline(never) func foo() |
| } |
| |
| private class Sub2 : Base2 { |
| override init() |
| @inline(never) override func foo() |
| } |
| |
| sil private [noinline] @_TBase2FooFun : $@convention(method) (@guaranteed Base2) -> () { |
| bb0(%0 : $Base2): |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| sil private [noinline] @_TSub2FooFun : $@convention(method) (@guaranteed Sub2) -> () { |
| bb0(%0 : $Sub2): |
| %1 = tuple() |
| return %1 : $() |
| } |
| |
| sil_vtable Base2 { |
| #Base2.foo!1: @_TBase2FooFun |
| } |
| |
| sil_vtable Sub2 { |
| #Base2.foo!1: @_TSub2FooFun |
| } |
| |
| sil @test_objc_ancestry2 : $@convention(thin) (@guaranteed Base2) -> () { |
| bb0(%0: $Base2): |
| %1 = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () |
| %2 = apply %1(%0) : $@convention(method) (@guaranteed Base2) -> () |
| %3 = tuple() |
| return %3 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_objc_ancestry2 |
| // CHECK: bb0 |
| // CHECK: [[METH:%.*]] = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () |
| // CHECK: checked_cast_br [exact] %0 : $Base2 to $Base2, bb{{.*}}, bb[[CHECK2:[0-9]+]] |
| // CHECK: bb[[CHECK2]]{{.*}}: |
| // CHECK: checked_cast_br [exact] %0 : $Base2 to $Sub2, bb{{.*}}, bb[[GENCALL:[0-9]+]] |
| // CHECK: bb[[GENCALL]]{{.*}}: |
| // CHECK: apply [[METH]] |
| |
| // Don't devirtualize 'unsafeGuaranteed' self calls. |
| // CHECK-LABEL: sil @test_unsafeGuaranteed |
| // CHECK-NOT: check_cast_br |
| // CHECK: return |
| sil @test_unsafeGuaranteed : $@convention(thin) (@guaranteed Base2) -> () { |
| bb0(%0: $Base2): |
| strong_retain %0 : $Base2 |
| %4 = builtin "unsafeGuaranteed"<Base2>(%0 : $Base2) : $(Base2, Builtin.Int8) |
| %5 = tuple_extract %4 : $(Base2, Builtin.Int8), 0 |
| %6 = tuple_extract %4 : $(Base2, Builtin.Int8), 1 |
| %1 = class_method %0 : $Base2, #Base2.foo!1 : (Base2) -> () -> (), $@convention(method) (@guaranteed Base2) -> () |
| %2 = apply %1(%5) : $@convention(method) (@guaranteed Base2) -> () |
| strong_release %5 : $Base2 |
| %16 = builtin "unsafeGuaranteedEnd"(%6 : $Builtin.Int8) : $() |
| %3 = tuple() |
| return %3 : $() |
| } |
| |
| // Check that NoReturn functions are devirtualized properly. |
| // The new apply should be followed by an unreachable instruction |
| // instead of a branch instruction. |
| // CHECK-LABEL: sil{{.*}}test_devirt_of_noreturn_function |
| // CHECK: checked_cast_br |
| // CHECK: function_ref @Base_exit |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: unreachable |
| // CHECK: checked_cast_br |
| // CHECK: function_ref @Sub_exit |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: unreachable |
| sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () { |
| bb0(%0 : $Base): |
| %2 = class_method %0 : $Base, #Base.exit!1 : (Base) -> () -> Never, $@convention(method) (@guaranteed Base) -> Never |
| %3 = apply %2(%0) : $@convention(method) (@guaranteed Base) -> Never |
| unreachable |
| } |
| |
| class Throw { |
| func mayThrow() throws |
| } |
| |
| class S1 : Throw { |
| override func mayThrow() |
| } |
| |
| class S2 : Throw { |
| override func mayThrow() throws |
| } |
| |
| sil hidden [thunk] @$S4main4ThrowC8mayThrowyyKF : $@convention(method) (@guaranteed Throw) -> @error Error { |
| bb0(%0 : $Throw): |
| %1 = tuple () |
| return %1 : $() |
| } |
| |
| sil hidden @$S4main2S1C8mayThrowyyF : $@convention(method) (@guaranteed S1) -> () { |
| bb0(%0 : $S1): |
| %1 = tuple () |
| return %1 : $() |
| } |
| |
| sil hidden [thunk] [always_inline] @$S4main2S2C8mayThrowyyKF : $@convention(method) (@guaranteed S2) -> @error Error { |
| bb0(%0 : $S2): |
| %1 = tuple () |
| return %1 : $() |
| } |
| |
| // CHECK-LABEL: sil{{.*}} @test_devirt_of_throw_without_error |
| // CHECK-NOT: checked_cast_br [exact] %0 : $Throw to $S1 |
| // CHECK: return |
| |
| sil hidden [noinline] @test_devirt_of_throw_without_error : $@convention(thin) (@owned Throw) -> () { |
| bb0(%0 : $Throw): |
| %80 = class_method %0 : $Throw, #Throw.mayThrow!1 : (Throw) -> () throws -> (), $@convention(method) (@guaranteed Throw) -> @error Error // user: %81 |
| try_apply %80(%0) : $@convention(method) (@guaranteed Throw) -> @error Error, normal bb7, error bb6 |
| |
| bb6(%82 : $Error): |
| br bb1 |
| |
| bb7(%84 : $()): |
| br bb1 |
| |
| bb1: |
| %3 = tuple () |
| return %3 : $() |
| } |
| |
| sil_vtable Throw { |
| #Throw.mayThrow!1: (Throw) -> () throws -> () : @$S4main4ThrowC8mayThrowyyKF |
| } |
| |
| sil_vtable S1 { |
| #Throw.mayThrow!1: (Throw) -> () throws -> () : @$S4main2S1C8mayThrowyyF [override] |
| } |
| |
| sil_vtable S2 { |
| #Throw.mayThrow!1: (Throw) -> () throws -> () : @$S4main2S2C8mayThrowyyKF [override] |
| } |