mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Reimplement most of the logic in Swift as an Instruction simplification and remove the old code from SILCombine
* support more cases of existential archetype replacements:
For example:
```
%0 = alloc_stack $any P
%1 = init_existential_addr %0, $T
use %1
```
is transformed to
```
%0 = alloc_stack $T
use %0
```
Also, if the alloc_stack is already an opened existential and the concrete type is known,
replace it as well:
```
%0 = metatype $@thick T.Type
%1 = init_existential_metatype %0, $@thick any P.Type
%2 = open_existential_metatype %1 : $@thick any P.Type to $@thick (@opened("X", P) Self).Type
...
%3 = alloc_stack $@opened("X", any P) Self
use %3
```
is transformed to
```
...
%3 = alloc_stack $T
use %3
```
377 lines
11 KiB
Swift
377 lines
11 KiB
Swift
|
|
// RUN: %target-swift-frontend -module-name devirt_protocol_method_invocations -enable-spec-devirt -O -Xllvm -sil-disable-pass=ExistentialSpecializer -Xllvm -sil-print-types -emit-sil %s | %FileCheck %s
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
protocol PPP {
|
|
func f()
|
|
}
|
|
|
|
protocol QQQ : PPP {
|
|
}
|
|
|
|
protocol RRR : QQQ {
|
|
}
|
|
|
|
struct S : RRR {}
|
|
|
|
extension QQQ {
|
|
@_optimize(none)
|
|
func f() {}
|
|
}
|
|
|
|
// Test that all witness_method instructions are devirtualized.
|
|
// This test used to crash the compiler because it uses inherited conformances.
|
|
// CHECK-LABEL: sil @$s34devirt_protocol_method_invocations24testInheritedConformanceyyF : $@convention(thin) () -> ()
|
|
// CHECK-NOT: witness_method
|
|
// CHECK-NOT: class_method
|
|
// CHECK: apply
|
|
// CHECK: // end sil function '$s34devirt_protocol_method_invocations24testInheritedConformanceyyF'
|
|
public func testInheritedConformance() {
|
|
(S() as QQQ).f()
|
|
}
|
|
|
|
// Test that a witness_method instruction using an indirectly-inherited conformance
|
|
// is devirtualized.
|
|
//
|
|
// This test used to crash the compiler because it uses inherited conformances.
|
|
// CHECK-LABEL: sil @$s34devirt_protocol_method_invocations34testIndirectlyInheritedConformanceyyF : $@convention(thin) () -> ()
|
|
// CHECK-NOT: witness_method
|
|
// CHECK: apply
|
|
// CHECK: // end sil function '$s34devirt_protocol_method_invocations34testIndirectlyInheritedConformanceyyF'
|
|
public func testIndirectlyInheritedConformance() {
|
|
(S() as RRR).f()
|
|
}
|
|
|
|
|
|
public protocol Foo {
|
|
func foo(_ x:Int) -> Int
|
|
}
|
|
|
|
public extension Foo {
|
|
func boo(_ x:Int) -> Int32 {
|
|
return 2222 + Int32(x)
|
|
}
|
|
|
|
func getSelf() -> Self {
|
|
return self
|
|
}
|
|
}
|
|
|
|
var gg = 1111
|
|
|
|
open class C : Foo {
|
|
@inline(never)
|
|
open func foo(_ x:Int) -> Int {
|
|
gg += 1
|
|
return gg + x
|
|
}
|
|
}
|
|
|
|
@_transparent
|
|
func callfoo(_ f: Foo) -> Int {
|
|
return f.foo(2) + f.foo(2)
|
|
}
|
|
|
|
@_transparent
|
|
func callboo(_ f: Foo) -> Int32 {
|
|
return f.boo(2) + f.boo(2)
|
|
}
|
|
|
|
@_transparent
|
|
func callGetSelf(_ f: Foo) -> Foo {
|
|
return f.getSelf()
|
|
}
|
|
|
|
// Check that methods returning Self are not devirtualized and do not crash the compiler.
|
|
// CHECK-LABEL: sil [noinline] {{.*}}@$s34devirt_protocol_method_invocations05test_a1_b11_extension_C33_invocation_with_self_return_typeyAA3Foo_pAA1CCF
|
|
// CHECK: init_existential_addr
|
|
// CHECK: open_existential_addr
|
|
// CHECK: return
|
|
@inline(never)
|
|
public func test_devirt_protocol_extension_method_invocation_with_self_return_type(_ c: C) -> Foo {
|
|
return callGetSelf(c)
|
|
}
|
|
|
|
// Check that calls to f.foo() get devirtualized and are not invoked
|
|
// via the expensive witness_method instruction.
|
|
// To achieve that the information about a concrete type C should
|
|
// be propagated from init_existential_addr into witness_method and
|
|
// apply instructions.
|
|
|
|
// CHECK-LABEL: sil [noinline] @$s34devirt_protocol_method_invocations05test_a1_b1_C11_invocationySiAA1CCF
|
|
// CHECK-NOT: witness_method
|
|
// CHECK: checked_cast
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK: bb1(
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK: return
|
|
// CHECK: bb2(
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK: function_ref
|
|
// CHECK: apply
|
|
// CHECK: apply
|
|
// CHECK: br bb1(
|
|
// CHECK: bb3
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK: apply
|
|
// CHECK: apply
|
|
// CHECK: br bb1(
|
|
|
|
// Check that calls of a method boo() from the protocol extension
|
|
// get devirtualized and are not invoked via the expensive witness_method instruction
|
|
// or by passing an existential as a parameter.
|
|
// To achieve that the information about a concrete type C should
|
|
// be propagated from init_existential_addr into apply instructions.
|
|
// In fact, the call is expected to be inlined and then constant-folded
|
|
// into a single integer constant.
|
|
|
|
// CHECK-LABEL: sil [noinline] {{.*}}@$s34devirt_protocol_method_invocations05test_a1_b11_extension_C11_invocationys5Int32VAA1CCF
|
|
// CHECK-NOT: checked_cast
|
|
// CHECK-NOT: open_existential
|
|
// CHECK-NOT: witness_method
|
|
// CHECK-NOT: apply
|
|
// CHECK: integer_literal
|
|
// CHECK: return
|
|
|
|
// CHECK: sil @$s34devirt_protocol_method_invocations12test24114020SiyF
|
|
// CHECK: [[T0:%.*]] = integer_literal $Builtin.Int{{.*}}, 1
|
|
// CHECK: [[T1:%.*]] = struct $Int ([[T0]] : $Builtin.Int{{.*}})
|
|
// CHECK: return [[T1]]
|
|
|
|
// CHECK: sil @$s34devirt_protocol_method_invocations14testExMetatypeSiyF
|
|
// CHECK: [[T0:%.*]] = integer_literal
|
|
// CHECK: [[T2:%.*]] = struct $Int ([[T0]] : {{.*}})
|
|
// CHECK: return [[T2]] : $Int
|
|
|
|
@inline(never)
|
|
public func test_devirt_protocol_method_invocation(_ c: C) -> Int {
|
|
return callfoo(c)
|
|
}
|
|
|
|
@inline(never)
|
|
public func test_devirt_protocol_extension_method_invocation(_ c: C) -> Int32 {
|
|
return callboo(c)
|
|
}
|
|
|
|
|
|
// Make sure that we are not crashing with an assertion due to specialization
|
|
// of methods with the Self return type as an argument.
|
|
// rdar://20868966
|
|
protocol Proto {
|
|
func f() -> Self
|
|
}
|
|
|
|
class CC : Proto {
|
|
func f() -> Self { return self }
|
|
}
|
|
|
|
func callDynamicSelfExistential(_ p: Proto) {
|
|
p.f()
|
|
}
|
|
|
|
public func testSelfReturnType() {
|
|
callDynamicSelfExistential(CC())
|
|
}
|
|
|
|
|
|
// Make sure that we are not crashing with an assertion due to specialization
|
|
// of methods with the Self return type.
|
|
// rdar://20955745.
|
|
protocol CP : class { func f() -> Self }
|
|
func callDynamicSelfClassExistential(_ cp: CP) { cp.f() }
|
|
class PP : CP {
|
|
func f() -> Self { return self }
|
|
}
|
|
|
|
callDynamicSelfClassExistential(PP())
|
|
|
|
// Make sure we handle indirect conformances.
|
|
// rdar://24114020
|
|
protocol Base {
|
|
var x: Int { get }
|
|
}
|
|
protocol Derived : Base {
|
|
}
|
|
struct SimpleBase : Derived {
|
|
var x: Int
|
|
}
|
|
public func test24114020() -> Int {
|
|
let base: Derived = SimpleBase(x: 1)
|
|
return base.x
|
|
}
|
|
|
|
protocol StaticP {
|
|
static var size: Int { get }
|
|
}
|
|
struct HasStatic<T> : StaticP {
|
|
static var size: Int { return MemoryLayout<T>.size }
|
|
}
|
|
public func testExMetatype() -> Int {
|
|
let type: StaticP.Type = HasStatic<Int>.self
|
|
return type.size
|
|
}
|
|
|
|
// rdar://32288618
|
|
public func testExMetatypeVar() -> Int {
|
|
var type: StaticP.Type = HasStatic<Int>.self
|
|
return type.size
|
|
}
|
|
|
|
// IRGen used to crash on the testPropagationOfConcreteTypeIntoExistential method.
|
|
// rdar://26286278
|
|
|
|
protocol MathP {
|
|
var sum: Int32 { get nonmutating set }
|
|
func done()
|
|
}
|
|
|
|
extension MathP {
|
|
@inline(never)
|
|
func plus() -> Self {
|
|
sum += 1
|
|
return self
|
|
}
|
|
|
|
@inline(never)
|
|
func minus() {
|
|
sum -= 1
|
|
if sum == 0 {
|
|
done()
|
|
}
|
|
}
|
|
}
|
|
|
|
protocol MathA : MathP {}
|
|
|
|
public final class V {
|
|
var a: MathA
|
|
|
|
init(a: MathA) {
|
|
self.a = a
|
|
}
|
|
}
|
|
|
|
// Check that all witness_method invocations are devirtualized.
|
|
// CHECK-LABEL: sil [noinline] {{.*}}@$s34devirt_protocol_method_invocations44testPropagationOfConcreteTypeIntoExistential1v1xyAA1VC_s5Int32VtF
|
|
// CHECK-NOT: witness_method
|
|
// CHECK-NOT: class_method
|
|
// CHECK: return
|
|
@inline(never)
|
|
public func testPropagationOfConcreteTypeIntoExistential(v: V, x: Int32) {
|
|
let y = v.a.plus()
|
|
defer {
|
|
y.minus()
|
|
}
|
|
}
|
|
|
|
// Check that we don't attempt to cast an opened type to a concrete
|
|
// type inferred via ProtocolConformanceAnalysis if the type requires
|
|
// reabstraction when erased by an existential.
|
|
protocol ReabstractedP {
|
|
func f()
|
|
}
|
|
extension Optional : ReabstractedP {
|
|
func f() {}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [noinline] {{.*}}@$s34devirt_protocol_method_invocations23testReabstractedWitnessyyAA0F1P_pF : $@convention(thin) (@in_guaranteed any ReabstractedP) -> () {
|
|
// CHECK: bb0(%0 : $*any ReabstractedP):
|
|
// CHECK: [[OPEN:%.*]] = open_existential_addr immutable_access %0 : $*any ReabstractedP to $*@opened([[ID:.*]], any ReabstractedP) Self
|
|
// CHECK: [[WM:%.*]] = witness_method $@opened([[ID]], any ReabstractedP) Self, #ReabstractedP.f : <Self where Self : ReabstractedP> (Self) -> () -> (), [[OPEN]] : $*@opened([[ID]], any ReabstractedP) Self : $@convention(witness_method: ReabstractedP) <τ_0_0 where τ_0_0 : ReabstractedP> (@in_guaranteed τ_0_0) -> ()
|
|
// CHECK: apply [[WM]]<@opened([[ID]], any ReabstractedP) Self>([[OPEN]]) : $@convention(witness_method: ReabstractedP) <τ_0_0 where τ_0_0 : ReabstractedP> (@in_guaranteed τ_0_0) -> ()
|
|
// CHECK-LABEL: } // end sil function '$s34devirt_protocol_method_invocations23testReabstractedWitnessyyAA0F1P_pF'
|
|
@inline(never)
|
|
func testReabstractedWitness(_ f: ReabstractedP) {
|
|
f.f()
|
|
}
|
|
|
|
public func testReabstracted(f: Optional<()->()>) {
|
|
testReabstractedWitness(f)
|
|
}
|
|
|
|
|
|
// Test that we don't devirtualize calls to protocol requirements with
|
|
// covariant `Self`-rooted type parameters nested inside a collection type;
|
|
// the devirtualizer doesn't know how to handle these yet.
|
|
protocol CovariantSelfInCollection {
|
|
associatedtype Assoc
|
|
|
|
func self1() -> Array<Self>
|
|
func self2() -> Dictionary<String, Self>
|
|
func self3(_: (Self...) -> Void)
|
|
func self4(_: (Array<(Dictionary<String, String>, Self)>) -> Void)
|
|
|
|
func assoc1() -> Array<Assoc>
|
|
func assoc2() -> Dictionary<String, Assoc>
|
|
func assoc3(_: (Assoc...) -> Void)
|
|
func assoc4(_: (Array<(Dictionary<String, String>, Assoc)>) -> Void)
|
|
}
|
|
struct CovariantSelfInCollectionImpl: CovariantSelfInCollection {
|
|
typealias Assoc = Bool
|
|
|
|
func self1() -> Array<Self> { [self] }
|
|
func self2() -> Dictionary<String, Self> { [#file : self] }
|
|
func self3(_: (Self...) -> Void) {}
|
|
func self4(_: (Array<(Dictionary<String, String>, Self)>) -> Void) {}
|
|
|
|
func assoc1() -> Array<Assoc> { [true] }
|
|
func assoc2() -> Dictionary<String, Assoc> { [#file : true] }
|
|
func assoc3(_: (Assoc...) -> Void) {}
|
|
func assoc4(_: (Array<(Dictionary<String, String>, Assoc)>) -> Void) {}
|
|
}
|
|
// CHECK-LABEL: sil @$s34devirt_protocol_method_invocations12testNoDevirtyyF
|
|
//
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.self1
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.self2
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.self3
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.self4
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.assoc1
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.assoc2
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.assoc3
|
|
// CHECK: witness_method $CovariantSelfInCollectionImpl, #CovariantSelfInCollection.assoc4
|
|
// CHECK: end sil function '$s34devirt_protocol_method_invocations12testNoDevirtyyF'
|
|
public func testNoDevirt() {
|
|
let p: any CovariantSelfInCollection = CovariantSelfInCollectionImpl()
|
|
|
|
_ = p.self1()
|
|
_ = p.self2()
|
|
p.self3 { _ in }
|
|
p.self4 { _ in }
|
|
|
|
_ = p.assoc1()
|
|
_ = p.assoc2()
|
|
p.assoc3 { _ in }
|
|
p.assoc4 { _ in }
|
|
}
|
|
|
|
protocol MyProtocol {
|
|
associatedtype Element
|
|
var array: [Element] { get }
|
|
var foo: Bool { get }
|
|
}
|
|
|
|
extension Array {
|
|
var isThisACoolArray: Bool {
|
|
return true
|
|
}
|
|
}
|
|
|
|
extension MyProtocol {
|
|
var foo: Bool { array.isThisACoolArray }
|
|
}
|
|
|
|
public struct MyStruct {
|
|
var array: [Int] = []
|
|
}
|
|
|
|
extension MyStruct: MyProtocol {}
|
|
|
|
// CHECK-LABEL: sil @$s34devirt_protocol_method_invocations15testArrayReturn1xSbAA8MyStructVz_tF :
|
|
// CHECK-NOT: witness_method
|
|
// CHECK: } // end sil function '$s34devirt_protocol_method_invocations15testArrayReturn1xSbAA8MyStructVz_tF'
|
|
public func testArrayReturn(x: inout MyStruct) -> Bool {
|
|
return x.foo
|
|
}
|
|
|