mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
- Fix block to func reabstraction thunks block argument handling - Forward cast ownership - Fix applyPartiallyAppliedSuperMethod ownership for @callee_guaranteed closures - Avoid a copy in buildBlockToFuncThunkBody - Update tests for callee_guaranteed closures SR-5441 rdar://33255593
416 lines
14 KiB
Plaintext
416 lines
14 KiB
Plaintext
// RUN: %empty-directory(%t)
|
|
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -inline -sil-inline-generics=true -sil-partial-specialization=false -debug-only=sil-inliner 2>%t/log | %FileCheck %s
|
|
// RUN: %FileCheck %s --check-prefix=CHECK-LOG <%t/log
|
|
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -inline -sil-inline-generics=true -sil-partial-specialization=true -generic-specializer -debug-only=sil-inliner 2>%t/log | %FileCheck %s --check-prefix=CHECK-PARTIAL-SPECIALIZATION
|
|
// REQUIRES: asserts
|
|
|
|
// This test checks the inline heuristics based on the debug log output of
|
|
// the performance inliner.
|
|
// Checking the final sil is just a goodie.
|
|
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
struct Cont {
|
|
var cl: (Int32) -> Int32
|
|
}
|
|
|
|
struct Cont2 {
|
|
var tp: (Int32, (Int32) -> Int32)
|
|
}
|
|
|
|
enum E {
|
|
case A
|
|
case B((Int32) -> Int32)
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @testDirectClosure
|
|
// CHECK: [[C:%[0-9]+]] = thin_to_thick_function
|
|
// CHECK: apply [[C]](
|
|
// CHECK: return
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testDirectClosure
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=70,
|
|
|
|
sil @testDirectClosure : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%0 = function_ref @takeDirectClosure : $@convention(thin) (@owned @callee_guaranteed (Int32) -> Int32) -> Int32
|
|
%1 = function_ref @closure : $@convention(thin) (Int32) -> Int32
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int32) -> Int32 to $@callee_guaranteed (Int32) -> Int32
|
|
%3 = apply %0(%2) : $@convention(thin) (@owned @callee_guaranteed (Int32) -> Int32) -> Int32
|
|
return %3 : $Int32
|
|
}
|
|
|
|
sil @takeDirectClosure : $@convention(thin) (@owned @callee_guaranteed (Int32) -> Int32) -> Int32 {
|
|
bb0(%0 : $@callee_guaranteed (Int32) -> Int32):
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = struct $Int32 (%1 : $Builtin.Int32)
|
|
%3 = apply %0(%2) : $@callee_guaranteed (Int32) -> Int32
|
|
return %3 : $Int32
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @testStructClosure
|
|
// CHECK: [[C:%[0-9]+]] = struct_extract
|
|
// CHECK: apply [[C]](
|
|
// CHECK: return
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testStructClosure
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=70,
|
|
|
|
sil @testStructClosure : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%0 = function_ref @takeStructClosure : $@convention(thin) (@owned Cont) -> Int32
|
|
%1 = function_ref @closure : $@convention(thin) (Int32) -> Int32
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int32) -> Int32 to $@callee_guaranteed (Int32) -> Int32
|
|
%3 = struct $Cont (%2 : $@callee_guaranteed (Int32) -> Int32)
|
|
%4 = apply %0(%3) : $@convention(thin) (@owned Cont) -> Int32
|
|
return %4 : $Int32
|
|
}
|
|
|
|
sil @takeStructClosure : $@convention(thin) (@owned Cont) -> Int32 {
|
|
bb0(%0 : $Cont):
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%1 = struct_extract %0 : $Cont, #Cont.cl
|
|
%2 = integer_literal $Builtin.Int32, 27
|
|
%3 = struct $Int32 (%2 : $Builtin.Int32)
|
|
%4 = apply %1(%3) : $@callee_guaranteed (Int32) -> Int32
|
|
return %4 : $Int32
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @testStructAddrClosure
|
|
// CHECK: [[C:%[0-9]+]] = load
|
|
// CHECK: apply [[C]](
|
|
// CHECK: return
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testStructAddrClosure
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=70,
|
|
|
|
sil @testStructAddrClosure : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%0 = alloc_stack $Cont
|
|
%1 = function_ref @closure : $@convention(thin) (Int32) -> Int32
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int32) -> Int32 to $@callee_guaranteed (Int32) -> Int32
|
|
%3 = struct $Cont (%2 : $@callee_guaranteed (Int32) -> Int32)
|
|
store %3 to %0 : $*Cont
|
|
%5 = function_ref @takeStructAddrClosure : $@convention(thin) (@inout Cont) -> Int32
|
|
%6 = apply %5(%0) : $@convention(thin) (@inout Cont) -> Int32
|
|
%7 = struct_element_addr %0 : $*Cont, #Cont.cl
|
|
%8 = load %7 : $*@callee_guaranteed (Int32) -> Int32
|
|
strong_release %8 : $@callee_guaranteed (Int32) -> Int32
|
|
dealloc_stack %0 : $*Cont
|
|
return %6 : $Int32
|
|
}
|
|
|
|
sil @takeStructAddrClosure : $@convention(thin) (@inout Cont) -> Int32 {
|
|
bb0(%0 : $*Cont):
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%1 = struct_element_addr %0 : $*Cont, #Cont.cl
|
|
%2 = load %1 : $*@callee_guaranteed (Int32) -> Int32
|
|
%3 = integer_literal $Builtin.Int32, 27
|
|
%4 = struct $Int32 (%3 : $Builtin.Int32)
|
|
strong_retain %2 : $@callee_guaranteed (Int32) -> Int32
|
|
%6 = apply %2(%4) : $@callee_guaranteed (Int32) -> Int32
|
|
return %6 : $Int32
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @testTupleClosure
|
|
// CHECK: [[C:%[0-9]+]] = tuple_extract
|
|
// CHECK: apply [[C]](
|
|
// CHECK: return
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testTupleClosure
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=70,
|
|
|
|
sil @testTupleClosure : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%0 = function_ref @takeTupleClosure : $@convention(thin) (@owned Cont2) -> Int32
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = struct $Int32 (%1 : $Builtin.Int32)
|
|
%3 = function_ref @closure : $@convention(thin) (Int32) -> Int32
|
|
%4 = thin_to_thick_function %3 : $@convention(thin) (Int32) -> Int32 to $@callee_guaranteed (Int32) -> Int32
|
|
%5 = tuple (%2 : $Int32, %4 : $@callee_guaranteed (Int32) -> Int32)
|
|
%6 = struct $Cont2 (%5 : $(Int32, @callee_guaranteed (Int32) -> Int32))
|
|
%7 = apply %0(%6) : $@convention(thin) (@owned Cont2) -> Int32
|
|
return %7 : $Int32
|
|
}
|
|
|
|
|
|
sil @takeTupleClosure : $@convention(thin) (@owned Cont2) -> Int32 {
|
|
bb0(%0 : $Cont2):
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%1 = struct_extract %0 : $Cont2, #Cont2.tp
|
|
%2 = tuple_extract %1 : $(Int32, @callee_guaranteed (Int32) -> Int32), 1
|
|
%3 = integer_literal $Builtin.Int32, 27
|
|
%4 = struct $Int32 (%3 : $Builtin.Int32)
|
|
%5 = apply %2(%4) : $@callee_guaranteed (Int32) -> Int32
|
|
return %5 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @testEnumClosure
|
|
// CHECK: [[C:%[0-9]+]] = unchecked_enum_data
|
|
// CHECK: apply [[C]](
|
|
// CHECK: return
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testEnumClosure
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=70,
|
|
|
|
sil @testEnumClosure : $@convention(thin) () -> Int32 {
|
|
bb0:
|
|
%0 = function_ref @takeEnumClosure : $@convention(thin) (@owned E) -> Int32
|
|
%1 = function_ref @closure : $@convention(thin) (Int32) -> Int32
|
|
%2 = thin_to_thick_function %1 : $@convention(thin) (Int32) -> Int32 to $@callee_guaranteed (Int32) -> Int32
|
|
%3 = enum $E, #E.B!enumelt.1, %2 : $@callee_guaranteed (Int32) -> Int32
|
|
%4 = apply %0(%3) : $@convention(thin) (@owned E) -> Int32
|
|
return %4 : $Int32
|
|
}
|
|
|
|
|
|
sil @takeEnumClosure : $@convention(thin) (@owned E) -> Int32 {
|
|
bb0(%0 : $E):
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%1 = unchecked_enum_data %0 : $E, #E.B!enumelt.1
|
|
%2 = integer_literal $Builtin.Int32, 27
|
|
%3 = struct $Int32 (%2 : $Builtin.Int32)
|
|
%4 = apply %1(%3) : $@callee_guaranteed (Int32) -> Int32
|
|
return %4 : $Int32
|
|
}
|
|
|
|
// The closure which is used by all tests above.
|
|
|
|
sil shared @closure : $@convention(thin) (Int32) -> Int32 {
|
|
bb0(%0 : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 1
|
|
%2 = struct_extract %0 : $Int32, #Int32._value
|
|
%3 = integer_literal $Builtin.Int1, -1
|
|
%4 = builtin "sadd_with_overflow_Int32"(%2 : $Builtin.Int32, %1 : $Builtin.Int32, %3 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
|
|
%5 = tuple_extract %4 : $(Builtin.Int32, Builtin.Int1), 0
|
|
%6 = tuple_extract %4 : $(Builtin.Int32, Builtin.Int1), 1
|
|
cond_fail %6 : $Builtin.Int1
|
|
%8 = struct $Int32 (%5 : $Builtin.Int32)
|
|
return %8 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @testCondBr
|
|
// CHECK-NOT: apply
|
|
// CHECK: builtin "assert_configuration"()
|
|
// CHECK-NOT: apply
|
|
// CHECK: return %{{.*}} : $()
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testCondBr
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=50,
|
|
|
|
sil @testCondBr : $@convention(thin) (Int32) -> () {
|
|
bb0(%a : $Int32):
|
|
%0 = function_ref @condBrCallee : $@convention(thin) (Int32, Int32) -> Int32
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = struct $Int32 (%1 : $Builtin.Int32)
|
|
%3 = apply %0(%2, %a) : $@convention(thin) (Int32, Int32) -> Int32
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
}
|
|
|
|
sil @condBrCallee : $@convention(thin) (Int32, Int32) -> Int32 {
|
|
bb0(%0 : $Int32, %r : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = struct_extract %0 : $Int32, #Int32._value
|
|
%3 = builtin "cmp_eq_Word"(%2 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1
|
|
cond_br %3, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%r : $Int32)
|
|
|
|
bb2:
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%6 = struct $Int32 (%1 : $Builtin.Int32)
|
|
br bb3(%6 : $Int32)
|
|
|
|
bb3(%8 : $Int32):
|
|
return %8 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @testSwitchValue
|
|
// CHECK-NOT: apply
|
|
// CHECK: builtin "assert_configuration"()
|
|
// CHECK-NOT: apply
|
|
// CHECK: return %{{.*}} : $()
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testSwitchValue
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=40,
|
|
|
|
sil @testSwitchValue : $@convention(thin) (Int32) -> () {
|
|
bb0(%a : $Int32):
|
|
|
|
%0 = function_ref @switchValueCallee : $@convention(thin) (Int32, Int32) -> Int32
|
|
%1 = integer_literal $Builtin.Int32, 28
|
|
%2 = struct $Int32 (%1 : $Builtin.Int32)
|
|
%3 = apply %0(%2, %a) : $@convention(thin) (Int32, Int32) -> Int32
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
}
|
|
|
|
sil @switchValueCallee : $@convention(thin) (Int32, Int32) -> Int32 {
|
|
bb0(%0 : $Int32, %r : $Int32):
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = integer_literal $Builtin.Int32, 28
|
|
%3 = struct_extract %r : $Int32, #Int32._value
|
|
switch_value %3 : $Builtin.Int32, case %1 : bb1, case %2 : bb2, default bb3
|
|
|
|
bb1:
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
br bb4(%1 : $Builtin.Int32)
|
|
|
|
bb2:
|
|
br bb4(%2 : $Builtin.Int32)
|
|
|
|
bb3:
|
|
// increase the scope length
|
|
%c3 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c4 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
br bb4(%1 : $Builtin.Int32)
|
|
|
|
bb4(%8 : $Builtin.Int32):
|
|
%9 = struct $Int32 (%8 : $Builtin.Int32)
|
|
return %9 : $Int32
|
|
}
|
|
|
|
// CHECK-LABEL: sil @testSwitchEnum
|
|
// CHECK-NOT: apply
|
|
// CHECK: builtin "assert_configuration"()
|
|
// CHECK-NOT: apply
|
|
// CHECK: return %{{.*}} : $()
|
|
|
|
// CHECK-LOG-LABEL: Inline into caller: testSwitchEnum
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=50,
|
|
|
|
sil @testSwitchEnum : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = function_ref @switchEnumCallee : $@convention(thin) (Optional<Int32>) -> Int32
|
|
%1 = integer_literal $Builtin.Int32, 27
|
|
%2 = struct $Int32 (%1 : $Builtin.Int32)
|
|
%3 = enum $Optional<Int32>, #Optional.some!enumelt.1, %2 : $Int32
|
|
%4 = apply %0(%3) : $@convention(thin) (Optional<Int32>) -> Int32
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
sil @switchEnumCallee : $@convention(thin) (Optional<Int32>) -> Int32 {
|
|
bb0(%0 : $Optional<Int32>):
|
|
switch_enum %0 : $Optional<Int32>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
|
|
|
|
bb1:
|
|
%2 = unchecked_enum_data %0 : $Optional<Int32>, #Optional.some!enumelt.1
|
|
br bb3(%2 : $Int32)
|
|
|
|
bb2:
|
|
// increase the scope length
|
|
%c1 = builtin "assert_configuration"() : $Builtin.Int32
|
|
%c2 = builtin "assert_configuration"() : $Builtin.Int32
|
|
|
|
%4 = integer_literal $Builtin.Int32, 0
|
|
%5 = struct $Int32 (%4 : $Builtin.Int32)
|
|
br bb3(%5 : $Int32)
|
|
|
|
bb3(%7 : $Int32):
|
|
return %7 : $Int32
|
|
}
|
|
|
|
// Tests of the generics inlining.
|
|
|
|
// CHECK-LABEL: sil @testSpecializationAfterGenericInlining
|
|
// CHECK-NOT: apply
|
|
// CHECK: function_ref @action
|
|
// CHECK: apply
|
|
// CHECK-NOT: apply
|
|
// CHECK: return
|
|
// CHECK: end sil function 'testSpecializationAfterGenericInlining'
|
|
|
|
// CHECK-PARTIAL-SPECIALIZATION-LABEL: sil @testSpecializationAfterGenericInlining
|
|
// CHECK-PARTIAL-SPECIALIZATION-NOT: apply
|
|
// Reference to the partial specialization of checkSpecializationAfterGenericInlining
|
|
// CHECK-PARTIAL-SPECIALIZATION: function_ref @_T039checkSpecializationAfterGenericInlinings5Int64Vq_ACRszr0_lIetyi_Tp5
|
|
// CHECK-PARTIAL-SPECIALIZATION: apply
|
|
// CHECK-PARTIAL-SPECIALIZATION-NOT: apply
|
|
// CHECK-PARTIAL-SPECIALIZATION: return
|
|
// CHECK-PARTIAL-SPECIALIZATION: end sil function 'testSpecializationAfterGenericInlining'
|
|
|
|
|
|
// Check that the inlining heuristic takes into account the possibility
|
|
// of performing a generic specialization after inlining.
|
|
// CHECK-LOG-LABEL: Inline into caller: testSpecializationAfterGenericInlining
|
|
// CHECK-LOG-NEXT: decision {{.*}}, b=320, {{.*}} checkSpecializationAfterGenericInlining
|
|
|
|
sil hidden [noinline] @action : $@convention(thin) <T> (@in T) -> () {
|
|
bb0(%0 : $*T):
|
|
destroy_addr %0 : $*T
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
} // end sil function 'action'
|
|
|
|
// checkSpecializationAfterGenericInlining<A, B> (A, B) -> ()
|
|
sil hidden @checkSpecializationAfterGenericInlining : $@convention(thin) <T, U> (@in T, @in U) -> () {
|
|
bb0(%0 : $*T, %1 : $*U):
|
|
// function_ref action<A> (A) -> ()
|
|
%4 = function_ref @action : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
%5 = alloc_stack $T
|
|
copy_addr %0 to [initialization] %5 : $*T
|
|
%7 = apply %4<T>(%5) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
|
|
dealloc_stack %5 : $*T
|
|
destroy_addr %1 : $*U
|
|
destroy_addr %0 : $*T
|
|
%11 = tuple ()
|
|
return %11 : $()
|
|
} // end sil function 'checkSpecializationAfterGenericInlining'
|
|
|
|
sil @testSpecializationAfterGenericInlining : $@convention(thin) <U> (@in U) -> () {
|
|
bb0(%0 : $*U):
|
|
// function_ref checkSpecializationAfterGenericInlining<A, B> (A, B) -> ()
|
|
%2 = function_ref @checkSpecializationAfterGenericInlining : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
|
|
%3 = integer_literal $Builtin.Int64, 1
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%5 = alloc_stack $Int64
|
|
store %4 to %5 : $*Int64
|
|
%7 = alloc_stack $U
|
|
copy_addr %0 to [initialization] %7 : $*U
|
|
%9 = apply %2<Int64, U>(%5, %7) : $@convention(thin) <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> ()
|
|
dealloc_stack %7 : $*U
|
|
dealloc_stack %5 : $*Int64
|
|
destroy_addr %0 : $*U
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
} // end sil function 'testSpecializationAfterGenericInlining'
|
|
|
|
// TODO:
|
|
// Check that the inlining heuristic takes into account the possibility
|
|
// of performing a devirtualization after inlining.
|