| // RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine -sil-combine-disable-alloc-stack-opts | %FileCheck %s |
| |
| import Swift |
| import Builtin |
| |
| ////////////////// |
| // Declarations // |
| ////////////////// |
| |
| sil @unknown : $@convention(thin) () -> () |
| sil @generic_callee_in : $@convention(thin) <T, U> (@in T, @in U) -> () |
| sil @generic_callee_inguaranteed : $@convention(thin) <T, U> (@in_guaranteed T, @in_guaranteed U) -> () |
| sil @callee : $@convention(thin) (Double, @in_guaranteed Int) -> () |
| sil @noreturn_func : $@convention(thin) () -> Never |
| |
| protocol Error {} |
| |
| protocol SwiftP { |
| func foo() |
| } |
| |
| class Klass {} |
| |
| protocol FakeProtocol { |
| func requirement() |
| } |
| |
| ///////////////////////////////// |
| // Tests for SILCombinerApply. // |
| ///////////////////////////////// |
| |
| // Make sure that we handle partial_apply captured arguments correctly. |
| // |
| // We use custom types here to make it easier to pattern match with FileCheck. |
| struct S1 { var x: Builtin.NativeObject } |
| struct S2 { var x: Builtin.NativeObject } |
| struct S3 { var x: Builtin.NativeObject } |
| struct S4 { var x: Builtin.NativeObject } |
| struct S5 { var x: Builtin.NativeObject } |
| struct S6 { var x: Builtin.NativeObject } |
| struct S7 { var x: Builtin.NativeObject } |
| struct S8 { var x: Builtin.NativeObject } |
| sil @sil_combine_partial_apply_callee : $@convention(thin) (@in S1, @in S2, @in_guaranteed S3, @in_guaranteed S4, @inout S5, S6, @owned S7, @guaranteed S8) -> () |
| |
| // *NOTE PLEASE READ*. If this test case looks funny to you, it is b/c partial |
| // apply is funny. Specifically, even though a partial apply has the conventions |
| // of the function on it, arguments to the partial apply (that will be passed |
| // off to the function) must /always/ be passed in at +1. This is because the |
| // partial apply is building up a boxed aggregate to send off to the closed over |
| // function. Of course when you call the function, the proper conventions will |
| // be used. |
| // |
| // CHECK-LABEL: sil @sil_combine_dead_partial_apply : $@convention(thin) (@in S2, @in S4, @inout S5, S6, @owned S7, @guaranteed S8) -> () { |
| // CHECK: bb0([[IN_ARG:%.*]] : $*S2, [[INGUARANTEED_ARG:%.*]] : $*S4, [[INOUT_ARG:%.*]] : $*S5, [[UNOWNED_ARG:%.*]] : $S6, [[OWNED_ARG:%.*]] : $S7, [[GUARANTEED_ARG:%.*]] : $S8): |
| // |
| // CHECK: function_ref unknown |
| // CHECK: [[UNKNOWN_FUNC:%.*]] = function_ref @unknown |
| // CHECK-NEXT: [[IN_ADDRESS:%.*]] = alloc_stack $S1 |
| // CHECK-NEXT: [[INGUARANTEED_ADDRESS:%.*]] = alloc_stack $S3 |
| // |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK: [[IN_ADDRESS_1:%.*]] = alloc_stack $S1 |
| // CHECK: copy_addr [take] [[IN_ADDRESS]] to [initialization] [[IN_ADDRESS_1]] |
| // CHECK: [[IN_ARG_1:%.*]] = alloc_stack $S2 |
| // CHECK: copy_addr [take] [[IN_ARG]] to [initialization] [[IN_ARG_1]] |
| // CHECK: [[INGUARANTEED_ADDRESS_1:%.*]] = alloc_stack $S3 |
| // CHECK: copy_addr [take] [[INGUARANTEED_ADDRESS]] to [initialization] [[INGUARANTEED_ADDRESS_1]] |
| // CHECK: [[INGUARANTEED_ARG_1:%.*]] = alloc_stack $S4 |
| // CHECK: copy_addr [take] [[INGUARANTEED_ARG]] to [initialization] [[INGUARANTEED_ARG_1]] |
| // |
| // Then make sure that the destroys are placed after the destroy_value of the |
| // partial_apply (which is after this apply)... |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // |
| // CHECK-NEXT: destroy_addr [[IN_ADDRESS_1]] |
| // CHECK-NEXT: destroy_addr [[IN_ARG_1]] |
| // CHECK-NEXT: destroy_addr [[INGUARANTEED_ADDRESS_1]] |
| // CHECK-NEXT: destroy_addr [[INGUARANTEED_ARG_1]] |
| // CHECK-NEXT: dealloc_stack [[INGUARANTEED_ARG_1]] |
| // CHECK-NEXT: dealloc_stack [[INGUARANTEED_ADDRESS_1]] |
| // CHECK-NEXT: dealloc_stack [[IN_ARG_1]] |
| // CHECK-NEXT: dealloc_stack [[IN_ADDRESS_1]] |
| // CHECK-NEXT: release_value [[UNOWNED_ARG]] |
| // CHECK-NEXT: release_value [[OWNED_ARG]] |
| // CHECK-NEXT: release_value [[GUARANTEED_ARG]] |
| // |
| // ... but before the function epilog. |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: dealloc_stack [[INGUARANTEED_ADDRESS]] |
| // CHECK-NEXT: dealloc_stack [[IN_ADDRESS]] |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| // CHECK-NEXT: } // end sil function 'sil_combine_dead_partial_apply' |
| sil @sil_combine_dead_partial_apply : $@convention(thin) (@in S2, @in S4, @inout S5, S6, @owned S7, @guaranteed S8) -> () { |
| bb0(%1 : $*S2, %2 : $*S4, %4 : $*S5, %5 : $S6, %6 : $S7, %7 : $S8): |
| %8 = function_ref @unknown : $@convention(thin) () -> () |
| %9 = function_ref @sil_combine_partial_apply_callee : $@convention(thin) (@in S1, @in S2, @in_guaranteed S3, @in_guaranteed S4, @inout S5, S6, @owned S7, @guaranteed S8) -> () |
| |
| // This is for the @in alloc_stack case. |
| %10 = alloc_stack $S1 |
| // This is for the @in_guaranteed alloc_stack case. |
| %11 = alloc_stack $S3 |
| |
| // Marker of space in between the alloc_stack and the partial_apply |
| apply %8() : $@convention(thin) () -> () |
| |
| // Now call the partial apply. We use the "unknown" function call after the |
| // partial apply to ensure that we are truly placing releases at the partial |
| // applies release rather than right afterwards. |
| %102 = partial_apply %9(%10, %1, %11, %2, %4, %5, %6, %7) : $@convention(thin) (@in S1, @in S2, @in_guaranteed S3, @in_guaranteed S4, @inout S5, S6, @owned S7, @guaranteed S8) -> () |
| |
| // Marker of space in between partial_apply and the release of %102. |
| apply %8() : $@convention(thin) () -> () |
| |
| strong_release %102 : $@callee_owned () -> () |
| |
| apply %8() : $@convention(thin) () -> () |
| |
| // Epilog. |
| |
| // Cleanup the stack locations. |
| dealloc_stack %11 : $*S3 |
| dealloc_stack %10 : $*S1 |
| |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| sil @sil_combine_partial_apply_callee_2 : $@convention(thin) (@in S1) -> () |
| |
| // CHECK-LABEL: sil @sil_combine_dead_partial_apply_non_overlapping_lifetime : $@convention(thin) () -> () { |
| // CHECK: bb0: |
| // CHECK-NEXT: function_ref unknown |
| // CHECK-NEXT: [[UNKNOWN_FUNC:%.*]] = function_ref @unknown : $@convention(thin) () -> () |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: br bb1 |
| // |
| // CHECK: bb1: |
| // CHECK: [[ORIGINAL_ALLOC_STACK:%.*]] = alloc_stack $S1 |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: [[NEW_ALLOC_STACK:%.*]] = alloc_stack $S1 |
| // CHECK-NEXT: copy_addr [take] [[ORIGINAL_ALLOC_STACK]] to [initialization] [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: apply |
| // CHECK-NEXT: cond_br undef, bb2, bb3 |
| // |
| // CHECK: bb2: |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: destroy_addr [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[ORIGINAL_ALLOC_STACK]] |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: br bb4 |
| // |
| // CHECK: bb3: |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: destroy_addr [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[ORIGINAL_ALLOC_STACK]] |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: br bb4 |
| // |
| // CHECK: bb4: |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: apply [[UNKNOWN_FUNC]]() |
| // CHECK: } // end sil function 'sil_combine_dead_partial_apply_non_overlapping_lifetime' |
| sil @sil_combine_dead_partial_apply_non_overlapping_lifetime : $@convention(thin) () -> () { |
| bb0: |
| %3 = function_ref @unknown : $@convention(thin) () -> () |
| apply %3() : $@convention(thin) () -> () |
| br bb1 |
| |
| bb1: |
| %0 = alloc_stack $S1 |
| apply %3() : $@convention(thin) () -> () |
| %1 = function_ref @sil_combine_partial_apply_callee_2 : $@convention(thin) (@in S1) -> () |
| apply %3() : $@convention(thin) () -> () |
| %2 = partial_apply %1(%0) : $@convention(thin) (@in S1) -> () |
| apply %3() : $@convention(thin) () -> () |
| dealloc_stack %0 : $*S1 |
| apply %3() : $@convention(thin) () -> () |
| cond_br undef, bb2, bb3 |
| |
| bb2: |
| apply %3() : $@convention(thin) () -> () |
| strong_release %2 : $@callee_owned () -> () |
| apply %3() : $@convention(thin) () -> () |
| br bb4 |
| |
| bb3: |
| apply %3() : $@convention(thin) () -> () |
| strong_release %2 : $@callee_owned () -> () |
| apply %3() : $@convention(thin) () -> () |
| br bb4 |
| |
| bb4: |
| apply %3() : $@convention(thin) () -> () |
| %9999 = tuple() |
| apply %3() : $@convention(thin) () -> () |
| return %9999 : $() |
| } |
| |
| sil @try_apply_func : $@convention(thin) () -> (Builtin.Int32, @error Error) |
| |
| // CHECK-LABEL: sil @sil_combine_dead_partial_apply_try_apply : $@convention(thin) () -> ((), @error Error) { |
| // CHECK: bb1: |
| // CHECK-NEXT: [[ORIGINAL_ALLOC_STACK:%.*]] = alloc_stack $S1 |
| // CHECK-NEXT: [[NEW_ALLOC_STACK:%.*]] = alloc_stack $S1 |
| // CHECK: bb2: |
| // CHECK-NEXT: destroy_addr [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[ORIGINAL_ALLOC_STACK]] |
| // CHECK: bb3: |
| // CHECK-NEXT: destroy_addr [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[NEW_ALLOC_STACK]] |
| // CHECK-NEXT: dealloc_stack [[ORIGINAL_ALLOC_STACK]] |
| // CHECK: bb5( |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| // CHECK: bb6( |
| // CHECK-NEXT: builtin "willThrow" |
| // CHECK-NEXT: throw |
| // CHECK: } // end sil function 'sil_combine_dead_partial_apply_try_apply' |
| sil @sil_combine_dead_partial_apply_try_apply : $@convention(thin) () -> ((), @error Error) { |
| bb0: |
| br bb1 |
| |
| bb1: |
| %0 = alloc_stack $S1 |
| %1 = function_ref @sil_combine_partial_apply_callee_2 : $@convention(thin) (@in S1) -> () |
| %2 = partial_apply %1(%0) : $@convention(thin) (@in S1) -> () |
| dealloc_stack %0 : $*S1 |
| cond_br undef, bb2, bb3 |
| |
| bb2: |
| strong_release %2 : $@callee_owned () -> () |
| %99991 = tuple() |
| br bb4 |
| |
| bb3: |
| strong_release %2 : $@callee_owned () -> () |
| %99992 = tuple() |
| br bb4 |
| |
| bb4: |
| %3 = function_ref @try_apply_func : $@convention(thin) () -> (Builtin.Int32, @error Error) |
| try_apply %3() : $@convention(thin) () -> (Builtin.Int32, @error Error), normal bb5, error bb6 |
| |
| bb5(%4 : $Builtin.Int32): |
| %9999 = tuple() |
| return %9999 : $() |
| |
| bb6(%5 : $Error): |
| %6 = builtin "willThrow"(%5 : $Error) : $() |
| throw %5 : $Error |
| } |
| |
| // Make sure that we do not optimize this case. If we do optimize this case, |
| // given the current algorithm which puts alloc_stack at the beginning/end of |
| // the function, we will have a fatal error. |
| sil @sil_combine_dead_partial_apply_with_opened_existential : $@convention(thin) () -> ((), @error Error) { |
| bb0: |
| %0b = alloc_stack $SwiftP |
| %1 = open_existential_addr mutable_access %0b : $*SwiftP to $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| %2 = witness_method $@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP, #SwiftP.foo, %1 : $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP : $@convention(witness_method: SwiftP) <Ï„_0_0 where Ï„_0_0 : SwiftP> (@in_guaranteed Ï„_0_0) -> () |
| %0c = alloc_stack $@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| %3 = partial_apply %2<@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP>(%0c) : $@convention(witness_method: SwiftP) <Ï„_0_0 where Ï„_0_0 : SwiftP> (@in_guaranteed Ï„_0_0) -> () |
| dealloc_stack %0c : $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| strong_release %3 : $@callee_owned () -> () |
| dealloc_stack %0b : $*SwiftP |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_generic_partial_apply_apply_in : |
| // CHECK: bb0([[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T): |
| // CHECK: [[STACK:%.*]] = alloc_stack $T |
| // CHECK: copy_addr [take] [[ARG1]] to [initialization] [[STACK]] |
| // CHECK: bb1: |
| // CHECK: [[STACK3:%.*]] = alloc_stack $T |
| // CHECK: copy_addr [[STACK]] to [initialization] [[STACK3]] |
| // CHECK: apply {{%.*}}<T, T>([[ARG0]], [[STACK3]]) |
| // CHECK: dealloc_stack [[STACK3]] |
| // CHECK: bb2: |
| // CHECK: destroy_addr [[STACK]] |
| // CHECK: dealloc_stack [[STACK]] |
| // CHECK: } // end sil function 'test_generic_partial_apply_apply_in' |
| sil @test_generic_partial_apply_apply_in : $@convention(thin) <T> (@in T, @in T) -> () { |
| bb0(%0 : $*T, %1 : $*T): |
| %f1 = function_ref @generic_callee_in : $@convention(thin) <T, U> (@in T, @in U) -> () |
| %pa = partial_apply [callee_guaranteed] %f1<T, T>(%1) : $@convention(thin) <T, U> (@in T, @in U) -> () |
| br bb1 |
| bb1: |
| %a1 = apply %pa(%0) : $@callee_guaranteed (@in T) -> () |
| cond_br undef, bb1, bb2 |
| bb2: |
| strong_release %pa : $@callee_guaranteed (@in T) -> () |
| %r = tuple () |
| return %r : $() |
| } |
| |
| // Now check that we handle the in_guaranteed case. |
| // CHECK-LABEL: sil @test_generic_partial_apply_apply_inguaranteed : $@convention(thin) <T> (@in T, @in T) -> () { |
| // CHECK: bb0([[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T): |
| // CHECK: [[STACK:%.*]] = alloc_stack $T |
| // CHECK: copy_addr [take] [[ARG1]] to [initialization] [[STACK]] |
| // CHECK: apply {{%.*}}<T, T>([[ARG0]], [[STACK]]) |
| // CHECK: destroy_addr [[STACK]] |
| // CHECK: destroy_addr [[ARG0]] |
| // CHECK: } // end sil function 'test_generic_partial_apply_apply_inguaranteed' |
| sil @test_generic_partial_apply_apply_inguaranteed : $@convention(thin) <T> (@in T, @in T) -> () { |
| bb0(%0 : $*T, %1 : $*T): |
| %f1 = function_ref @generic_callee_inguaranteed : $@convention(thin) <T, U> (@in_guaranteed T, @in_guaranteed U) -> () |
| %pa = partial_apply %f1<T, T>(%1) : $@convention(thin) <T, U> (@in_guaranteed T, @in_guaranteed U) -> () |
| %a1 = apply %pa(%0) : $@callee_owned (@in_guaranteed T) -> () |
| destroy_addr %0 : $*T |
| %r = tuple () |
| return %r : $() |
| } |
| |
| // CHECK-LABEL: sil @sil_combine_applied_partialapply_to_apply_with_dependent_type : $@convention(thin) (@in_guaranteed SwiftP) -> () { |
| // CHECK: bb0({{.*}}): |
| // CHECK: [[EX:%.*]] = open_existential_addr |
| // CHECK: [[METHOD:%.*]] = witness_method |
| // CHECK: [[STACK1:%.*]] = alloc_stack $@opened |
| // CHECK: copy_addr [[EX]] to [initialization] [[STACK1]] |
| // CHECK: [[STACK2:%.*]] = alloc_stack $@opened |
| // CHECK: copy_addr [take] [[STACK1]] to [initialization] [[STACK2]] |
| // CHECK: apply [[METHOD]]<@opened{{.*}}>([[STACK2]]) |
| // CHECK: destroy_addr [[STACK2]] |
| // CHECK: dealloc_stack [[STACK2]] |
| // CHECK: dealloc_stack [[STACK1]] |
| // CHECK: } // end sil function 'sil_combine_applied_partialapply_to_apply_with_dependent_type' |
| sil @sil_combine_applied_partialapply_to_apply_with_dependent_type : $@convention(thin) (@in_guaranteed SwiftP) -> () { |
| bb0(%0 : $*SwiftP): |
| %1 = open_existential_addr immutable_access %0 : $*SwiftP to $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| %2 = witness_method $@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP, #SwiftP.foo, %1 : $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP : $@convention(witness_method: SwiftP) <Ï„_0_0 where Ï„_0_0 : SwiftP> (@in_guaranteed Ï„_0_0) -> () |
| %0c = alloc_stack $@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| copy_addr %1 to [initialization] %0c : $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| %3 = partial_apply %2<@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP>(%0c) : $@convention(witness_method: SwiftP) <Ï„_0_0 where Ï„_0_0 : SwiftP> (@in_guaranteed Ï„_0_0) -> () |
| dealloc_stack %0c : $*@opened("3305E696-5685-11E5-9393-B8E856428C60") SwiftP |
| %4 = apply %3() : $@callee_owned () -> () |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| protocol MutatingProto { |
| mutating func mutatingMethod() |
| } |
| |
| struct MStruct : MutatingProto { |
| |
| var somevar: Builtin.Int32 |
| |
| mutating func mutatingMethod() |
| } |
| |
| // CHECK-LABEL: sil @dont_replace_copied_self_in_mutating_method_call |
| sil @dont_replace_copied_self_in_mutating_method_call : $@convention(thin) (MStruct) -> (@out MutatingProto) { |
| bb0(%0 : $*MutatingProto, %1 : $MStruct): |
| %2 = alloc_stack $MutatingProto |
| %4 = init_existential_addr %2 : $*MutatingProto, $MStruct |
| store %1 to %4 : $*MStruct |
| %9 = alloc_stack $MutatingProto |
| copy_addr %2 to [initialization] %9 : $*MutatingProto |
| // CHECK: [[E:%[0-9]+]] = open_existential_addr |
| %11 = open_existential_addr mutable_access %9 : $*MutatingProto to $*@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto |
| // CHECK: [[M:%[0-9]+]] = witness_method $MStruct, |
| %12 = witness_method $@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto, #MutatingProto.mutatingMethod : <Self where Self : MutatingProto> (inout Self) -> () -> (), %11 : $*@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto : $@convention(witness_method: MutatingProto) <Ï„_0_0 where Ï„_0_0 : MutatingProto> (@inout Ï„_0_0) -> () |
| // CHECK: [[C:%[0-9]+]] = unchecked_addr_cast [[E]] : $*@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto to $*MStruct |
| // CHECK: apply [[M]]<MStruct>([[C]]) : |
| %13 = apply %12<@opened("FC5F3CFA-A7A4-11E7-911F-685B35C48C83") MutatingProto>(%11) : $@convention(witness_method: MutatingProto) <Ï„_0_0 where Ï„_0_0 : MutatingProto> (@inout Ï„_0_0) -> () |
| copy_addr [take] %9 to [initialization] %0 : $*MutatingProto |
| dealloc_stack %9 : $*MutatingProto |
| destroy_addr %2 : $*MutatingProto |
| dealloc_stack %2 : $*MutatingProto |
| %27 = tuple () |
| return %27 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_replace_copied_self_in_mutating_method_call2 |
| sil @dont_replace_copied_self_in_mutating_method_call2 : $@convention(thin) (@thick MutatingProto.Type) -> (@out MutatingProto) { |
| bb0(%0 : $*MutatingProto, %1 : $@thick MutatingProto.Type): |
| %alloc1 = alloc_stack $MutatingProto, let, name "p" |
| %openType = open_existential_metatype %1 : $@thick MutatingProto.Type to $@thick (@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto).Type |
| %initType = init_existential_addr %alloc1 : $*MutatingProto, $@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto |
| %alloc2 = alloc_stack $MutatingProto |
| copy_addr %alloc1 to [initialization] %alloc2 : $*MutatingProto |
| %oeaddr = open_existential_addr mutable_access %alloc2 : $*MutatingProto to $*@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto |
| %witmethod = witness_method $@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto, #MutatingProto.mutatingMethod : <Self where Self : MutatingProto> (inout Self) -> () -> (), %openType : $@thick (@opened("66A6DAFC-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto).Type : $@convention(witness_method: MutatingProto) <Ï„_0_0 where Ï„_0_0 : MutatingProto> (@inout Ï„_0_0) -> () |
| // CHECK: apply {{%.*}}<@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto>({{%.*}}) : $@convention(witness_method: MutatingProto) <Ï„_0_0 where Ï„_0_0 : MutatingProto> (@inout Ï„_0_0) -> () // type-defs |
| %apply = apply %witmethod<@opened("6E02DCF6-AF78-11E7-8F3B-28CFE9213F4F") MutatingProto>(%oeaddr) : $@convention(witness_method: MutatingProto) <Ï„_0_0 where Ï„_0_0 : MutatingProto> (@inout Ï„_0_0) -> () |
| dealloc_stack %alloc2 : $*MutatingProto |
| dealloc_stack %alloc1 : $*MutatingProto |
| %27 = tuple () |
| return %27 : $() |
| } |
| |
| sil @helperForOptimizeApplyOfConvertFunction : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 |
| |
| // Test function_ref -> thin_to_thick -> convert_function with indirect results. |
| // (we currently bail on it). |
| // CHECK-LABEL: sil @testOptimizeApplyOfConvertFunction : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 { |
| // CHECK: bb0(%0 : $*Builtin.Int32, %1 : $*Builtin.Int8): |
| // CHECK: [[FN:%.*]] = function_ref @helperForOptimizeApplyOfConvertFunction : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 |
| // CHECK: [[TTF:%.*]] = thin_to_thick_function [[FN]] : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 to $@noescape @callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 |
| // CHECK: %{{.*}} = apply [[TTF]](%0, %1) : $@noescape @callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 |
| // CHECK: %{{.*}} = tuple () |
| // CHECK: return %{{.*}} : $() |
| // CHECK-LABEL: } // end sil function 'testOptimizeApplyOfConvertFunction' |
| sil @testOptimizeApplyOfConvertFunction : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 { |
| bb0(%0 : $*Builtin.Int32, %1 : $*Builtin.Int8): |
| %2 = function_ref @helperForOptimizeApplyOfConvertFunction : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 |
| %3 = thin_to_thick_function %2 : $@convention(thin) (@in Builtin.Int8) -> @out Builtin.Int32 to $@callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 |
| %4 = convert_escape_to_noescape %3 : $@callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 to $@noescape @callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 |
| |
| %18 = apply %4(%0, %1) : $@noescape @callee_owned (@in Builtin.Int8) -> @out Builtin.Int32 |
| |
| %29 = tuple () |
| return %29 : $() |
| } |
| |
| sil @actually_not_throwing: $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) |
| |
| // CHECK-LABEL: sil @remove_throwing_thin_to_not_throwing_thick_conversion |
| // CHECK: [[FN:%[0-9]+]] = function_ref |
| // CHECK: apply [nothrow] [[FN]](%0) |
| // CHECK: } // end sil function 'remove_throwing_thin_to_not_throwing_thick_conversion' |
| sil @remove_throwing_thin_to_not_throwing_thick_conversion : $@convention(thin) (Builtin.Int32) -> @owned Builtin.Int32 { |
| bb0(%0 : $Builtin.Int32): |
| %2 = function_ref @actually_not_throwing : $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) |
| %3 = thin_to_thick_function %2 : $@convention(thin) (Builtin.Int32) -> (@owned Builtin.Int32, @error Error) to $@callee_guaranteed (Builtin.Int32) -> @owned Builtin.Int32 |
| %5 = apply %3(%0) : $@callee_guaranteed (Builtin.Int32) -> @owned Builtin.Int32 |
| return %5 : $Builtin.Int32 |
| } |
| |
| sil @testCombineClosureHelper : $(Builtin.Int32) -> () |
| |
| // Test function_ref -> partial_apply -> convert_function -> apply. |
| // Where the convert_function only affects @noescape. |
| // |
| // CHECK-LABEL: sil @testCombineClosureNoescape : $@convention(thin) (Builtin.Int32) -> () { |
| // CHECK: bb0(%0 : $Builtin.Int32): |
| // CHECK: [[F:%.*]] = function_ref @testCombineClosureHelper : $@convention(thin) (Builtin.Int32) -> () |
| // CHECK: apply [[F]](%0) : $@convention(thin) (Builtin.Int32) -> () |
| // CHECK-LABEL: } // end sil function 'testCombineClosureNoescape' |
| sil @testCombineClosureNoescape : $(Builtin.Int32) -> () { |
| bb0(%0 : $Builtin.Int32): |
| %49 = function_ref @testCombineClosureHelper : $@convention(thin) (Builtin.Int32) -> () |
| %50 = partial_apply %49(%0) : $@convention(thin) (Builtin.Int32) -> () |
| %51 = convert_escape_to_noescape %50 : $@callee_owned () -> () to $@noescape @callee_owned () -> () |
| apply %51() : $@noescape @callee_owned () -> () |
| %empty = tuple () |
| return %empty : $() |
| } |
| |
| // Test function_ref -> partial_apply -> convert_function -> try_apply. |
| // This is not currently combined because we don't know how to reform the |
| // try_apply with a different type. |
| // |
| // CHECK-LABEL: sil @testCombineClosureConvert : $@convention(thin) (Builtin.Int32) -> () { |
| // CHECK: bb0(%0 : $Builtin.Int32): |
| // CHECK: [[F:%.*]] = function_ref @testCombineClosureHelper : $@convention(thin) (Builtin.Int32) -> () |
| // CHECK: [[PA:%.*]] = partial_apply [[F]](%0) : $@convention(thin) (Builtin.Int32) -> () |
| // CHECK: [[CVT1:%.*]] = convert_escape_to_noescape [[PA]] : $@callee_owned () -> () to $@noescape @callee_owned () -> () |
| // CHECK: [[CVT2:%.*]] = convert_function [[CVT1]] : $@noescape @callee_owned () -> () to $@noescape @callee_owned () -> @error Error |
| // CHECK: try_apply [[CVT2]]() : $@noescape @callee_owned () -> @error Error, normal bb1, error bb2 |
| // CHECK-LABEL: } // end sil function 'testCombineClosureConvert' |
| sil @testCombineClosureConvert : $(Builtin.Int32) -> () { |
| bb0(%0 : $Builtin.Int32): |
| %49 = function_ref @testCombineClosureHelper : $@convention(thin) (Builtin.Int32) -> () |
| %50 = partial_apply %49(%0) : $@convention(thin) (Builtin.Int32) -> () |
| %51 = convert_escape_to_noescape %50 : $@callee_owned () -> () to $@noescape @callee_owned () -> () |
| %52 = convert_function %51 : $@noescape @callee_owned () -> () to $@noescape @callee_owned () -> @error Error |
| try_apply %52() : $@noescape @callee_owned () -> @error Error, normal bb7, error bb11 |
| |
| bb7(%callret : $()): |
| br bb99 |
| |
| bb11(%128 : $Error): |
| br bb99 |
| |
| bb99: |
| %empty = tuple () |
| return %empty : $() |
| } |
| |
| class C {} |
| |
| sil @guaranteed_closure : $@convention(thin) (@guaranteed C) -> () |
| |
| // CHECK-LABEL: sil @test_guaranteed_closure |
| // CHECK: bb0([[ARG:%.*]] : $C): |
| // CHECK: strong_retain [[ARG]] |
| // CHECK: [[F:%.*]] = function_ref @guaranteed_closure |
| // CHECK: strong_retain [[ARG]] |
| // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[F]]([[ARG]]) |
| // CHECK: apply [[F]] |
| // Don't release the closure context -- it is @callee_guaranteed. |
| // CHECK-NOT: strong_release [[C]] : $@callee_guaranteed () -> () |
| // CHECK: strong_release [[ARG]] |
| // CHECK-NOT: strong_release [[C]] : $@callee_guaranteed () -> () |
| // CHECK: return [[C]] |
| |
| sil @test_guaranteed_closure : $@convention(thin) (@guaranteed C) -> @owned @callee_guaranteed () -> () { |
| bb0(%0: $C): |
| strong_retain %0 : $C |
| %closure_fun = function_ref @guaranteed_closure : $@convention(thin) (@guaranteed C) -> () |
| %closure = partial_apply [callee_guaranteed] %closure_fun(%0) : $@convention(thin) (@guaranteed C) -> () |
| apply %closure() : $@callee_guaranteed () -> () |
| return %closure : $@callee_guaranteed () -> () |
| } |
| |
| sil @guaranteed_closure_throws : $@convention(thin) (@guaranteed C, Builtin.Int32) -> @error Error |
| |
| // CHECK: sil @test_guaranteed_closure_try_apply |
| // CHECK: bb0(%0 : $C, %1 : $Builtin.Int32): |
| // CHECK: strong_retain %0 : $C |
| // CHECK: [[FUN:%.*]] = function_ref @guaranteed_closure_throws |
| // CHECK: [[CL:%.*]] = partial_apply [callee_guaranteed] [[FUN]](%1) |
| // CHECK: try_apply [[FUN]](%0, %1) |
| // CHECK: bb1({{.*}}): |
| // CHECK-NOT: strong_release |
| // CHECK: br bb3 |
| // CHECK: bb2({{.*}}): |
| // CHECK-NOT: strong_release |
| // CHECK: br bb3 |
| // CHECK: bb3: |
| // CHECK: return [[CL]] |
| |
| sil @test_guaranteed_closure_try_apply : $@convention(thin) (@guaranteed C, Builtin.Int32) -> @owned @callee_guaranteed (@guaranteed C) -> @error Error { |
| bb0(%0: $C, %1: $Builtin.Int32): |
| strong_retain %0 : $C |
| %closure_fun = function_ref @guaranteed_closure_throws : $@convention(thin) (@guaranteed C, Builtin.Int32) -> @error Error |
| %closure = partial_apply [callee_guaranteed] %closure_fun(%1) : $@convention(thin) (@guaranteed C, Builtin.Int32) -> @error Error |
| try_apply %closure(%0) : $@callee_guaranteed (@guaranteed C) -> @error Error, normal bb7, error bb11 |
| |
| bb7(%callret : $()): |
| br bb99 |
| |
| bb11(%128 : $Error): |
| br bb99 |
| |
| bb99: |
| return %closure : $@callee_guaranteed (@guaranteed C) -> @error Error |
| } |
| |
| // Make sure convert_function -> apply does the right thing with metatypes |
| class Base {} |
| class Derived {} |
| |
| sil @takesMetatype : $@convention(thin) (@thick Base.Type) -> () |
| |
| // CHECK-LABEL: sil @passesMetatype |
| // CHECK: [[METATYPE:%.*]] = metatype $@thick Derived.Type |
| // CHECK: [[FN:%.*]] = function_ref @takesMetatype |
| // CHECK: [[CONVERTED:%.*]] = unchecked_trivial_bit_cast [[METATYPE]] : $@thick Derived.Type to $@thick Base.Type |
| // CHECK: [[RESULT:%.*]] = apply [[FN]]([[CONVERTED]]) |
| |
| sil @passesMetatype : $@convention(thin) () -> () { |
| bb0: |
| %metatype = metatype $@thick Derived.Type |
| %fn = function_ref @takesMetatype : $@convention(thin) (@thick Base.Type) -> () |
| %converted = convert_function %fn : $@convention(thin) (@thick Base.Type) -> () to $@convention(thin) (@thick Derived.Type) -> () |
| %result = apply %converted(%metatype) : $@convention(thin) (@thick Derived.Type) -> () |
| %return = tuple () |
| return %return : $() |
| } |
| |
| sil shared [transparent] [thunk] @genericClosure : $@convention(thin) <T> (@in T) -> @out T { |
| bb0(%0 : $*T, %1 : $*T): |
| %12 = tuple () |
| return %12 : $() |
| } |
| |
| // CHECK-LABEL: sil shared @genericThinToThick : $@convention(thin) () -> () |
| // CHECK: [[F:%.*]] = function_ref @genericClosure : $@convention(thin) <Ï„_0_0> (@in Ï„_0_0) -> @out Ï„_0_0 |
| // CHECK: apply [[F]]<Builtin.Int64>(%{{.*}}, %{{.*}}) : $@convention(thin) <Ï„_0_0> (@in Ï„_0_0) -> @out Ï„_0_0 |
| // CHECK-LABEL: } // end sil function 'genericThinToThick' |
| sil shared @genericThinToThick : $@convention(thin) () -> () { |
| bb0: |
| %fn = function_ref @genericClosure : $@convention(thin) <T> (@in T) -> @out T |
| %thick = thin_to_thick_function %fn : $@convention(thin) <T> (@in T) -> @out T to $@noescape @callee_guaranteed <T> (@in T) -> @out T |
| %in = alloc_stack $Builtin.Int64 |
| %out = alloc_stack $Builtin.Int64 |
| %c3 = integer_literal $Builtin.Int64, 3 |
| store %c3 to %in : $*Builtin.Int64 |
| %call = apply %thick<Builtin.Int64>(%out, %in) : $@noescape @callee_guaranteed <T> (@in T) -> @out T |
| dealloc_stack %out : $*Builtin.Int64 |
| dealloc_stack %in : $*Builtin.Int64 |
| %999 = tuple () |
| return %999 : $() |
| } |
| |
| sil shared [transparent] [thunk] @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 { |
| bb0(%0 : $*Builtin.Int64, %1 : $*Builtin.Int64): |
| %val = load %1 : $*Builtin.Int64 |
| store %val to %0 : $*Builtin.Int64 |
| %999 = tuple () |
| return %999 : $() |
| } |
| |
| // CHECK-LABEL: sil shared @appliedEscapeToNoEscape : $@convention(thin) () -> () { |
| // CHECK: [[F:%.*]] = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 // user: %5 |
| // CHECK: apply [[F]](%{{.*}}, %{{.*}}) : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| sil shared @appliedEscapeToNoEscape : $@convention(thin) () -> () { |
| bb0: |
| %fn = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| %pa = partial_apply [callee_guaranteed] %fn() : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| %cvt = convert_escape_to_noescape %pa : $@callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 to $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 |
| %out = alloc_stack $Builtin.Int64 |
| %in = alloc_stack $Builtin.Int64 |
| %c3 = integer_literal $Builtin.Int64, 3 |
| store %c3 to %in : $*Builtin.Int64 |
| %call = apply %cvt(%out, %in) : $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 |
| dealloc_stack %in : $*Builtin.Int64 |
| dealloc_stack %out : $*Builtin.Int64 |
| strong_release %pa : $@callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 |
| %999 = tuple () |
| return %999 : $() |
| } |
| |
| // CHECK-LABEL: sil shared @applied_on_stack : $@convention(thin) () -> () { |
| // CHECK: bb0: |
| // CHECK: [[F:%.*]] = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| // CHECK: [[STK:%.*]] = alloc_stack $Builtin.Int64 |
| // CHECK: [[STK2:%.*]] = alloc_stack $Builtin.Int64 |
| // CHECK: [[I:%.*]] = integer_literal $Builtin.Int64, 3 |
| // CHECK: store [[I]] to [[STK2]] : $*Builtin.Int64 |
| // CHECK: apply [[F]]([[STK]], [[STK2]]) : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| // CHECK: dealloc_stack [[STK2]] : $*Builtin.Int64 |
| // CHECK: dealloc_stack [[STK]] : $*Builtin.Int64 |
| // CHECK: return %8 : $() |
| |
| sil shared @applied_on_stack : $@convention(thin) () -> () { |
| bb0: |
| %fn = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| %pa = partial_apply [callee_guaranteed] [on_stack] %fn() : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 |
| %out = alloc_stack $Builtin.Int64 |
| %in = alloc_stack $Builtin.Int64 |
| %c3 = integer_literal $Builtin.Int64, 3 |
| store %c3 to %in : $*Builtin.Int64 |
| %call = apply %pa(%out, %in) : $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 |
| dealloc_stack %in : $*Builtin.Int64 |
| dealloc_stack %out : $*Builtin.Int64 |
| dealloc_stack %pa : $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 |
| %999 = tuple () |
| return %999 : $() |
| } |
| |
| sil @guaranteed_closure_throws2 : $@convention(thin) (Builtin.Int32, @guaranteed C) -> @error Error |
| |
| // CHECK-LABEL: sil @test_guaranteed_closure_try_apply_on_stack |
| // CHECK: bb0 |
| // CHECK: strong_retain |
| // CHECK: try_apply |
| // CHECK: bb1 |
| // CHECK: strong_release |
| // CHECK: br bb3 |
| // CHECK: bb2 |
| // CHECK: strong_release |
| // CHECK: br bb3 |
| sil @test_guaranteed_closure_try_apply_on_stack : $@convention(thin) (@guaranteed C, Builtin.Int32) -> () { |
| bb0(%0: $C, %1: $Builtin.Int32): |
| strong_retain %0 : $C |
| %closure_fun = function_ref @guaranteed_closure_throws2 : $@convention(thin) (Builtin.Int32, @guaranteed C) -> @error Error |
| %closure = partial_apply [callee_guaranteed] [on_stack] %closure_fun(%0) : $@convention(thin) (Builtin.Int32, @guaranteed C) -> @error Error |
| try_apply %closure(%1) : $@noescape @callee_guaranteed (Builtin.Int32) -> @error Error, normal bb7, error bb11 |
| |
| bb7(%callret : $()): |
| dealloc_stack %closure : $@noescape @callee_guaranteed (Builtin.Int32) -> @error Error |
| strong_release %0: $C |
| br bb99 |
| |
| bb11(%128 : $Error): |
| dealloc_stack %closure : $@noescape @callee_guaranteed (Builtin.Int32) -> @error Error |
| strong_release %0: $C |
| br bb99 |
| |
| bb99: |
| %t = tuple() |
| return %t : $() |
| } |
| |
| sil @convert_function_simplification_callee : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : $Klass): |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| sil @convert_function_simplification_callee_with_error : $@convention(thin) (@guaranteed Klass) -> @error Error { |
| bb0(%0 : $Klass): |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @convert_function_simplification_caller : $@convention(thin) (@guaranteed Klass) -> () { |
| // CHECK: [[FUNC:%.*]] = function_ref @convert_function_simplification_callee : $@convention(thin) (@guaranteed Klass) -> () |
| // CHECK: apply [[FUNC]]({{.*}}) : $@convention(thin) (@guaranteed Klass) -> () |
| // CHECK: [[FUNC:%.*]] = function_ref @convert_function_simplification_callee_with_error : $@convention(thin) (@guaranteed Klass) -> @error Error |
| // CHECK: apply [nothrow] [[FUNC]]({{.*}}) : $@convention(thin) (@guaranteed Klass) -> @error Error |
| // CHECK: } // end sil function 'convert_function_simplification_caller' |
| sil @convert_function_simplification_caller : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : $Klass): |
| %1 = function_ref @convert_function_simplification_callee : $@convention(thin) (@guaranteed Klass) -> () |
| %2 = thin_to_thick_function %1 : $@convention(thin) (@guaranteed Klass) -> () to $@callee_guaranteed (@guaranteed Klass) -> () |
| %3 = convert_function %2 : $@callee_guaranteed (@guaranteed Klass) -> () to $@callee_guaranteed (@guaranteed Klass) -> @error Error |
| %4 = apply [nothrow] %3(%0) : $@callee_guaranteed (@guaranteed Klass) -> @error Error |
| |
| %5 = function_ref @convert_function_simplification_callee_with_error : $@convention(thin) (@guaranteed Klass) -> @error Error |
| %6 = thin_to_thick_function %5 : $@convention(thin) (@guaranteed Klass) -> @error Error to $@callee_guaranteed (@guaranteed Klass) -> @error Error |
| %7 = convert_function %6 : $@callee_guaranteed (@guaranteed Klass) -> @error Error to $@callee_guaranteed (@guaranteed Klass) -> @error Error |
| %8 = apply [nothrow] %7(%0) : $@callee_guaranteed (@guaranteed Klass) -> @error Error |
| |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| /////////////////////////////////// |
| // (apply partial_apply()) tests // |
| /////////////////////////////////// |
| |
| class CC1 { |
| deinit |
| init() |
| } |
| |
| class CC2 { |
| deinit |
| init() |
| } |
| |
| class CC3 { |
| deinit |
| init() |
| } |
| |
| class CC4 { |
| deinit |
| init() |
| } |
| |
| sil @closure_with_in_args : $@convention(method) (@in CC1) -> () |
| sil @closure_with_inguaranteed_args : $@convention(method) (@in_guaranteed CC1) -> () |
| sil @closure_with_owned_args : $@convention(method) (@owned CC1) -> () |
| sil @closure_with_guaranteed_args : $@convention(method) (@guaranteed CC1) -> () |
| |
| // Test the peephole performing apply{partial_apply(x, y, z)}(a) -> apply(a, x, y, z) |
| // |
| // We need to check the following: |
| // |
| // - All arguments of a partial_apply, which are either results of a stack_alloc |
| // or consumed indirect arguments should be copied into temporaries. This should |
| // happen just before that partial_apply instruction. |
| // |
| // - Before each apply of the partial_apply, we retain values of any arguments |
| // which are of non-address type. This is required because they could be |
| // consumed (i.e. released by the callee). |
| // |
| // - After each apply of the partial_apply, we release values of any arguments |
| // which are non-consumed by the callee (e.g. @guaranteed ones) |
| // |
| // We do this separately for each type of argument. |
| |
| // CHECK-LABEL: sil @test_apply_of_partial_apply_in : $@convention(thin) (@in CC1) -> () { |
| // CHECK: [[FUNC:%.*]] = function_ref |
| // CHECK: [[STACK1:%.*]] = alloc_stack $CC1 |
| // CHECK: copy_addr [take] %0 to [initialization] [[STACK1]] |
| // CHECK: cond_br undef, bb1, bb2 |
| // CHECK: bb1: |
| // CHECK: destroy_addr [[STACK1]] |
| // CHECK: dealloc_stack [[STACK1]] |
| // CHECK: bb2: |
| // CHECK: [[STACK2:%.*]] = alloc_stack $CC1 |
| // CHECK: copy_addr [[STACK1]] to [initialization] [[STACK2]] |
| // CHECK: apply [[FUNC]]([[STACK2]]) |
| // CHECK: dealloc_stack [[STACK2]] |
| // CHECK: destroy_addr [[STACK1]] |
| // CHECK: dealloc_stack [[STACK1]] |
| // CHECK: bb3: |
| // CHECK: } // end sil function 'test_apply_of_partial_apply_in' |
| sil @test_apply_of_partial_apply_in : $@convention(thin) (@in CC1) -> () { |
| bb0(%0 : $*CC1): |
| %1 = function_ref @closure_with_in_args : $@convention(method) (@in CC1) -> () |
| %3 = partial_apply %1(%0) : $@convention(method) (@in CC1) -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| strong_release %3 : $@callee_owned () -> () |
| br bb3 |
| |
| bb2: |
| apply %3() : $@callee_owned () -> () |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_apply_of_partial_apply_inguaranteed : $@convention(thin) (@in CC1) -> () { |
| // CHECK: bb0([[ARG:%.*]] : |
| // CHECK: [[STACK1:%.*]] = alloc_stack $CC1 |
| // CHECK: copy_addr [take] [[ARG]] to [initialization] [[STACK1]] |
| // CHECK: [[STACK2:%.*]] = alloc_stack $CC1 |
| // CHECK: copy_addr [take] [[STACK1]] to [initialization] [[STACK2]] |
| // CHECK: cond_br undef, bb1, bb2 |
| // |
| // CHECK: bb1: |
| // CHECK: destroy_addr [[STACK2]] |
| // CHECK: dealloc_stack [[STACK2]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb2: |
| // CHECK: apply {{%.*}}([[STACK2]]) : $@convention(method) (@in_guaranteed CC1) -> () |
| // CHECK: destroy_addr [[STACK2]] |
| // CHECK: dealloc_stack [[STACK2]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb3: |
| // CHECK: dealloc_stack [[STACK1]] |
| // CHECK: } // end sil function 'test_apply_of_partial_apply_inguaranteed' |
| sil @test_apply_of_partial_apply_inguaranteed : $@convention(thin) (@in CC1) -> () { |
| bb0(%0 : $*CC1): |
| %1 = function_ref @closure_with_inguaranteed_args : $@convention(method) (@in_guaranteed CC1) -> () |
| %2 = alloc_stack $CC1 |
| copy_addr [take] %0 to [initialization] %2 : $*CC1 |
| %3 = partial_apply %1(%2) : $@convention(method) (@in_guaranteed CC1) -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| strong_release %3 : $@callee_owned () -> () |
| br bb3 |
| |
| bb2: |
| apply %3() : $@callee_owned () -> () |
| br bb3 |
| |
| bb3: |
| dealloc_stack %2 : $*CC1 |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_apply_of_partial_apply_owned : $@convention(thin) (@in CC1) -> () { |
| // CHECK: bb0([[ARG:%.*]] : |
| // CHECK: [[LOAD_ARG:%.*]] = load [[ARG]] |
| // CHECK: cond_br undef, bb1, bb2 |
| // |
| // CHECK: bb1: |
| // CHECK: strong_release [[LOAD_ARG]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb2: |
| // CHECK: strong_retain [[LOAD_ARG]] |
| // CHECK: apply {{%.*}}([[LOAD_ARG]]) : $@convention(method) (@owned CC1) -> () |
| // CHECK: strong_release [[LOAD_ARG]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb3: |
| // CHECK: } // end sil function 'test_apply_of_partial_apply_owned' |
| sil @test_apply_of_partial_apply_owned : $@convention(thin) (@in CC1) -> () { |
| bb0(%0 : $*CC1): |
| %1 = function_ref @closure_with_owned_args : $@convention(method) (@owned CC1) -> () |
| %2 = load %0 : $*CC1 |
| %3 = partial_apply %1(%2) : $@convention(method) (@owned CC1) -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| strong_release %3 : $@callee_owned () -> () |
| br bb3 |
| |
| bb2: |
| apply %3() : $@callee_owned () -> () |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_apply_of_partial_apply_guaranteed : $@convention(thin) (@in CC1) -> () { |
| // CHECK: bb0([[ARG:%.*]] : |
| // CHECK: [[LOAD_ARG:%.*]] = load [[ARG]] |
| // CHECK: cond_br undef, bb1, bb2 |
| // |
| // CHECK: bb1: |
| // CHECK: strong_release [[LOAD_ARG]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb2: |
| // CHECK: apply {{%.*}}([[LOAD_ARG]]) : $@convention(method) (@guaranteed CC1) -> () |
| // CHECK: strong_release [[LOAD_ARG]] |
| // CHECK: br bb3 |
| // |
| // CHECK: bb3: |
| // CHECK: } // end sil function 'test_apply_of_partial_apply_guaranteed' |
| sil @test_apply_of_partial_apply_guaranteed : $@convention(thin) (@in CC1) -> () { |
| bb0(%0 : $*CC1): |
| %1 = function_ref @closure_with_guaranteed_args : $@convention(method) (@guaranteed CC1) -> () |
| %2 = load %0 : $*CC1 |
| %3 = partial_apply %1(%2) : $@convention(method) (@guaranteed CC1) -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| strong_release %3 : $@callee_owned () -> () |
| br bb3 |
| |
| bb2: |
| apply %3() : $@callee_owned () -> () |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| |
| // Test if we insert the right stack/dealloc-stack when converting apply{partial_apply}. |
| |
| // CHECK-LABEL: sil @test_stack_insertion_for_partial_apply_apply |
| // CHECK: bb0({{.*}}): |
| // CHECK-NEXT: alloc_stack $Bool |
| // CHECK-NEXT: [[S1:%[0-9]+]] = alloc_stack $Int |
| // CHECK: store %0 to [[S1]] |
| // CHECK: [[S2:%[0-9]+]] = alloc_stack $Int |
| // CHECK: copy_addr [[S1]] to [initialization] [[S2]] |
| // CHECK: apply {{.*}}(%1, [[S2]]) |
| // CHECK: destroy_addr [[S2]] : $*Int |
| // CHECK: dealloc_stack [[S2]] : $*Int |
| // CHECK: dealloc_stack [[S1]] : $*Int |
| // CHECK: bb1: |
| // CHECK-NOT: dealloc_stack |
| // CHECK: bb2: |
| // CHECK: dealloc_stack {{.*}} : $*Bool |
| // CHECK: return |
| sil @test_stack_insertion_for_partial_apply_apply : $@convention(thin) (Int, Double) -> () { |
| bb0(%0 : $Int, %1 : $Double): |
| %s1 = alloc_stack $Bool |
| %s2 = alloc_stack $Int |
| store %0 to %s2 : $*Int |
| %f1 = function_ref @callee : $@convention(thin) (Double, @in_guaranteed Int) -> () |
| %pa = partial_apply %f1(%s2) : $@convention(thin) (Double, @in_guaranteed Int) -> () |
| dealloc_stack %s2 : $*Int |
| %a1 = apply %pa(%1) : $@callee_owned (Double) -> () |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| %f2 = function_ref @noreturn_func : $@convention(thin) () -> Never |
| %a2 = apply %f2() : $@convention(thin) () -> Never |
| unreachable |
| |
| bb2: |
| dealloc_stack %s1 : $*Bool |
| %r = tuple () |
| return %r : $() |
| } |
| |
| // CHECK-LABEL: sil @test_existential_partial_apply_apply |
| // CHECK: bb0(%0 : $*FakeProtocol): |
| // CHECK-NEXT: [[OPEN:%.*]] = open_existential_addr immutable_access |
| // CHECK-NEXT: [[FN:%.*]] = witness_method |
| // CHECK-NEXT: apply [[FN]]<@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol>([[OPEN]]) |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| sil @test_existential_partial_apply_apply : $@convention(thin) (@in FakeProtocol) -> () { |
| bb0(%0: $*FakeProtocol): |
| %o = open_existential_addr immutable_access %0 : $*FakeProtocol to $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol |
| %f1 = witness_method $@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol, #FakeProtocol.requirement, %o : $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol : $@convention(witness_method: FakeProtocol) <T where T : FakeProtocol> (@in_guaranteed T) -> () |
| %pa = partial_apply %f1<@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol>() : $@convention(witness_method: FakeProtocol) <T where T : FakeProtocol> (@in_guaranteed T) -> () |
| %a1 = apply %pa(%o) : $@callee_owned (@in_guaranteed @opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol) -> () |
| |
| %r = tuple () |
| return %r : $() |
| } |
| |
| // Test `partial_apply` with a `@convention(method)` callee. |
| // Rewriting `partial_apply` to `thin_to_thick_function` is not okay because |
| // `thin_to_thick_function` supports only `@convention(thin)` operands. |
| |
| sil @method : $@convention(method) (Int32) -> () |
| |
| sil @test_partial_apply_method : $@convention(thin) () -> @owned @callee_owned (Int32) -> () { |
| %1 = function_ref @method : $@convention(method) (Int32) -> () |
| %2 = partial_apply %1() : $@convention(method) (Int32) -> () |
| return %2 : $@callee_owned (Int32) -> () |
| } |
| |
| // CHECK-LABEL: sil @test_partial_apply_method |
| // CHECK: [[FN:%.*]] = function_ref @method |
| // CHECK-NEXT: partial_apply [[FN]]() |
| // CHECK-NEXT: return |
| |
| sil [noinline] @$foo : $@convention(thin) (@owned { var Int64 }) -> () |
| |
| // CHECK-LABEL: sil private [noinline] @$testdeadpartialapply : |
| // CHECK-NOT: partial_apply |
| // CHECK-LABEL: } // end sil function '$testdeadpartialapply' |
| sil private [noinline] @$testdeadpartialapply : $@convention(thin) (@owned { var Int64 }) -> () { |
| bb0(%0 : ${ var Int64 }): |
| %3 = function_ref @$foo : $@convention(thin) (@owned { var Int64 }) -> () |
| %5 = partial_apply [callee_guaranteed] %3(%0) : $@convention(thin) (@owned { var Int64 }) -> () |
| cond_br undef, bb1, bb2 |
| bb1: |
| strong_retain %0 : ${ var Int64 } |
| strong_retain %5 : $@callee_guaranteed () -> () |
| unreachable |
| bb2: |
| strong_retain %0 : ${ var Int64 } |
| strong_retain %5 : $@callee_guaranteed () -> () |
| br bb3 |
| bb3: |
| strong_release %5 : $@callee_guaranteed () -> () |
| strong_release %5 : $@callee_guaranteed () -> () |
| strong_release %0 : ${ var Int64 } |
| %rv = tuple() |
| return %rv : $() |
| } |