Skip to content

Commit 9d2735e

Browse files
committed
SILGen: Provide a reabstracted writeback for inout parameters emitted in a more abstract closure literal context.
When we emit closure literals in the context of a generic parameter, the parameters of the closure may be more abstract than they would normally be for the closure's abstract type. We handled this by reabstracting value parameters to their usual substituted representation in the prolog, but neglected to do so for `inout` parameters too. In this case, we need to take the initial value, reabstract it to provide a local variable with the substituted representation, and then take the final value, reabstract it back, and write back to the original inout parameter on function exit. Fixes rdar://111563642.
1 parent b1f36d2 commit 9d2735e

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

lib/SILGen/SILGenProlog.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,39 @@ struct LoweredParamGenerator {
130130
}
131131
};
132132

133+
struct WritebackReabstractedInoutCleanup final : Cleanup {
134+
SILValue OrigAddress, SubstAddress;
135+
AbstractionPattern OrigTy;
136+
CanType SubstTy;
137+
WritebackReabstractedInoutCleanup(SILValue origAddress, SILValue substAddress,
138+
AbstractionPattern origTy,
139+
CanType substTy)
140+
: OrigAddress(origAddress), SubstAddress(substAddress),
141+
OrigTy(origTy), SubstTy(substTy)
142+
{}
143+
144+
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind)
145+
override {
146+
Scope s(SGF.Cleanups, l);
147+
// Load the final local value coming in.
148+
auto mv = SGF.emitLoad(l, SubstAddress,
149+
SGF.getTypeLowering(SubstAddress->getType()),
150+
SGFContext(), IsTake);
151+
// Reabstract the value back to the original representation.
152+
mv = SGF.emitSubstToOrigValue(l, mv.ensurePlusOne(SGF, l),
153+
OrigTy, SubstTy);
154+
// Write it back to the original inout parameter.
155+
SGF.B.createStore(l, mv.forward(SGF), OrigAddress,
156+
StoreOwnershipQualifier::Init);
157+
}
158+
159+
void dump(SILGenFunction&) const override {
160+
llvm::errs() << "WritebackReabstractedInoutCleanup\n";
161+
OrigAddress->print(llvm::errs());
162+
SubstAddress->print(llvm::errs());
163+
}
164+
};
165+
133166
class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
134167
/*RetTy*/ ManagedValue,
135168
/*ArgTys...*/ AbstractionPattern,
@@ -255,6 +288,23 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
255288
if (mv.getType().isMoveOnly() && !mv.getType().isMoveOnlyWrapped())
256289
mv = SGF.B.createMarkMustCheckInst(
257290
loc, mv, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
291+
292+
// If the value needs to be reabstracted, set up a shadow copy with
293+
// writeback here.
294+
if (argType.getASTType() != mv.getType().getASTType()) {
295+
// Load the value coming in.
296+
auto origBuf = mv.getValue();
297+
mv = SGF.emitLoad(loc, origBuf, SGF.getTypeLowering(mv.getType()), SGFContext(), IsTake);
298+
// Reabstract the value if necessary.
299+
mv = SGF.emitOrigToSubstValue(loc, mv.ensurePlusOne(SGF, loc), orig, t);
300+
// Store the value to a local buffer.
301+
auto substBuf = SGF.emitTemporaryAllocation(loc, argType);
302+
SGF.B.createStore(loc, mv.forward(SGF), substBuf, StoreOwnershipQualifier::Init);
303+
// Introduce a writeback to put the final value back in the inout.
304+
SGF.Cleanups.pushCleanup<WritebackReabstractedInoutCleanup>(origBuf, substBuf, orig, t);
305+
mv = ManagedValue::forLValue(substBuf);
306+
}
307+
258308
return mv;
259309
}
260310

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %target-swift-emit-silgen %s
2+
3+
func bar<T>(f: (inout T) throws -> Void) {}
4+
5+
func condition() -> Bool { fatalError() }
6+
func error() -> Error { fatalError() }
7+
8+
func foo(x: @escaping () -> Void) {
9+
// CHECK-LABEL: sil private [ossa] @${{.*}}3foo{{.*}}U_ :
10+
// CHECK: bb0(
11+
// CHECK-SAME: [[ARG:%.*]] = $*@callee_guaranteed @substituted <τ_0_0> () -> @out τ_0_0 for <()>,
12+
// CHECK-SAME: [[CAPTURE:%.*]] = @closureCapture @guaranteed $@callee_guaranteed () -> ()
13+
14+
// Reabstract the initial value of the inout parameter
15+
// CHECK: [[INITIAL:%.*]] = load [take] [[ARG]]
16+
// CHECK: [[INITIAL_CONV:%.*]] = convert_function [[INITIAL]]
17+
// CHECK: [[THUNK:%.*]] = function_ref @{{.*}}_TR
18+
// CHECK: [[INITIAL_REAB:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[INITIAL_CONV]])
19+
// CHECK: [[REAB_ARG:%.*]] = alloc_stack
20+
// CHECK: store [[INITIAL_REAB]] to [[REAB_ARG]]
21+
bar {
22+
// Read from the reabstracted buffer
23+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[REAB_ARG]]
24+
// CHECK: [[COPY]] = load [copy] [[ACCESS]]
25+
_ = $0 as () -> Void
26+
27+
// Assign to the reabstracted buffer
28+
$0 = x
29+
30+
// Writeback occurs no matter how we exit the function
31+
// CHECK: cond_br {{%.*}}, [[THEN1:bb[0-9]+]], [[ELSE1:bb[0-9]+]]
32+
if condition() {
33+
// Throwing:
34+
// CHECK: [[THEN1]]:
35+
// Take final reabstracted value from the reabstracted buffer
36+
// CHECK: [[FINAL_REAB:%.*]] = load [take] [[REAB_ARG]]
37+
// Reabstract back to original representation and store back to
38+
// original buffer
39+
// CHECK: [[THUNK_ORIG:%.*]] = function_ref @{{.*}}_TR
40+
// CHECK: [[FINAL_CONV:%.*]] = partial_apply [callee_guaranteed] [[THUNK_ORIG]]([[FINAL_REAB]])
41+
// CHECK: [[FINAL:%.*]] = convert_function [[FINAL_CONV]]
42+
// CHECK: store [[FINAL]] to [[ARG]]
43+
// CHECK: throw
44+
throw error()
45+
// CHECK: [[ELSE1]]:
46+
// CHECK: cond_br {{%.*}}, [[THEN2:bb[0-9]+]], [[ELSE2:bb[0-9]+]]
47+
} else if condition() {
48+
// Explicit return
49+
// CHECK: [[THEN1]]:
50+
// CHECK: br [[EPILOG:bb[0-9]+]]
51+
return
52+
} else {
53+
// Fall through
54+
}
55+
// CHECK: [[EPILOG]]:
56+
// Take final reabstracted value from the reabstracted buffer
57+
// CHECK: [[FINAL_REAB:%.*]] = load [take] [[REAB_ARG]]
58+
// Reabstract back to original representation and store back to
59+
// original buffer
60+
// CHECK: [[THUNK_ORIG:%.*]] = function_ref @{{.*}}_TR
61+
// CHECK: [[FINAL_CONV:%.*]] = partial_apply [callee_guaranteed] [[THUNK_ORIG]]([[FINAL_REAB]])
62+
// CHECK: [[FINAL:%.*]] = convert_function [[FINAL_CONV]]
63+
// CHECK: store [[FINAL]] to [[ARG]]
64+
// CHECK: return
65+
}
66+
}

0 commit comments

Comments
 (0)