mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The old C++ pass didn't catch a few cases. Also: * The new pass is significantly simpler: it doesn't perform dataflow for _all_ memory locations at once using bitfields, but handles each store separately. (In both implementations there is a complexity limit in place to avoid quadratic complexity) * The new pass works with OSSA
124 lines
5.2 KiB
Plaintext
124 lines
5.2 KiB
Plaintext
// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all %s | %FileCheck %s
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
public class A {
|
|
deinit
|
|
init()
|
|
}
|
|
|
|
public protocol P {
|
|
func foo() -> Int64
|
|
}
|
|
|
|
public class B : A, P {
|
|
public func foo() -> Int64
|
|
deinit
|
|
override init()
|
|
}
|
|
|
|
public class C : A, P {
|
|
public func foo() -> Int64
|
|
deinit
|
|
override init()
|
|
}
|
|
|
|
func imp(x: A & P, y: A & P) -> Int64
|
|
|
|
public func test(x: B, y: C) -> Int64
|
|
|
|
// protocol witness for P.foo() in conformance B
|
|
sil shared [transparent] [serialized] [thunk] @Pfoo : $@convention(witness_method: P) (@in_guaranteed B) -> Int64 {
|
|
// %0 // user: %1
|
|
bb0(%0 : $*B):
|
|
%1 = load %0 : $*B // users: %2, %3
|
|
%2 = class_method %1 : $B, #B.foo : (B) -> () -> Int64, $@convention(method) (@guaranteed B) -> Int64 // user: %3
|
|
%3 = apply %2(%1) : $@convention(method) (@guaranteed B) -> Int64 // user: %4
|
|
return %3 : $Int64 // id: %4
|
|
} // end sil function 'Pfoo'
|
|
|
|
// B.foo()
|
|
sil [noinline] @foo : $@convention(method) (@guaranteed B) -> Int64 {
|
|
// %0 // user: %1
|
|
bb0(%0 : $B):
|
|
debug_value %0 : $B, let, name "self", argno 1 // id: %1
|
|
%2 = integer_literal $Builtin.Int64, 1 // user: %3
|
|
%3 = struct $Int64 (%2 : $Builtin.Int64) // user: %4
|
|
return %3 : $Int64 // id: %4
|
|
} // end sil function 'foo'
|
|
|
|
|
|
// This function calls @impl. The optimizer generates a specialization of impl
|
|
// based on the arguments passed to it here. Even though this function isn't
|
|
// directly checked, it is essential for the optimization.
|
|
// test(x:y:)
|
|
sil @test :$@convention(thin) (@guaranteed B, @guaranteed C) -> Int64 {
|
|
// %0 // users: %5, %4, %2
|
|
// %1 // users: %7, %6, %3
|
|
bb0(%0 : $B, %1 : $C):
|
|
debug_value %0 : $B, let, name "x", argno 1 // id: %2
|
|
debug_value %1 : $C, let, name "y", argno 2 // id: %3
|
|
strong_retain %0 : $B // id: %4
|
|
%5 = init_existential_ref %0 : $B : $B, $A & P // users: %11, %9
|
|
strong_retain %1 : $C // id: %6
|
|
%7 = init_existential_ref %1 : $C : $C, $A & P // users: %10, %9
|
|
// function_ref imp(x:y:)
|
|
%8 = function_ref @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 // user: %9
|
|
%9 = apply %8(%5, %7) : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 // user: %12
|
|
strong_release %7 : $A & P // id: %10
|
|
strong_release %5 : $A & P // id: %11
|
|
return %9 : $Int64 // id: %12
|
|
} // end sil function 'test'
|
|
|
|
// We're looking of an optimized specialization of @impl, not @impl itself.
|
|
// CHECK-NOT: @impl
|
|
// CHECK-LABEL: sil shared [noinline] @{{.*}}impl{{.*}}
|
|
// In the optimization pass we look for uses of alloc_stack (aka %5).
|
|
// This test makes sure that we look at the correct uses with the
|
|
// correct dominance order (store before apply before dealloc).
|
|
// This function will be passed arguments of type B and C for arguments
|
|
// %0 and %1 respectively. We want to make sure that we call B's foo method
|
|
// and not C's foo method.
|
|
sil hidden [noinline] @impl : $@convention(thin) (@guaranteed A & P, @guaranteed A & P) -> Int64 {
|
|
bb0(%0 : $A & P, %1 : $A & P):
|
|
%2 = open_existential_ref %0 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
%3 = unchecked_ref_cast %1 : $A & P to $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
|
|
%5 = alloc_stack $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
store %2 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
|
|
// We want to make sure that we picked up on the FIRST store and not the second one.
|
|
// class C's foo method is named "bar" whereas class B's foo method is named "foo".
|
|
// We want to make sure that we call a function named "foo" not "bar".
|
|
// CHECK: [[FN:%[0-9]+]] = function_ref @${{.*}}foo{{.*}} : $@convention(thin) () -> Int64
|
|
%7 = witness_method $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self, #P.foo : <Self where Self : P> (Self) -> () -> Int64, %2 : $@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
|
|
// CHECK: apply [[FN]]
|
|
%8 = apply %7<@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self>(%5) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64
|
|
|
|
store %3 to %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
dealloc_stack %5 : $*@opened("A1F1B526-1463-11EA-932F-ACBC329C418B", A & P) Self
|
|
return %8 : $Int64
|
|
// CHECK-LABEL: end sil function {{.*}}impl{{.*}}
|
|
} // end sil function 'impl'
|
|
|
|
// C.foo()
|
|
sil @bar : $@convention(method) (@guaranteed C) -> Int64
|
|
|
|
sil_vtable [serialized] B {
|
|
#B.foo: (B) -> () -> Int64 : @foo // B.foo()
|
|
}
|
|
|
|
sil_vtable [serialized] C {
|
|
#C.foo: (C) -> () -> Int64 : @bar
|
|
}
|
|
|
|
sil_witness_table [serialized] B: P module run {
|
|
method #P.foo: <Self where Self : P> (Self) -> () -> Int64 : @Pfoo // protocol witness for P.foo() in conformance B
|
|
}
|