Skip to content

Commit 852911c

Browse files
committed
[silgen] Add support for emitting ImmutableBoxes.
1 parent 8a194fa commit 852911c

File tree

4 files changed

+86
-8
lines changed

4 files changed

+86
-8
lines changed

include/swift/SIL/TypeLowering.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,8 @@ struct SILConstantInfo {
665665
enum class CaptureKind {
666666
/// A local value captured as a mutable box.
667667
Box,
668+
/// A local value captured as an immutable box.
669+
ImmutableBox,
668670
/// A local value captured as a single pointer to storage (formed with
669671
/// @noescape closures).
670672
StorageAddress,
@@ -674,7 +676,6 @@ enum class CaptureKind {
674676
Immutable
675677
};
676678

677-
678679
/// TypeConverter - helper class for creating and managing TypeLowerings.
679680
class TypeConverter {
680681
friend class TypeLowering;

lib/SIL/IR/SILFunctionType.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,21 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
18531853
inputs.push_back(param);
18541854
break;
18551855
}
1856+
case CaptureKind::ImmutableBox: {
1857+
// The type in the box is lowered in the minimal context.
1858+
auto minimalLoweredTy =
1859+
TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType,
1860+
TypeExpansionContext::minimal())
1861+
.getLoweredType();
1862+
// Lvalues are captured as a box that owns the captured value.
1863+
auto boxTy =
1864+
TC.getInterfaceBoxTypeForCapture(VD, minimalLoweredTy.getASTType(),
1865+
/*mutable*/ false);
1866+
auto convention = ParameterConvention::Direct_Guaranteed;
1867+
auto param = SILParameterInfo(boxTy, convention);
1868+
inputs.push_back(param);
1869+
break;
1870+
}
18561871
case CaptureKind::StorageAddress: {
18571872
// Non-escaping lvalues are captured as the address of the value.
18581873
SILType ty = loweredTy.getAddressType();

lib/SILGen/SILGenFunction.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,8 @@ void SILGenFunction::emitCaptures(SILLocation loc,
326326
Diags.diagnose(capture.getLoc(), diag::value_captured_here);
327327

328328
// Emit an 'undef' of the correct type.
329-
switch (SGM.Types.getDeclCaptureKind(capture, expansion)) {
329+
auto captureKind = SGM.Types.getDeclCaptureKind(capture, expansion);
330+
switch (captureKind) {
330331
case CaptureKind::Constant:
331332
capturedArgs.push_back(emitUndef(getLoweredType(type)));
332333
break;
@@ -338,13 +339,15 @@ void SILGenFunction::emitCaptures(SILLocation loc,
338339
capturedArgs.push_back(emitUndef(ty));
339340
break;
340341
}
342+
case CaptureKind::ImmutableBox:
341343
case CaptureKind::Box: {
344+
bool isMutable = captureKind == CaptureKind::Box;
342345
auto boxTy = SGM.Types.getContextBoxTypeForCapture(
343346
vd,
344347
SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
345348
type),
346349
FunctionDC->getGenericEnvironmentOfContext(),
347-
/*mutable*/ true);
350+
/*mutable*/ isMutable);
348351
capturedArgs.push_back(emitUndef(boxTy));
349352
break;
350353
}
@@ -497,6 +500,57 @@ void SILGenFunction::emitCaptures(SILLocation loc,
497500

498501
break;
499502
}
503+
case CaptureKind::ImmutableBox: {
504+
auto entryValue = getAddressValue(Entry.value);
505+
// LValues are captured as both the box owning the value and the
506+
// address of the value.
507+
assert(entryValue->getType().isAddress() &&
508+
"no address for captured var!");
509+
// Boxes of opaque return values stay opaque.
510+
auto minimalLoweredType = SGM.Types.getLoweredRValueType(
511+
TypeExpansionContext::minimal(), type->getCanonicalType());
512+
// If this is a boxed variable, we can use it directly.
513+
if (Entry.box &&
514+
entryValue->getType().getASTType() == minimalLoweredType) {
515+
// We can guarantee our own box to the callee.
516+
if (canGuarantee) {
517+
capturedArgs.push_back(
518+
ManagedValue::forUnmanaged(Entry.box).borrow(*this, loc));
519+
} else {
520+
capturedArgs.push_back(emitManagedRetain(loc, Entry.box));
521+
}
522+
if (captureCanEscape)
523+
escapesToMark.push_back(entryValue);
524+
} else {
525+
// Address only 'let' values are passed by box. This isn't great, in
526+
// that a variable captured by multiple closures will be boxed for each
527+
// one. This could be improved by doing an "isCaptured" analysis when
528+
// emitting address-only let constants, and emit them into an alloc_box
529+
// like a variable instead of into an alloc_stack.
530+
//
531+
// TODO: This might not be profitable anymore with guaranteed captures,
532+
// since we could conceivably forward the copied value into the
533+
// closure context and pass it down to the partially applied function
534+
// in-place.
535+
// TODO: Use immutable box for immutable captures.
536+
auto boxTy = SGM.Types.getContextBoxTypeForCapture(
537+
vd, minimalLoweredType,
538+
FunctionDC->getGenericEnvironmentOfContext(),
539+
/*mutable*/ false);
540+
541+
AllocBoxInst *allocBox = B.createAllocBox(loc, boxTy);
542+
ProjectBoxInst *boxAddress = B.createProjectBox(loc, allocBox, 0);
543+
B.createCopyAddr(loc, entryValue, boxAddress, IsNotTake,
544+
IsInitialization);
545+
if (canGuarantee)
546+
capturedArgs.push_back(
547+
emitManagedRValueWithCleanup(allocBox).borrow(*this, loc));
548+
else
549+
capturedArgs.push_back(emitManagedRValueWithCleanup(allocBox));
550+
}
551+
552+
break;
553+
}
500554
}
501555
}
502556

lib/SILGen/SILGenProlog.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,8 @@ static void emitCaptureArguments(SILGenFunction &SGF,
517517
};
518518

519519
auto expansion = SGF.getTypeExpansionContext();
520-
switch (SGF.SGM.Types.getDeclCaptureKind(capture, expansion)) {
520+
auto captureKind = SGF.SGM.Types.getDeclCaptureKind(capture, expansion);
521+
switch (captureKind) {
521522
case CaptureKind::Constant: {
522523
auto type = getVarTypeInCaptureContext();
523524
auto &lowering = SGF.getTypeLowering(type);
@@ -569,24 +570,31 @@ static void emitCaptureArguments(SILGenFunction &SGF,
569570
break;
570571
}
571572

573+
case CaptureKind::ImmutableBox:
572574
case CaptureKind::Box: {
573575
// LValues are captured as a retained @box that owns
574576
// the captured value.
575577
auto type = getVarTypeInCaptureContext();
576578
// Get the content for the box in the minimal resilience domain because we
577579
// are declaring a type.
580+
bool isMutable = captureKind != CaptureKind::ImmutableBox;
578581
auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(
579582
VD,
580583
SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
581584
type),
582-
SGF.F.getGenericEnvironment(), /*mutable*/ true);
585+
SGF.F.getGenericEnvironment(), /*mutable*/ isMutable);
583586
auto *box = SGF.F.begin()->createFunctionArgument(
584587
SILType::getPrimitiveObjectType(boxTy), VD);
585588
box->setClosureCapture(true);
586589
SILValue addr = SGF.B.createProjectBox(VD, box, 0);
587-
if (addr->getType().isMoveOnly())
588-
addr = SGF.B.createMarkMustCheckInst(
589-
VD, addr, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
590+
if (addr->getType().isMoveOnly()) {
591+
if (isMutable)
592+
addr = SGF.B.createMarkMustCheckInst(
593+
VD, addr, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
594+
else
595+
addr = SGF.B.createMarkMustCheckInst(
596+
VD, addr, MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
597+
}
590598
SGF.VarLocs[VD] = SILGenFunction::VarLoc::get(addr, box);
591599
SILDebugVariable DbgVar(VD->isLet(), ArgNo);
592600
SGF.B.createDebugValueAddr(Loc, addr, DbgVar);

0 commit comments

Comments
 (0)