Skip to content

Commit 37655db

Browse files
authored
Merge pull request #29645 from gottesmm/pr-4435ac48330e0dd08b30bf6fe8f29a93f25ff835
[sil] Do not eliminate (apply (partial_apply)) if the partial_apply captures an @in parameter and handle @in_guaranteed appropriately.
2 parents c9b1723 + e5aaa68 commit 37655db

File tree

4 files changed

+355
-268
lines changed

4 files changed

+355
-268
lines changed

lib/SILOptimizer/Utils/PartialApplyCombiner.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,29 +90,38 @@ bool PartialApplyCombiner::allocateTemporaries() {
9090
for (unsigned i : indices(argList)) {
9191
SILValue arg = argList[i];
9292
SILParameterInfo param = paramList[i];
93+
// If we do not have an indirect parameter, we do not need to do any further
94+
// work here.
95+
if (!pai->getSubstCalleeConv().isSILIndirect(param))
96+
continue;
97+
98+
// If our indirect parameter is mutating, we can just skip it as well.
9399
if (param.isIndirectMutating())
94100
continue;
95101

96-
// Create a temporary and copy the argument into it, if:
97-
// - the argument stems from an alloc_stack
98-
// - the argument is consumed by the callee and is indirect
99-
// (e.g. it is an @in argument)
100-
if (isa<AllocStackInst>(arg) ||
101-
(param.isConsumed() &&
102-
pai->getSubstCalleeConv().isSILIndirect(param))) {
103-
// If the argument has a dependent type, then we can not create a
104-
// temporary for it at the beginning of the function, so we must bail.
105-
//
106-
// TODO: This is because we are inserting alloc_stack at the beginning/end
107-
// of functions where the dependent type may not exist yet.
108-
if (arg->getType().hasOpenedExistential())
109-
return false;
110-
111-
// If the temporary is non-trivial, we need to destroy it later.
112-
if (!arg->getType().isTrivial(*pai->getFunction()))
113-
needsDestroys = true;
114-
argsToHandle.push_back(std::make_pair(arg, i));
102+
// If we are consuming an indirect parameter. Bail! We do not support that
103+
// today!
104+
if (param.isConsumed()) {
105+
return false;
106+
}
107+
108+
// Otherwise, we must have a guaranteed parameter. If we don't, bail.
109+
if (!param.isGuaranteed()) {
110+
return false;
115111
}
112+
113+
// If the argument has a dependent type, then we can not create a
114+
// temporary for it at the beginning of the function, so we must bail.
115+
//
116+
// TODO: This is because we are inserting alloc_stack at the beginning/end
117+
// of functions where the dependent type may not exist yet.
118+
if (arg->getType().hasOpenedExistential())
119+
return false;
120+
121+
// If the temporary is non-trivial, we need to destroy it later.
122+
if (!arg->getType().isTrivial(*pai->getFunction()))
123+
needsDestroys = true;
124+
argsToHandle.push_back(std::make_pair(arg, i));
116125
}
117126

118127
if (needsDestroys) {
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-run-simple-swift(-Osize)
2+
3+
// REQUIRES: executable_test
4+
5+
@inline(never)
6+
func consumeSelf<T>(_ t : __owned T) {
7+
print("Consuming self!")
8+
print(t)
9+
}
10+
11+
class Klass {}
12+
struct S<T> {
13+
let t: T? = (Klass() as! T)
14+
15+
@inline(__always)
16+
__consuming func foo(_ t: T) {
17+
consumeSelf(self)
18+
}
19+
}
20+
21+
public func test<T>(_ t: __owned T) {
22+
let k = S<T>()
23+
let f = k.foo
24+
25+
for _ in 0..<1024 {
26+
f(t)
27+
}
28+
}
29+
30+
test(Klass())

test/SILOptimizer/sil_combine.sil

Lines changed: 0 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,231 +2905,6 @@ bb0:
29052905
return %2 : $Builtin.Int1
29062906
}
29072907

2908-
class CC1 {
2909-
deinit
2910-
init()
2911-
}
2912-
2913-
class CC2 {
2914-
deinit
2915-
init()
2916-
}
2917-
2918-
class CC3 {
2919-
deinit
2920-
init()
2921-
}
2922-
2923-
class CC4 {
2924-
deinit
2925-
init()
2926-
}
2927-
2928-
2929-
// Function that takes different kinds of arguments: @in, @guaranteed, @owned,
2930-
sil @closure_with_in_guaranteed_owned_in_args : $@convention(method) (@in CC2, @guaranteed CC1, @owned CC3, @in CC4) -> Optional<Int>
2931-
2932-
// Test the peephole performing apply{partial_apply(x, y, z)}(a) -> apply(a, x, y, z)
2933-
// We need to check the following:
2934-
// - all arguments of a partial_apply, which are either results of a stack_alloc or consumed indirect arguments
2935-
// should be copied into temporaries. This should happen just before that partial_apply instruction.
2936-
// - The temporaries are allocated at the beginning of the function and deallocated at the end.
2937-
// - Before each apply of the partial_apply, we retain values of any arguments which are of non-address type.
2938-
// This is required because they could be consumed (i.e. released by the callee).
2939-
// - After each apply of the partial_apply, we release values of any arguments which are non-consumed by the callee (e.g. @guaranteed ones)
2940-
2941-
// CHECK-LABEL: sil @test_apply_of_partial_apply
2942-
2943-
// CHECK: bb0{{.*}}:
2944-
// A new temporary should have been created for each alloc_stack argument passed to partial_apply
2945-
// CHECK: [[TMP:%[0-9]+]] = alloc_stack $CC4
2946-
// CHECK: [[CLOSURE:%[0-9]+]] = function_ref @closure_with_in_guaranteed_owned_in_args
2947-
// Copy the original value of the argument into a temporary
2948-
// CHECK: copy_addr {{.*}} to [initialization] [[TMP]] : $*CC4
2949-
// CHECK-NOT: partial_apply
2950-
2951-
// CHECK: bb1:
2952-
// CHECK-NOT: partial_apply
2953-
// Check that the peephole inserted a retain of the closure's guaranteed argument
2954-
// CHECK: strong_retain %{{[0-9]+}} : $CC1
2955-
// Check that the peephole inserted a retain of the closure's owned argument
2956-
// CHECK: strong_retain %{{[0-9]+}} : $CC3
2957-
// CHECK: apply [[CLOSURE]]
2958-
// Check that the peephole inserted a release the closure's guaranteed argument
2959-
// CHECK: strong_release %{{[0-9]+}} : $CC1
2960-
// CHECK-NOT: partial_apply
2961-
// Retain the guaranteed argument
2962-
// CHECK: strong_retain %{{[0-9]+}} : $CC1
2963-
// Retain the owned argument
2964-
// CHECK: strong_retain %{{[0-9]+}} : $CC3
2965-
// CHECK: apply [[CLOSURE]]({{.*}}, [[TMP]])
2966-
// Check that the peephole inserted a release the guaranteed argument
2967-
// CHECK: strong_release %{{[0-9]+}} : $CC1
2968-
// Release the @owned CC4 argument of the function
2969-
// CHECK: load {{%[0-9]+}} : $*CC4
2970-
// CHECK: strong_release {{%[0-9]+}} : $CC4
2971-
// CHECK: br bb3
2972-
2973-
// CHECK: bb2:
2974-
// CHECK-NOT: partial_apply
2975-
// Check that the peephole inserted a retain of the closure's guaranteed argument
2976-
// CHECK: strong_retain %{{[0-9]+}} : $CC1
2977-
// Check that the peephole inserted a retain of the closure's owned argument
2978-
// CHECK: strong_retain %{{[0-9]+}} : $CC3
2979-
// CHECK: apply [[CLOSURE]]({{.*}}, [[TMP]])
2980-
// Check that the peephole inserted a release the closure's guaranteed argument
2981-
// CHECK: strong_release %{{[0-9]+}} : $CC1
2982-
// Release the @owned CC4 argument of the function
2983-
// CHECK: load {{%[0-9]+}} : $*CC4
2984-
// CHECK: strong_release {{%[0-9]+}} : $CC4
2985-
// CHECK: br bb3
2986-
2987-
// bb3:
2988-
// dealloc_stack [[TMP]] : $CC4
2989-
2990-
sil @test_apply_of_partial_apply : $@convention(thin) (@in Optional<CC2>, @guaranteed CC1, @guaranteed CC3, @guaranteed CC4, @guaranteed CC2) -> Optional<Int> {
2991-
bb0(%0 : $*Optional<CC2>, %1 : $CC1, %2 : $CC3, %3 : $CC4, %4 : $CC2):
2992-
2993-
%5 = function_ref @closure_with_in_guaranteed_owned_in_args : $@convention(method) (@in CC2, @guaranteed CC1, @owned CC3, @in CC4) -> Optional<Int>
2994-
%6 = alloc_stack $CC3
2995-
store %2 to %6 : $*CC3
2996-
%8 = load %6 : $*CC3
2997-
%9 = alloc_stack $CC4
2998-
store %3 to %9 : $*CC4
2999-
%11 = load %9 : $*CC4
3000-
strong_retain %1 : $CC1
3001-
%12 = partial_apply %5(%1, %8, %9) : $@convention(method) (@in CC2, @guaranteed CC1, @owned CC3, @in CC4) -> Optional<Int>
3002-
dealloc_stack %9 : $*CC4
3003-
dealloc_stack %6 : $*CC3
3004-
%15 = convert_function %12 : $@callee_owned (@in CC2) -> Optional<Int> to $@callee_owned (@in CC2) -> (Optional<Int>, @error Error)
3005-
%16 = alloc_stack $Optional<Int>
3006-
%17 = alloc_stack $Optional<CC2>
3007-
copy_addr %0 to [initialization] %17 : $*Optional<CC2>
3008-
switch_enum_addr %17 : $*Optional<CC2>, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2
3009-
3010-
bb1:
3011-
%21 = unchecked_take_enum_data_addr %17 : $*Optional<CC2>, #Optional.some!enumelt.1
3012-
3013-
%22 = alloc_stack $CC2
3014-
copy_addr [take] %21 to [initialization] %22 : $*CC2
3015-
%24 = alloc_stack $CC2
3016-
copy_addr %22 to [initialization] %24 : $*CC2
3017-
destroy_addr %22 : $*CC2
3018-
strong_retain %15 : $@callee_owned (@in CC2) -> (Optional<Int>, @error Error)
3019-
%28 = apply %12(%24) : $@callee_owned (@in CC2) -> Optional<Int>
3020-
store %28 to %16 : $*Optional<Int>
3021-
dealloc_stack %24 : $*CC2
3022-
dealloc_stack %22 : $*CC2
3023-
3024-
%102 = alloc_stack $CC2
3025-
copy_addr [take] %21 to [initialization] %102 : $*CC2
3026-
%104 = alloc_stack $CC2
3027-
copy_addr %102 to [initialization] %104 : $*CC2
3028-
destroy_addr %102 : $*CC2
3029-
strong_retain %15 : $@callee_owned (@in CC2) -> (Optional<Int>, @error Error)
3030-
%108 = apply %12(%104) : $@callee_owned (@in CC2) -> Optional<Int>
3031-
store %108 to %16 : $*Optional<Int>
3032-
dealloc_stack %104 : $*CC2
3033-
dealloc_stack %102 : $*CC2
3034-
3035-
dealloc_stack %17 : $*Optional<CC2>
3036-
3037-
br bb3
3038-
3039-
bb2:
3040-
%39 = alloc_stack $CC2
3041-
store %4 to %39 : $*CC2
3042-
strong_retain %15 : $@callee_owned (@in CC2) -> (Optional<Int>, @error Error)
3043-
%42 = apply %12(%39) : $@callee_owned (@in CC2) -> Optional<Int>
3044-
store %42 to %16 : $*Optional<Int>
3045-
dealloc_stack %39 : $*CC2
3046-
dealloc_stack %17 : $*Optional<CC2>
3047-
br bb3
3048-
3049-
bb3:
3050-
destroy_addr %0 : $*Optional<CC2>
3051-
strong_release %15 : $@callee_owned (@in CC2) -> (Optional<Int>, @error Error)
3052-
%36 = load %16 : $*Optional<Int>
3053-
dealloc_stack %16 : $*Optional<Int>
3054-
return %36 : $Optional<Int>
3055-
}
3056-
3057-
// Test if we insert the right stack/dealloc-stack when converting apply{partial_apply}.
3058-
3059-
// CHECK-LABEL: sil @test_stack_insertion_for_partial_apply_apply
3060-
// CHECK: bb0({{.*}}):
3061-
// CHECK-NEXT: [[T:%[0-9]+]] = alloc_stack $Int
3062-
// CHECK-NEXT: alloc_stack $Bool
3063-
// CHECK-NEXT: [[S2:%[0-9]+]] = alloc_stack $Int
3064-
// CHECK: copy_addr [[S2]] to [initialization] [[T]]
3065-
// CHECK: dealloc_stack [[S2]] : $*Int
3066-
// CHECK: apply
3067-
// CHECK: bb1:
3068-
// CHECK-NOT: dealloc_stack
3069-
// CHECK: bb2:
3070-
// CHECK: dealloc_stack {{.*}} : $*Bool
3071-
// CHECK: dealloc_stack [[T]] : $*Int
3072-
// CHECK: return
3073-
sil @test_stack_insertion_for_partial_apply_apply : $@convention(thin) (Int, Double) -> () {
3074-
bb0(%0 : $Int, %1 : $Double):
3075-
%s1 = alloc_stack $Bool
3076-
%s2 = alloc_stack $Int
3077-
store %0 to %s2 : $*Int
3078-
%f1 = function_ref @callee : $@convention(thin) (Double, @in_guaranteed Int) -> ()
3079-
%pa = partial_apply %f1(%s2) : $@convention(thin) (Double, @in_guaranteed Int) -> ()
3080-
dealloc_stack %s2 : $*Int
3081-
%a1 = apply %pa(%1) : $@callee_owned (Double) -> ()
3082-
cond_br undef, bb1, bb2
3083-
3084-
bb1:
3085-
%f2 = function_ref @noreturn_func : $@convention(thin) () -> Never
3086-
%a2 = apply %f2() : $@convention(thin) () -> Never
3087-
unreachable
3088-
3089-
bb2:
3090-
dealloc_stack %s1 : $*Bool
3091-
%r = tuple ()
3092-
return %r : $()
3093-
}
3094-
3095-
// CHECK-LABEL: sil @test_generic_partial_apply_apply
3096-
// CHECK: bb0([[ARG0:%.*]] : $*T, [[ARG1:%.*]] : $*T):
3097-
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $T
3098-
// CHECK: [[FN:%.*]] = function_ref @generic_callee
3099-
// CHECK-NEXT: copy_addr [[ARG1]] to [initialization] [[TMP]] : $*T
3100-
// CHECK-NEXT: destroy_addr [[ARG1]]
3101-
// CHECK-NEXT: apply [[FN]]<T, T>([[ARG0]], [[TMP]])
3102-
// CHECK-NEXT: destroy_addr [[TMP]]
3103-
// CHECK-NEXT: tuple
3104-
// CHECK-NEXT: dealloc_stack [[TMP]]
3105-
// CHECK-NEXT: return
3106-
sil @test_generic_partial_apply_apply : $@convention(thin) <T> (@in T, @in T) -> () {
3107-
bb0(%0 : $*T, %1 : $*T):
3108-
%f1 = function_ref @generic_callee : $@convention(thin) <T, U> (@in T, @in U) -> ()
3109-
%pa = partial_apply %f1<T, T>(%1) : $@convention(thin) <T, U> (@in T, @in U) -> ()
3110-
%a1 = apply %pa(%0) : $@callee_owned (@in T) -> ()
3111-
%r = tuple ()
3112-
return %r : $()
3113-
}
3114-
3115-
// CHECK-LABEL: sil @test_existential_partial_apply_apply
3116-
// CHECK: bb0(%0 : $*FakeProtocol):
3117-
// CHECK-NEXT: [[OPEN:%.*]] = open_existential_addr immutable_access
3118-
// CHECK-NEXT: [[FN:%.*]] = witness_method
3119-
// CHECK-NEXT: apply [[FN]]<@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol>([[OPEN]])
3120-
// CHECK-NEXT: tuple
3121-
// CHECK-NEXT: return
3122-
sil @test_existential_partial_apply_apply : $@convention(thin) (@in FakeProtocol) -> () {
3123-
bb0(%0: $*FakeProtocol):
3124-
%o = open_existential_addr immutable_access %0 : $*FakeProtocol to $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol
3125-
%f1 = witness_method $@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol, #FakeProtocol.requirement!1, %o : $*@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol : $@convention(witness_method: FakeProtocol) <T where T : FakeProtocol> (@in_guaranteed T) -> ()
3126-
%pa = partial_apply %f1<@opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol>() : $@convention(witness_method: FakeProtocol) <T where T : FakeProtocol> (@in_guaranteed T) -> ()
3127-
%a1 = apply %pa(%o) : $@callee_owned (@in_guaranteed @opened("5DD6F3D0-808A-11E6-93A0-34363BD08DA0") FakeProtocol) -> ()
3128-
3129-
%r = tuple ()
3130-
return %r : $()
3131-
}
3132-
31332908
sil @callee : $@convention(thin) (Double, @in_guaranteed Int) -> ()
31342909
sil @generic_callee : $@convention(thin) <T, U> (@in T, @in U) -> ()
31352910
sil @noreturn_func : $@convention(thin) () -> Never

0 commit comments

Comments
 (0)