// 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 %1 = integer_literal $Builtin.Int32, 27 %2 = struct $Int32 (%1 : $Builtin.Int32) %3 = enum $Optional, #Optional.some!enumelt.1, %2 : $Int32 %4 = apply %0(%3) : $@convention(thin) (Optional) -> Int32 %5 = tuple () return %5 : $() } sil @switchEnumCallee : $@convention(thin) (Optional) -> Int32 { bb0(%0 : $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1: %2 = unchecked_enum_data %0 : $Optional, #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) (@in T) -> () { bb0(%0 : $*T): destroy_addr %0 : $*T %3 = tuple () return %3 : $() } // end sil function 'action' // checkSpecializationAfterGenericInlining (A, B) -> () sil hidden @checkSpecializationAfterGenericInlining : $@convention(thin) (@in T, @in U) -> () { bb0(%0 : $*T, %1 : $*U): // function_ref action (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(%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) (@in U) -> () { bb0(%0 : $*U): // function_ref checkSpecializationAfterGenericInlining (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(%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.