Skip to content

Commit 5e206f3

Browse files
committed
SILGen: Generate closure contexts as guaranteed.
When enabled, generate closure functions with guaranteed conventions as their context parameters, and pass context arguments to them as guaranteed when possible. (When forming a closure by partial_apply, the partial apply still needs to take ownership of the parameters, regardless of their convention.)
1 parent f4ccef2 commit 5e206f3

File tree

8 files changed

+146
-55
lines changed

8 files changed

+146
-55
lines changed

include/swift/SIL/TypeLowering.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ enum class CaptureKind {
420420
/// A local value captured as a single pointer to storage (formed with
421421
/// @noescape closures).
422422
StorageAddress,
423-
// A local value captures as a constant.
423+
/// A local value captured as a constant.
424424
Constant,
425425
};
426426

lib/IRGen/GenFunc.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,19 +1366,8 @@ void SignatureExpansion::expand(SILParameterInfo param) {
13661366

13671367
case ParameterConvention::Direct_Owned:
13681368
case ParameterConvention::Direct_Unowned:
1369-
case ParameterConvention::Direct_Guaranteed: /*
1370-
// Go ahead and further decompose tuples.
1371-
if (auto tuple = dyn_cast<TupleType>(param.getType())) {
1372-
for (auto elt : tuple.getElementTypes()) {
1373-
// Propagate the same ownedness down to the element.
1374-
expand(SILParameterInfo(elt, param.getConvention()));
1375-
}
1376-
return;
1377-
}
1378-
*/
1379-
SWIFT_FALLTHROUGH;
1369+
case ParameterConvention::Direct_Guaranteed:
13801370
case ParameterConvention::Direct_Deallocating:
1381-
13821371
switch (FnType->getLanguage()) {
13831372
case SILFunctionLanguage::C: {
13841373
llvm_unreachable("Unexpected C/ObjC method in parameter expansion!");

lib/SIL/SILFunctionType.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -585,27 +585,30 @@ static CanSILFunctionType getSILFunctionType(SILModule &M,
585585
break;
586586
case CaptureKind::Constant: {
587587
// Constants are captured by value.
588-
SILParameterInfo param;
588+
ParameterConvention convention;
589589
if (loweredTL.isAddressOnly()) {
590-
param = SILParameterInfo(loweredTy.getSwiftRValueType(),
591-
ParameterConvention::Indirect_In);
592-
590+
convention = M.getOptions().EnableGuaranteedClosureContexts
591+
? ParameterConvention::Indirect_In_Guaranteed
592+
: ParameterConvention::Indirect_In;
593593
} else if (loweredTL.isTrivial()) {
594-
param = SILParameterInfo(loweredTy.getSwiftRValueType(),
595-
ParameterConvention::Direct_Unowned);
594+
convention = ParameterConvention::Direct_Unowned;
596595
} else {
597-
param = SILParameterInfo(loweredTy.getSwiftRValueType(),
598-
ParameterConvention::Direct_Owned);
596+
convention = M.getOptions().EnableGuaranteedClosureContexts
597+
? ParameterConvention::Direct_Guaranteed
598+
: ParameterConvention::Direct_Owned;
599599
}
600+
SILParameterInfo param(loweredTy.getSwiftRValueType(), convention);
600601
inputs.push_back(param);
601602
break;
602603
}
603604
case CaptureKind::Box: {
604605
// Lvalues are captured as a box that owns the captured value.
605606
SILType ty = loweredTy.getAddressType();
606607
CanType boxTy = SILBoxType::get(ty.getSwiftRValueType());
607-
auto param = SILParameterInfo(boxTy,
608-
ParameterConvention::Direct_Owned);
608+
auto convention = M.getOptions().EnableGuaranteedClosureContexts
609+
? ParameterConvention::Direct_Guaranteed
610+
: ParameterConvention::Direct_Owned;
611+
auto param = SILParameterInfo(boxTy, convention);
609612
inputs.push_back(param);
610613
break;
611614
}

lib/SILGen/SILGenApply.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,7 +1168,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11681168
&& "generic local fns not implemented");
11691169

11701170
SmallVector<ManagedValue, 4> captures;
1171-
SGF.emitCaptures(e, afd, captures);
1171+
SGF.emitCaptures(e, afd, CaptureEmission::ImmediateApplication,
1172+
captures);
11721173
ApplyCallee->setCaptures(std::move(captures));
11731174
}
11741175

@@ -1214,7 +1215,8 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
12141215
// If the closure requires captures, emit them.
12151216
if (e->getCaptureInfo().hasLocalCaptures()) {
12161217
SmallVector<ManagedValue, 4> captures;
1217-
SGF.emitCaptures(e, e, captures);
1218+
SGF.emitCaptures(e, e, CaptureEmission::ImmediateApplication,
1219+
captures);
12181220
ApplyCallee->setCaptures(std::move(captures));
12191221
}
12201222
// If there are substitutions, add them, always at depth 0.
@@ -3840,7 +3842,8 @@ emitSpecializedAccessorFunctionRef(SILGenFunction &gen,
38403842
if (accessorFn->getCaptureInfo().hasLocalCaptures()) {
38413843
assert(!selfValue && "local property has self param?!");
38423844
SmallVector<ManagedValue, 4> captures;
3843-
gen.emitCaptures(loc, accessorFn, captures);
3845+
gen.emitCaptures(loc, accessorFn, CaptureEmission::ImmediateApplication,
3846+
captures);
38443847
callee.setCaptures(std::move(captures));
38453848
}
38463849

lib/SILGen/SILGenFunction.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,28 @@ ManagedValue SILGenFunction::emitFunctionRef(SILLocation loc,
220220

221221
void SILGenFunction::emitCaptures(SILLocation loc,
222222
AnyFunctionRef TheClosure,
223+
CaptureEmission purpose,
223224
SmallVectorImpl<ManagedValue> &capturedArgs) {
224225
auto captureInfo = SGM.Types.getLoweredLocalCaptures(TheClosure);
225226
// For boxed captures, we need to mark the contained variables as having
226227
// escaped for DI diagnostics.
227228
SmallVector<SILValue, 2> escapesToMark;
228229

230+
// Partial applications take ownership of the context parameters, so we'll
231+
// need to pass ownership rather than merely guaranteeing parameters.
232+
bool canGuarantee;
233+
switch (purpose) {
234+
case CaptureEmission::PartialApplication:
235+
canGuarantee = false;
236+
break;
237+
case CaptureEmission::ImmediateApplication:
238+
canGuarantee = true;
239+
break;
240+
}
241+
// TODO: Or we always retain them when guaranteed contexts aren't enabled.
242+
if (!SGM.M.getOptions().EnableGuaranteedClosureContexts)
243+
canGuarantee = false;
244+
229245
for (auto capture : captureInfo.getCaptures()) {
230246
auto *vd = capture.getDecl();
231247

@@ -237,11 +253,18 @@ void SILGenFunction::emitCaptures(SILLocation loc,
237253
// let declarations.
238254
auto Entry = VarLocs[vd];
239255

240-
// Non-address-only constants are passed at +1.
241256
auto &tl = getTypeLowering(vd->getType()->getReferenceStorageReferent());
242257
SILValue Val = Entry.value;
243258

244259
if (!Val.getType().isAddress()) {
260+
// Our 'let' binding can guarantee the lifetime for the callee,
261+
// if we don't need to do anything more to it.
262+
if (canGuarantee && !vd->getType()->is<ReferenceStorageType>()) {
263+
auto guaranteed = ManagedValue::forUnmanaged(Val);
264+
capturedArgs.push_back(guaranteed);
265+
break;
266+
}
267+
245268
// Just retain a by-val let.
246269
B.emitRetainValueOperation(loc, Val);
247270
} else {
@@ -281,15 +304,25 @@ void SILGenFunction::emitCaptures(SILLocation loc,
281304

282305
// If this is a boxed variable, we can use it directly.
283306
if (vl.box) {
284-
B.createStrongRetain(loc, vl.box);
285-
capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box));
307+
// We can guarantee our own box to the callee.
308+
if (canGuarantee) {
309+
capturedArgs.push_back(ManagedValue::forUnmanaged(vl.box));
310+
} else {
311+
B.createStrongRetain(loc, vl.box);
312+
capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box));
313+
}
286314
escapesToMark.push_back(vl.value);
287315
} else {
288316
// Address only 'let' values are passed by box. This isn't great, in
289317
// that a variable captured by multiple closures will be boxed for each
290318
// one. This could be improved by doing an "isCaptured" analysis when
291319
// emitting address-only let constants, and emit them into an alloc_box
292320
// like a variable instead of into an alloc_stack.
321+
//
322+
// TODO: This might not be profitable anymore with guaranteed captures,
323+
// since we could conceivably forward the copied value into the
324+
// closure context and pass it down to the partially applied function
325+
// in-place.
293326
AllocBoxInst *allocBox =
294327
B.createAllocBox(loc, vl.value.getType().getObjectType());
295328
auto boxAddress = SILValue(allocBox, 1);
@@ -348,10 +381,10 @@ SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant,
348381
}
349382

350383
SmallVector<ManagedValue, 4> capturedArgs;
351-
emitCaptures(loc, TheClosure, capturedArgs);
384+
emitCaptures(loc, TheClosure, CaptureEmission::PartialApplication,
385+
capturedArgs);
352386

353-
// Currently all capture arguments are captured at +1.
354-
// TODO: Ideally this would be +0.
387+
// The partial application takes ownership of the context parameters.
355388
SmallVector<SILValue, 4> forwardedArgs;
356389
for (auto capture : capturedArgs)
357390
forwardedArgs.push_back(capture.forward(*this));

lib/SILGen/SILGenFunction.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,16 @@ class SILGenBuilder : public SILBuilder {
257257
ArrayRef<ProtocolConformance *> Conformances);
258258
};
259259

260+
/// Parameter to \c SILGenFunction::emitCaptures that indicates what the
261+
/// capture parameters are being emitted for.
262+
enum class CaptureEmission {
263+
/// Captures are being emitted for immediate application to a local function.
264+
ImmediateApplication,
265+
/// Captures are being emitted for partial application to form a closure
266+
/// value.
267+
PartialApplication,
268+
};
269+
260270
/// SILGenFunction - an ASTVisitor for producing SIL from function bodies.
261271
class LLVM_LIBRARY_VISIBILITY SILGenFunction
262272
: public ASTVisitor<SILGenFunction>
@@ -1031,6 +1041,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
10311041

10321042
void emitCaptures(SILLocation loc,
10331043
AnyFunctionRef TheClosure,
1044+
CaptureEmission purpose,
10341045
SmallVectorImpl<ManagedValue> &captures);
10351046

10361047
ManagedValue emitClosureValue(SILLocation loc,

lib/SILGen/SILGenProlog.cpp

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -420,27 +420,6 @@ void SILGenFunction::bindParametersForForwarding(Pattern *pattern,
420420
ArgumentForwardVisitor(*this, parameters).visit(pattern);
421421
}
422422

423-
/// Tuple values captured by a closure are passed as individual arguments to the
424-
/// SILFunction since SILFunctionType canonicalizes away tuple types.
425-
static SILValue
426-
emitReconstitutedConstantCaptureArguments(SILType ty,
427-
ValueDecl *capture,
428-
SILGenFunction &gen) {
429-
auto TT = ty.getAs<TupleType>();
430-
if (!TT)
431-
return new (gen.SGM.M) SILArgument(gen.F.begin(), ty, capture);
432-
433-
SmallVector<SILValue, 4> Elts;
434-
for (unsigned i = 0, e = TT->getNumElements(); i != e; ++i) {
435-
auto EltTy = ty.getTupleElementType(i);
436-
auto EV =
437-
emitReconstitutedConstantCaptureArguments(EltTy, capture, gen);
438-
Elts.push_back(EV);
439-
}
440-
441-
return gen.B.createTuple(capture, ty, Elts);
442-
}
443-
444423
static void emitCaptureArguments(SILGenFunction &gen, CapturedValue capture,
445424
unsigned ArgNo) {
446425
auto *VD = capture.getDecl();
@@ -472,7 +451,10 @@ static void emitCaptureArguments(SILGenFunction &gen, CapturedValue capture,
472451
AllocStack->setArgNo(ArgNo);
473452
else
474453
gen.B.createDebugValue(Loc, val, {/*Constant*/true, ArgNo});
475-
if (!lowering.isTrivial())
454+
455+
// TODO: Closure contexts should always be guaranteed.
456+
if (!gen.SGM.M.getOptions().EnableGuaranteedClosureContexts
457+
&& !lowering.isTrivial())
476458
gen.enterDestroyCleanup(val);
477459
break;
478460
}
@@ -487,7 +469,8 @@ static void emitCaptureArguments(SILGenFunction &gen, CapturedValue capture,
487469
SILValue addr = gen.B.createProjectBox(VD, box);
488470
gen.VarLocs[VD] = SILGenFunction::VarLoc::get(addr, box);
489471
gen.B.createDebugValueAddr(Loc, addr, {/*Constant*/false, ArgNo});
490-
gen.Cleanups.pushCleanup<StrongReleaseCleanup>(box);
472+
if (!gen.SGM.M.getOptions().EnableGuaranteedClosureContexts)
473+
gen.Cleanups.pushCleanup<StrongReleaseCleanup>(box);
491474
break;
492475
}
493476
case CaptureKind::StorageAddress: {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %target-swift-frontend -parse-as-library -emit-silgen -enable-guaranteed-closure-contexts %s | FileCheck %s
2+
3+
func use<T>(_: T) {}
4+
5+
func escape(f: () -> ()) {}
6+
7+
protocol P {}
8+
class C: P {}
9+
struct S {}
10+
11+
// CHECK-LABEL: sil hidden @_TF26guaranteed_closure_context19guaranteed_capturesFT_T_
12+
func guaranteed_captures() {
13+
// CHECK: [[MUTABLE_TRIVIAL_BOX:%.*]] = alloc_box $S
14+
var mutableTrivial = S()
15+
// CHECK: [[MUTABLE_RETAINABLE_BOX:%.*]] = alloc_box $C
16+
var mutableRetainable = C()
17+
// CHECK: [[MUTABLE_ADDRESS_ONLY_BOX:%.*]] = alloc_box $P
18+
var mutableAddressOnly: P = C()
19+
20+
// CHECK: [[IMMUTABLE_TRIVIAL:%.*]] = apply {{.*}} -> S
21+
let immutableTrivial = S()
22+
// CHECK: [[IMMUTABLE_RETAINABLE:%.*]] = apply {{.*}} -> @owned C
23+
let immutableRetainable = C()
24+
// CHECK: [[IMMUTABLE_ADDRESS_ONLY:%.*]] = alloc_stack $P
25+
let immutableAddressOnly: P = C()
26+
27+
func captureEverything() {
28+
use((mutableTrivial, mutableRetainable, mutableAddressOnly,
29+
immutableTrivial, immutableRetainable, immutableAddressOnly))
30+
}
31+
32+
// CHECK-NOT: strong_retain [[MUTABLE_TRIVIAL_BOX]]
33+
// CHECK-NOT: strong_retain [[MUTABLE_RETAINABLE_BOX]]
34+
// CHECK-NOT: strong_retain [[MUTABLE_ADDRESS_ONLY_BOX]]
35+
// CHECK-NOT: strong_retain [[IMMUTABLE_RETAINABLE]]
36+
// CHECK: [[IMMUTABLE_AO_BOX:%.*]] = alloc_box $P
37+
38+
// CHECK: [[FN:%.*]] = function_ref [[FN_NAME:@_TFF26guaranteed_closure_context19guaranteed_capturesFT_T_L_17captureEverythingfT_T_]]
39+
// CHECK: apply [[FN]]([[MUTABLE_TRIVIAL_BOX]]#0, [[MUTABLE_RETAINABLE_BOX]]#0, [[MUTABLE_ADDRESS_ONLY_BOX]]#0, [[IMMUTABLE_TRIVIAL]], [[IMMUTABLE_RETAINABLE]], [[IMMUTABLE_AO_BOX]]#0)
40+
captureEverything()
41+
42+
// CHECK: strong_release [[IMMUTABLE_AO_BOX]]
43+
44+
// CHECK-NOT: strong_retain [[MUTABLE_TRIVIAL_BOX]]
45+
// CHECK-NOT: strong_retain [[MUTABLE_RETAINABLE_BOX]]
46+
// CHECK-NOT: strong_retain [[MUTABLE_ADDRESS_ONLY_BOX]]
47+
// CHECK-NOT: strong_retain [[IMMUTABLE_RETAINABLE]]
48+
49+
// -- partial_apply still takes ownership of its arguments.
50+
// CHECK: [[FN:%.*]] = function_ref [[FN_NAME]]
51+
// CHECK: strong_retain [[MUTABLE_TRIVIAL_BOX]]
52+
// CHECK: strong_retain [[MUTABLE_RETAINABLE_BOX]]
53+
// CHECK: strong_retain [[MUTABLE_ADDRESS_ONLY_BOX]]
54+
// CHECK: strong_retain [[IMMUTABLE_RETAINABLE]]
55+
// CHECK: [[IMMUTABLE_AO_BOX:%.*]] = alloc_box $P
56+
// CHECK: [[CLOSURE:%.*]] = partial_apply {{.*}}([[MUTABLE_TRIVIAL_BOX]]#0, [[MUTABLE_RETAINABLE_BOX]]#0, [[MUTABLE_ADDRESS_ONLY_BOX]]#0, [[IMMUTABLE_TRIVIAL]], [[IMMUTABLE_RETAINABLE]], [[IMMUTABLE_AO_BOX]]#0)
57+
// CHECK: apply {{.*}}[[CLOSURE]]
58+
59+
// CHECK-NOT: strong_retain [[MUTABLE_TRIVIAL_BOX]]
60+
// CHECK-NOT: strong_retain [[MUTABLE_RETAINABLE_BOX]]
61+
// CHECK-NOT: strong_retain [[MUTABLE_ADDRESS_ONLY_BOX]]
62+
// CHECK-NOT: strong_retain [[IMMUTABLE_RETAINABLE]]
63+
// CHECK-NOT: strong_release [[IMMUTABLE_AO_BOX]]
64+
65+
escape(captureEverything)
66+
67+
}
68+
69+
// CHECK: sil shared [[FN_NAME]] : $@convention(thin) (@guaranteed @box S, @guaranteed @box C, @guaranteed @box P, S, @guaranteed C, @guaranteed @box P)

0 commit comments

Comments
 (0)