Files
swift-mirror/test/SILOptimizer/closure_specialization_consolidated.sil
Erik Eckstein df20d36255 ClosureSpecialization: support for OSSA and a big overhaul
Beside supporting OSSA, this change significantly simplifies the pass.
The main change is that instead of starting at a closure (e.g. `partial_apply`) and finding all call sites, we now start at a call site and look for closures for all arguments. This makes a lot of things much simpler, e.g. not so many intermediate data structures are required to track all the states.

I needed to remove the 3 unit tests because the things those tests were testing are not there anymore. However, the pass is tested with a lot of sil tests (and I added quite a few), which should give good test coverage.

The old ClosureSpecializer pass is still kept in place, because at that point in the pipeline we don't have OSSA, yet. Once we have that, we can replace the old pass withe the new one.
However, the autodiff closure specializer already runs in the OSSA pipeline and there the new changes take effect.
2025-10-06 12:02:48 +02:00

693 lines
38 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all -closure-specialization %s | %FileCheck %s
import Builtin
import Swift
///////////////////
// Utility Types //
///////////////////
protocol P {
func foo(f: (Int32)->Int32, _ j: Int32) -> Int32
}
protocol Q {
}
public class C {
@_hasStorage var c: C? { get set }
init()
}
public struct S: Q {
@_hasStorage var c: C? { get set }
init(c: C?)
init()
}
// = Test Summary =
// We test the following things here:
//
// 1. Address Argument
// 2. ThinToThick, Partial Apply:
// a. with and without removal of closure.
// b. @owned and @guaranteed.
// 3. Bad NonFailureExitBB.
// 4. No Call in Apply Callee.
// 5. Non simple closure (i.e. non function_ref closure).
// 6. Handle interface return types correctly.
////////////////////////////
// Address Argument Tests //
////////////////////////////
//
// Make sure that we can specialize even if we have address arguments.
//
// But we don't handle closures that close over address types passed as @in or
// @in_guaranteed.
// (*NOTE* this includes address and non-address only types).
// This is a temporary limitation.
// CHECK-LABEL: sil [ossa] @address_closure : $@convention(thin) (@in Int32) -> () {
sil [ossa] @address_closure : $@convention(thin) (@in Int32) -> () {
bb0(%0 : $*Int32):
%6 = tuple()
return %6 : $()
}
sil [ossa] @address_closure_struct_complex : $@convention(thin) (@in_guaranteed S) -> () {
bb0(%0 : $*S):
%6 = tuple()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @address_closure_user : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
sil [ossa] @address_closure_user : $@convention(thin) (@owned @callee_owned () -> ()) -> () {
bb0(%0 : @owned $@callee_owned () -> ()):
%1 = apply %0() : $@callee_owned () -> ()
%9999 = tuple()
return %9999 : $()
}
// Check that a specialization of address_closure_noescape_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
// CHECK: function_ref @address_closure_trivial : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
// CHECK: apply
// CHECK: return
// Check that a specialization of address_closure_noescape_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable Int32) -> ()
// CHECK: function_ref @address_closure_trivial_mutating : $@convention(thin) (@inout_aliasable Int32) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (@inout_aliasable Int32) -> ()
// CHECK: apply
// CHECK: return
// Check that a specialization of address_closure_noescape_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable any P) -> ()
// CHECK: function_ref @address_closure_existential : $@convention(thin) (@inout_aliasable any P) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (@inout_aliasable any P) -> ()
// CHECK: apply
// CHECK: return
// Check that a specialization of address_closure_noescape_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: function_ref @address_closure_struct1 : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: apply
// CHECK: return
// Check that a specialization of address_closure_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: function_ref @address_closure_struct2 : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: apply
// CHECK: return
// Check that a specialization of address_closure_user was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
// CHECK: function_ref @address_closure_class1 : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
// CHECK: partial_apply %{{.*}} : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
// CHECK: apply
// CHECK: return
// CHECK-LABEL: sil [ossa] @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> () {
sil [ossa] @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> () {
bb0(%0 : @owned $@noescape @callee_owned () -> ()):
%1 = apply %0() : $@noescape @callee_owned () -> ()
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller : $@convention(thin) (@in Int32) -> () {
// CHECK-NOT: _TTSf1cl15address_closureSi__address_closure_user
sil [ossa] @address_caller : $@convention(thin) (@in Int32) -> () {
bb0(%0 : $*Int32):
%1 = function_ref @address_closure : $@convention(thin) (@in Int32) -> ()
%2 = partial_apply %1(%0) : $@convention(thin) (@in Int32) -> ()
%3 = function_ref @address_closure_user : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%4 = apply %3(%2) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%9999 = tuple()
return %9999 : $()
}
// We don't handle closures that close over address types passed as @in or
// @in_guaranteed.
// (*NOTE* this includes address and non-address only types).
// This is a temporary limitation.
//
// CHECK-LABEL: sil [ossa] @address_caller_complex : $@convention(thin) (@in Int32) -> ()
// CHECK-NOT: function_ref @{{.*}}address_closure_user{{.*}} : $@convention(thin) (@in Int32) -> ()
// CHECK: partial_apply
// CHECK-NOT: function_ref @{{.*}}address_closure_user{{.*}} : $@convention(thin) (@in Int32) -> ()
// CHECK: return
sil [ossa] @address_caller_complex : $@convention(thin) (@in Int32) -> () {
bb0(%0 : $*Int32):
%00 = alloc_stack $Int32
%01 = load [trivial] %0 : $*Int32
store %01 to [trivial] %00 : $*Int32
%1 = function_ref @address_closure : $@convention(thin) (@in Int32) -> ()
%2 = partial_apply %1(%00) : $@convention(thin) (@in Int32) -> ()
%3 = function_ref @address_closure_user : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%2c = copy_value %2
%4 = apply %3(%2c) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
dealloc_stack %00 : $*Int32
br bb1
bb1:
%6 = apply %3(%2) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%9999 = tuple()
return %9999 : $()
}
// We don't handle closures that close over address types passed as @in or
// @in_guaranteed.
// (*NOTE* this includes address and non-address only types).
// This is a temporary limitation.
//
// CHECK-LABEL: sil [ossa] @address_caller_struct_complex : $@convention(thin) (@in S) -> ()
// CHECK-NOT: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_user{{.*}} : $@convention(thin) (@in S) -> ()
// CHECK: partial_apply
// CHECK-NOT: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_user{{.*}} : $@convention(thin) (@in S) -> ()
// CHECK: return
sil [ossa] @address_caller_struct_complex : $@convention(thin) (@in S) -> () {
bb0(%0 : $*S):
%00 = alloc_stack $S
%01 = load [take] %0 : $*S
store %01 to [init] %00 : $*S
%1 = function_ref @address_closure_struct_complex : $@convention(thin) (@in_guaranteed S) -> ()
%2 = partial_apply %1(%00) : $@convention(thin) (@in_guaranteed S) -> ()
%3 = function_ref @address_closure_user : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%2c = copy_value %2
%4 = apply %3(%2c) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
dealloc_stack %00 : $*S
br bb1
bb1:
%6 = apply %3(%2) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%9999 = tuple()
return %9999 : $()
}
// More complex tests involving address arguments.
sil [ossa] @address_closure_trivial : $@convention(thin) (Int32, @inout_aliasable Int32) -> () {
bb0(%0 : $Int32, %1 : $*Int32):
%9 = integer_literal $Builtin.Int32, 42
%10 = struct $Int32 (%9 : $Builtin.Int32)
store %10 to [trivial] %1 : $*Int32
%12 = tuple ()
return %12 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller_trivial
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
// CHECK: apply [[SPECIALIZED_FN1]]{{.*}}
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_trivial: $@convention(thin) (Int32) -> Int32 {
bb0(%0 : $Int32):
%2 = alloc_stack $Int32, var, name "xx"
store %0 to [trivial] %2 : $*Int32
// function_ref address_closure_noescape_user(f:)
%4 = function_ref @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
// function_ref address_closure_trivial(x:)
%5 = function_ref @address_closure_trivial : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
%6 = partial_apply %5(%0, %2) : $@convention(thin) (Int32, @inout_aliasable Int32) -> ()
%6b = convert_escape_to_noescape %6 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%7 = apply %4(%6b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
destroy_value %6
%8 = load [trivial] %2 : $*Int32
dealloc_stack %2 : $*Int32
return %8 : $Int32
}
sil [ossa] @address_closure_trivial_mutating : $@convention(thin) (@inout_aliasable Int32) -> () {
bb0(%0 : $*Int32):
%2 = struct_element_addr %0 : $*Int32, #Int32._value
%3 = load [trivial] %2 : $*Builtin.Int32
%4 = integer_literal $Builtin.Int32, 1
%5 = integer_literal $Builtin.Int1, -1
%6 = builtin "sadd_with_overflow_Int32"(%3 : $Builtin.Int32, %4 : $Builtin.Int32, %5 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%7 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 0
%8 = tuple_extract %6 : $(Builtin.Int32, Builtin.Int1), 1
cond_fail %8 : $Builtin.Int1
%10 = struct $Int32 (%7 : $Builtin.Int32)
store %10 to [trivial] %0 : $*Int32
%12 = tuple ()
return %12 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller_trivial_mutating
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable Int32) -> ()
// CHECK: apply [[SPECIALIZED_FN1]]{{.*}}
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_trivial_mutating: $@convention(thin) (Int32) -> Int32 {
bb0(%0 : $Int32):
%2 = alloc_stack $Int32, var, name "xx"
store %0 to [trivial] %2 : $*Int32
%4 = function_ref @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%5 = function_ref @address_closure_trivial_mutating : $@convention(thin) (@inout_aliasable Int32) -> ()
%6 = partial_apply %5(%2) : $@convention(thin) (@inout_aliasable Int32) -> ()
%6b = convert_escape_to_noescape %6 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%7 = apply %4(%6b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
destroy_value %6
%8 = load [trivial] %2 : $*Int32
dealloc_stack %2 : $*Int32
return %8 : $Int32
}
sil [ossa] @S_init : $@convention(method) (@thin S.Type) -> @owned S
sil hidden [ossa] @address_closure_body_out_result : $@convention(thin) (@in Q, @in Q) -> @out Q {
bb0(%0 : $*Q, %1 : $*Q, %2 : $*Q):
%5 = init_existential_addr %0 : $*Q, $S
// function_ref S.init()
%6 = function_ref @S_init : $@convention(method) (@thin S.Type) -> @owned S
%7 = metatype $@thin S.Type
%8 = apply %6(%7) : $@convention(method) (@thin S.Type) -> @owned S
store %8 to [init] %5 : $*S
destroy_addr %2 : $*Q
destroy_addr %1 : $*Q
%12 = tuple ()
return %12 : $()
}
sil [ossa] @address_closure_out_result : $@convention(thin) (@in Q, @inout_aliasable Q, @inout_aliasable Q) -> @out Q {
bb0(%0 : $*Q, %1 : $*Q, %2 : $*Q, %3 : $*Q):
%7 = function_ref @address_closure_body_out_result : $@convention(thin) (@in Q, @in Q) -> @out Q
%8 = alloc_stack $Q
copy_addr %2 to [init] %8 : $*Q
%10 = alloc_stack $Q
copy_addr %3 to [init] %10 : $*Q
%12 = apply %7(%0, %8, %10) : $@convention(thin) (@in Q, @in Q) -> @out Q
dealloc_stack %10 : $*Q
dealloc_stack %8 : $*Q
destroy_addr %1 : $*Q
%16 = tuple ()
return %16 : $()
}
// Check that a specialization of address_closure_user_out_result was generated which does not
// take a closure as a parameter anymore.
// CHECK-LABEL: sil shared [ossa] @{{.*}}address_closure_user_out_result{{.*}} : $@convention(thin) (@inout_aliasable any Q, @inout_aliasable any Q) -> @out any Q
// CHECK: function_ref @address_closure_out_result : $@convention(thin) (@in any Q, @inout_aliasable any Q, @inout_aliasable any Q) -> @out any Q
// CHECK: [[PARTIAL_APPLY:%.*]] = partial_apply %{{.*}} : $@convention(thin) (@in any Q, @inout_aliasable any Q, @inout_aliasable any Q) -> @out any Q
// CHECK: apply [[PARTIAL_APPLY]]
// CHECK: return
sil [ossa] @address_closure_user_out_result : $@convention(thin) (@owned @noescape @callee_owned (@in Q) -> @out Q) -> @out Q {
bb0(%0 : $*Q, %1 : @owned $@noescape @callee_owned (@in Q) -> @out Q):
%4 = alloc_stack $Q
%5 = init_existential_addr %4 : $*Q, $S
%6 = function_ref @S_init : $@convention(method) (@thin S.Type) -> @owned S
%7 = metatype $@thin S.Type
%8 = apply %6(%7) : $@convention(method) (@thin S.Type) -> @owned S
store %8 to [init] %5 : $*S
%10 = apply %1(%0, %4) : $@noescape @callee_owned (@in Q) -> @out Q
dealloc_stack %4 : $*Q
%13 = tuple ()
return %13 : $()
}
// Check that closure specialization can handle cases where the full closure type may have
// unsupported address type arguments (e.g. @in or @out), but the partial_apply has only
// supported address type arguments, i.e. @inout or @inout_aliasable.
//
// CHECK-LABEL: sil [ossa] @address_caller_out_result : $@convention(thin) (@in any Q, @in any Q) -> @out any Q
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_user_out_result{{.*}} : $@convention(thin) (@inout_aliasable any Q, @inout_aliasable any Q) -> @out any Q
// CHECK: apply [[SPECIALIZED_FN1]]{{.*}}
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_out_result: $@convention(thin) (@in Q, @in Q) -> @out Q {
bb0(%0 : $*Q, %1 : $*Q, %2 : $*Q):
%5 = function_ref @address_closure_user_out_result : $@convention(thin) (@owned @noescape @callee_owned (@in Q) -> @out Q) -> @out Q
%6 = function_ref @address_closure_out_result : $@convention(thin) (@in Q, @inout_aliasable Q, @inout_aliasable Q) -> @out Q
%7 = partial_apply %6(%1, %2) : $@convention(thin) (@in Q, @inout_aliasable Q, @inout_aliasable Q) -> @out Q
%7b = convert_escape_to_noescape %7 : $@callee_owned (@in Q) -> @out Q to $@noescape @callee_owned (@in Q) -> @out Q
%8 = apply %5(%0, %7b) : $@convention(thin) (@owned @noescape @callee_owned (@in Q) -> @out Q) -> @out Q
destroy_value %7
destroy_addr %2 : $*Q
destroy_addr %1 : $*Q
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller_existential
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN2:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable any P) -> ()
// CHECK: apply [[SPECIALIZED_FN2]]{{.*}}
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable any P) -> ()
// CHECK: apply [[SPECIALIZED_FN1]]{{.*}}
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_existential : $@convention(thin) (@in P, @in P, Int32) -> @out P {
bb0(%0 : $*P, %1 : $*P, %2 : $*P, %3 : $Int32):
%7 = alloc_stack $P
copy_addr %1 to [init] %7 : $*P
%9 = function_ref @address_closure_existential : $@convention(thin) (@inout_aliasable P) -> ()
%10 = partial_apply %9(%7) : $@convention(thin) (@inout_aliasable P) -> ()
%10b = convert_escape_to_noescape %10 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%12 = function_ref @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%14 = apply %12(%10b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%10c = convert_escape_to_noescape %10 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%16 = apply %12(%10c) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%17 = integer_literal $Builtin.Int32, 10
%18 = struct_extract %3 : $Int32, #Int32._value
%19 = builtin "cmp_slt_Int32"(%17 : $Builtin.Int32, %18 : $Builtin.Int32) : $Builtin.Int1
cond_br %19, bb1, bb2
bb1:
destroy_addr %2 : $*P
copy_addr %1 to [init] %0 : $*P
destroy_addr %1 : $*P
destroy_value %10
br bb3
bb2:
destroy_addr %1 : $*P
copy_addr %2 to [init] %0 : $*P
destroy_addr %2 : $*P
destroy_value %10
br bb3
bb3:
destroy_addr %7 : $*P
dealloc_stack %7 : $*P
%33 = tuple ()
return %33 : $()
}
sil shared [ossa] @address_closure_existential : $@convention(thin) (@inout_aliasable P) -> () {
bb0(%0 : $*P):
%7 = tuple ()
return %7 : $()
}
sil [ossa] @address_closure_struct1 : $@convention(thin) (@inout_aliasable S, @owned S) -> () {
bb0(%0 : $*S, %1 : @owned $S):
%4 = struct_element_addr %0 : $*S, #S.c
%5 = load [copy] %4 : $*Optional<C>
store %1 to [assign] %0 : $*S
destroy_value %5
%8 = tuple ()
return %8 : $()
}
sil [ossa] @address_closure_struct2 : $@convention(thin) (@inout_aliasable S, @owned S) -> () {
bb0(%0 : $*S, %1 : @owned $S):
%4 = struct_element_addr %0 : $*S, #S.c
%5 = load [copy] %4 : $*Optional<C>
store %1 to [assign] %0 : $*S
destroy_value %5
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller_struct
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: apply [[SPECIALIZED_FN1]]
// CHECK: [[SPECIALIZED_FN2:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
// CHECK: apply [[SPECIALIZED_FN2]]
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_struct : $@convention(thin) (@owned S, @guaranteed S) -> @owned S {
bb0(%0 : @owned $S, %1 : @guaranteed $S):
%2 = copy_value %0
%3 = copy_value %1
%4 = alloc_stack $S, var, name "xx"
store %0 to [init] %4 : $*S
%7 = function_ref @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%8 = function_ref @address_closure_struct1 : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
%9 = partial_apply %8(%4, %3) : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
%9b = convert_escape_to_noescape %9 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%12 = apply %7(%9b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%13 = function_ref @address_closure_struct2 : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
%14 = partial_apply %13(%4, %2) : $@convention(thin) (@inout_aliasable S, @owned S) -> ()
%14b = convert_escape_to_noescape %14 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%16 = apply %7(%14b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%17 = load [take] %4 : $*S
destroy_value %9
destroy_value %14
dealloc_stack %4 : $*S
return %17 : $S
}
sil shared [ossa] @address_closure_class1 : $@convention(thin) (@inout_aliasable C, @owned C) -> () {
bb0(%0 : $*C, %1 : @owned $C):
%4 = load [copy] %0 : $*C
store %1 to [assign] %0 : $*C
destroy_value %4
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil [ossa] @address_caller_class1
// CHECK-NOT: partial_apply
// CHECK: [[SPECIALIZED_FN2:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
// CHECK: apply [[SPECIALIZED_FN2]]{{.*}}
// CHECK: [[SPECIALIZED_FN1:%.*]] = function_ref @{{.*}}address_closure_noescape_user{{.*}} : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
// CHECK: apply [[SPECIALIZED_FN1]]{{.*}}
// CHECK-NOT: partial_apply
// CHECK: return
sil [ossa] @address_caller_class1 : $@convention(thin) (@guaranteed C, @guaranteed C) -> @owned C {
bb0(%0 : @guaranteed $C, %1 : @guaranteed $C):
%2 = copy_value %0
%3 = copy_value %1
%4 = alloc_stack $C, var, name "xx"
store %2 to [init] %4 : $*C
%7 = function_ref @address_closure_class1 : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
%8 = partial_apply %7(%4, %3) : $@convention(thin) (@inout_aliasable C, @owned C) -> ()
%8b = convert_escape_to_noescape %8 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%10 = function_ref @address_closure_noescape_user : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%14 = apply %10(%8b) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%8c = convert_escape_to_noescape %8 : $@callee_owned () -> () to $@noescape @callee_owned () -> ()
%16 = apply %10(%8c) : $@convention(thin) (@owned @noescape @callee_owned () -> ()) -> ()
%17 = load [copy] %4 : $*C
%20 = load [take] %4 : $*C
destroy_value %8
destroy_value %20
dealloc_stack %4 : $*C
return %17 : $C
}
/////////////////////////////////////
// Thin To Thick and Partial Apply //
/////////////////////////////////////
//
// Make sure that we handle these correctly with and without removal of the
// closure and @owned and @guaranteed.
//
// CHECK-LABEL: sil [ossa] @large_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
sil [ossa] @large_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @unowned $Builtin.NativeObject, %1 : $Builtin.Int32, %2 : @owned $Builtin.NativeObject, %3 : @guaranteed $Builtin.NativeObject, %4 : @unowned $Builtin.NativeObject, %5 : $Builtin.Int32, %6 : @owned $Builtin.NativeObject, %7 : @guaranteed $Builtin.NativeObject):
%9999 = tuple ()
destroy_value %2
destroy_value %6
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @small_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
sil [ossa] @small_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @unowned $Builtin.NativeObject, %1 : $Builtin.Int32, %2 : @owned $Builtin.NativeObject, %3 : @guaranteed $Builtin.NativeObject):
%9999 = tuple ()
destroy_value %2
return %9999 : $()
}
// CHECK-LABEL: sil shared [ossa] @$s18owned_apply_callee014large_closure_C0BoBi32_BoBoTf1cnnnn_n : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () {
// CHECK: bb0
// CHECK: [[FUN:%.*]] = function_ref @large_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
// CHECK: [[CLOSURE:%.*]] = partial_apply [[FUN]](
// CHECK: [[C:%.*]] = copy_value [[CLOSURE]]
// CHECK: apply [[C]](
// CHECK-LABEL: sil shared [ossa] @$s18owned_apply_callee014small_closure_C0Tf1cnnnn_n : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
// CHECK: bb0
// CHECK: [[FUN:%.*]] = function_ref @small_closure_callee
// CHECK: [[CLOSURE:%.*]] = thin_to_thick_function [[FUN]] : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () to $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
// CHECK: apply [[CLOSURE]](
// CHECK-LABEL: sil [ossa] @owned_apply_callee : $@convention(thin) (@owned @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
sil [ossa] @owned_apply_callee : $@convention(thin) (@owned @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @owned $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), %1 : @unowned $Builtin.NativeObject, %2 : $Builtin.Int32, %3 : @owned $Builtin.NativeObject, %4 : @guaranteed $Builtin.NativeObject):
%5 = copy_value %3
apply %0(%1, %2, %5, %4) : $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
destroy_value %3
%9999 = tuple ()
return %9999 : $()
}
// CHECK-LABEL: sil shared [ossa] @$s23guaranteed_apply_callee014large_closure_C0BoBi32_BoBoTf1cnnnn_n : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @owned Builtin.NativeObject) -> () {
// CHECK: bb0(%0 : @unowned $Builtin.NativeObject, %1 : $Builtin.Int32, %2 : @owned $Builtin.NativeObject, %3 : @guaranteed $Builtin.NativeObject, %4 : @owned $Builtin.NativeObject, %5 : $Builtin.Int32, %6 : @owned $Builtin.NativeObject, %7 : @owned $Builtin.NativeObject):
// CHECK: [[FUN:%.*]] = function_ref @large_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
// CHECK: [[CLOSURE:%.*]] = partial_apply [[FUN]](
// CHECK: [[C:%.*]] = copy_value [[CLOSURE]]
// CHECK: apply [[C]](
// CHECK: destroy_value [[CLOSURE]]
// CHECK-LABEL: sil shared [ossa] @$s23guaranteed_apply_callee014small_closure_C0Tf1cnnnn_n : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
// CHECK: bb0(%0 : @unowned $Builtin.NativeObject, %1 : $Builtin.Int32, %2 : @owned $Builtin.NativeObject, %3 : @guaranteed $Builtin.NativeObject):
// CHECK: [[FUN:%.*]] = function_ref @small_closure_callee
// CHECK: [[CLOSURE:%.*]] = thin_to_thick_function [[FUN]] :
// CHECK: [[C:%.*]] = copy_value [[CLOSURE]]
// CHECK: apply [[C]](
// CHECK-LABEL: sil [ossa] @guaranteed_apply_callee : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
sil [ossa] @guaranteed_apply_callee : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @guaranteed $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), %1 : @unowned $Builtin.NativeObject, %2 : $Builtin.Int32, %3 : @owned $Builtin.NativeObject, %4 : @guaranteed $Builtin.NativeObject):
%5 = copy_value %3
%6 = copy_value %0
apply %6(%1, %2, %5, %4) : $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
destroy_value %3
%9999 = tuple ()
return %9999 : $()
}
sil [ossa] @guaranteed_apply_callee_throw : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Error) -> @error Error {
bb0(%0 : @guaranteed $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), %1 : @unowned $Builtin.NativeObject, %2 : $Builtin.Int32, %3 : @owned $Builtin.NativeObject, %4 : @guaranteed $Builtin.NativeObject, %5: @owned $Error):
%6 = copy_value %3
%7 = copy_value %0
apply %7(%1, %2, %6, %4) : $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
destroy_value %3
throw %5 : $Error
}
// CHECK-LABEL: sil [ossa] @thin_thick_and_partial_apply_test : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned any Error) -> () {
// CHECK: bb0([[ARG0:%.*]] : @unowned $Builtin.NativeObject, [[ARG1:%.*]] : $Builtin.Int32, [[ARG2:%.*]] : @owned $Builtin.NativeObject, [[ARG3:%.*]] : @guaranteed $Builtin.NativeObject, [[ARG4:%.*]] : @owned $any Error):
// CHECK: [[OLD_CLOSURE_CALLEE1:%.*]] = function_ref @large_closure_callee
// CHECK: [[OLD_CLOSURE_CALLEE2:%.*]] = function_ref @small_closure_callee
// CHECK: [[C0:%.*]] = copy_value [[ARG0]]
// CHECK-NEXT: [[C2:%.*]] = copy_value [[ARG2]]
// CHECK-NEXT: [[C3:%.*]] = copy_value [[ARG3]]
// CHECK: [[D0:%.*]] = copy_value [[C0]]
// CHECK-NEXT: [[D2:%.*]] = copy_value [[C2]]
// CHECK-NEXT: [[D3:%.*]] = copy_value [[C3]]
// CHECK: [[E0:%.*]] = copy_value [[C0]]
// CHECK-NEXT: [[E2:%.*]] = copy_value [[C2]]
// CHECK-NEXT: [[E3:%.*]] = copy_value [[C3]]
// CHECK-NOT: partial_apply [[OLD_CLOSURE_CALLEE1]]
// CHECK-NOT: thin_to_thick_function [[OLD_CLOSURE_CALLEE2]]
// CHECK: [[SPECFUN1:%.*]] = function_ref @$s18owned_apply_callee014large_closure_C0BoBi32_BoBoTf1cnnnn_n
// CHECK: apply [[SPECFUN1]](
// CHECK: [[SPECFUN3:%.*]] = function_ref @$s18owned_apply_callee014small_closure_C0Tf1cnnnn_n
// CHECK: apply [[SPECFUN3]](
// CHECK: [[SPECFUN0:%.*]] = function_ref @$s23guaranteed_apply_callee014large_closure_C0BoBi32_BoBoTf1cnnnn_n
// CHECK: apply [[SPECFUN0]](
// CHECK: [[SPECFUN2:%.*]] = function_ref @$s23guaranteed_apply_callee014small_closure_C0Tf1cnnnn_n
// CHECK: apply [[SPECFUN2]](
// CHECK-NEXT: destroy_value [[C0]]
// CHECK-NEXT: destroy_value [[C2]]
// CHECK-NEXT: destroy_value [[C3]]
// CHECK: [[SPECFUN4:%.*]] = function_ref @$s29guaranteed_apply_callee_throw014small_closure_C0Tf1cnnnnn_n
// CHECK-NEXT: try_apply [[SPECFUN4]](
sil [ossa] @thin_thick_and_partial_apply_test : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Error) -> () {
bb0(%0 : @unowned $Builtin.NativeObject, %1 : $Builtin.Int32, %2 : @owned $Builtin.NativeObject, %3 : @guaranteed $Builtin.NativeObject, %11: @owned $Error):
%4 = function_ref @owned_apply_callee : $@convention(thin) (@owned @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%5 = function_ref @guaranteed_apply_callee : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%6 = function_ref @large_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%7 = function_ref @small_closure_callee : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%10 = function_ref @guaranteed_apply_callee_throw : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Error) -> @error Error
%0c = copy_value %0
%2c = copy_value %2
%3c = copy_value %3
%8 = partial_apply %6(%0c, %1, %2c, %3c) : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%9 = thin_to_thick_function %7 : $@convention(thin) (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> () to $@callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%2d = copy_value %2
%8c = copy_value %8
apply %4(%8c, %0, %1, %2d, %3) : $@convention(thin) (@owned @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%2e = copy_value %2
apply %4(%9, %0, %1, %2e, %3) : $@convention(thin) (@owned @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%2f = copy_value %2
apply %5(%8, %0, %1, %2f, %3) : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
%2g = copy_value %2
apply %5(%9, %0, %1, %2g, %3) : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> ()
destroy_value %8
try_apply %10(%9, %0, %1, %2, %3, %11) : $@convention(thin) (@guaranteed @callee_owned (Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject) -> (), Builtin.NativeObject, Builtin.Int32, @owned Builtin.NativeObject, @guaranteed Builtin.NativeObject, @owned Error) -> @error Error, normal bb2, error bb3
bb2(%n : $()):
br bb4
bb3(%e : $Error):
br bb4
bb4:
%9999 = tuple()
return %9999 : $()
}
//////////////////////////////
// Non Function Ref Closure //
//////////////////////////////
//
// Make sure we do not try to specialize a closure if we are not closing over a
// direct function ref.
// CHECK-LABEL: @$s4test3barSiAA1P_p_SitF : $@convention(thin) (@in any P, Int32) -> Int32 {
// CHECK: partial_apply
// CHECK: apply
sil [noinline] [ossa] @$s4test3barSiAA1P_p_SitF : $@convention(thin) (@in P, Int32) -> Int32 {
bb0(%0 : $*P, %1 : $Int32):
%2 = open_existential_addr mutable_access %0 : $*P to $*@opened("01234567-89ab-cdef-0123-000000000000", P) Self
%3 = witness_method $@opened("01234567-89ab-cdef-0123-000000000000", P) Self, #P.foo, %2 : $*@opened("01234567-89ab-cdef-0123-000000000000", P) Self : $@convention(witness_method: P) @callee_owned <T: P> (@callee_owned (Int32) -> Int32, Int32, @inout T) -> Int32
%4 = integer_literal $Builtin.Int32, 2
%5 = struct $Int32 (%4 : $Builtin.Int32)
// function_ref test.baz (Swift.Int32)(m : Swift.Int32) -> Swift.Int32
%6 = function_ref @$s4test3bazSiSi1m_tcSiF : $@convention(thin) (Int32, Int32) -> Int32
%7 = partial_apply %6(%5) : $@convention(thin) (Int32, Int32) -> Int32
%8 = apply %3<@opened("01234567-89ab-cdef-0123-000000000000", P) Self>(%7, %1, %2) : $@convention(witness_method: P) @callee_owned <T: P> (@callee_owned (Int32) -> Int32, Int32, @inout T) -> Int32
destroy_value %7
destroy_addr %0 : $*P
return %8 : $Int32
}
sil [ossa] @$s4test3bazSiSi1m_tcSiF : $@convention(thin) (Int32, Int32) -> Int32
//////////////////////////////////////////////////////////////////////////////////
// Make sure that we properly set a specialized closure's indirect return type. //
//////////////////////////////////////////////////////////////////////////////////
//
// SIL verification should catch the incorrect type.
// rdar:://19321284
// CHECK-LABEL: sil [serialized] [ossa] @callee : $@convention(thin) (Builtin.Int32) -> () {
sil [serialized] [ossa] @callee : $@convention(thin) (Builtin.Int32) -> () {
bb0(%0 : $Builtin.Int32):
unreachable
}
sil shared [serialized] [ossa] @thunk : $@convention(thin) (@owned @callee_owned () -> ()) -> @out () {
bb0(%0 : $*(), %1 : @owned $@callee_owned () -> ()):
apply %1() : $@callee_owned () -> ()
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: @test_closure_propagation : $@convention(thin) () -> () {
// CHECK-NOT: partial_apply
sil [serialized] [ossa] @test_closure_propagation : $@convention(thin) () -> () {
bb0:
%f1 = function_ref @callee : $@convention(thin) (Builtin.Int32) -> ()
%i1 = integer_literal $Builtin.Int32, 24
%p1 = partial_apply %f1(%i1) : $@convention(thin) (Builtin.Int32) -> ()
%f2 = function_ref @thunk : $@convention(thin) (@owned @callee_owned () -> ()) -> @out ()
%s1 = alloc_stack $()
%a1 = apply %f2(%s1, %p1) : $@convention(thin) (@owned @callee_owned () -> ()) -> @out ()
dealloc_stack %s1 : $*()
unreachable
}