Skip to content

Commit ddeab73

Browse files
authored
Merge pull request #67239 from jckarter/reabstracted-closure-literal-inout-reabstraction
SILGen: Provide a reabstracted writeback for inout parameters emitted in a more abstract closure literal context.
2 parents f423115 + 9d2735e commit ddeab73

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)