blob: 0e3198119a91849eaa4a8b946ec79311652224a4 [file] [log] [blame]
// RUN: %target-sil-opt -enable-objc-interop -module-name devirt_speculative -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: unchecked_ref_cast
// 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]
}