| // 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 @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' |
| |
| // TODO: |
| // Check that the inlining heuristic takes into account the possibility |
| // of performing a devirtualization after inlining. |