blob: 53373be236cdb5c7c8fd3a7f813ef87975de407c [file] [log] [blame]
// RUN: %empty-directory(%t)
// RUN: %target-sil-opt -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 -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 @testSwitchEnumArg
// CHECK-NOT: apply
// CHECK: builtin "assert_configuration"()
// CHECK-NOT: apply
// CHECK: return %{{.*}} : $()
// CHECK-LOG-LABEL: Inline into caller: testSwitchEnumArg
// CHECK-LOG-NEXT: decision {{.*}}, b=50,
sil @testSwitchEnumArg : $@convention(thin) (Builtin.Int32) -> () {
bb0(%1 : $Builtin.Int32):
%0 = function_ref @switchEnumCallee : $@convention(thin) (Optional<Int32>) -> Int32
%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 : $()
}
// CHECK-LABEL: sil @testSwitchEnumConst
// CHECK-NOT: apply
// CHECK: builtin "assert_configuration"()
// CHECK-NOT: apply
// CHECK: return %{{.*}} : $()
// CHECK-LOG-LABEL: Inline into caller: testSwitchEnumConst
// CHECK-LOG-NEXT: pure-call decision
sil @testSwitchEnumConst : $@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 @$s39checkSpecializationAfterGenericInlinings5Int64Vq_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'
// Tests of exclusivity inlining.
// CHECK-LABEL: sil @testExclusivity : $@convention(thin) () -> () {
// CHECK-NOT: apply
// CHECK: begin_access
// CHECK: return
// CHECK: end sil function 'testExclusivity'
struct X {
@_hasStorage var i: Int64 { get set }
init(i: Int64)
init()
}
var globalX: X
sil_global hidden @globalX : $X
sil @exclusivityCallee : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%4 = tuple ()
return %4 : $()
}
sil @testExclusivity : $@convention(thin) () -> () {
bb0:
%0 = function_ref @exclusivityCallee : $@convention(thin) () -> ()
%1 = apply %0() : $@convention(thin) () -> ()
%2 = tuple ()
return %2 : $()
}
// TODO:
// Check that the inlining heuristic takes into account the possibility
// of performing a devirtualization after inlining.