Skip to content

Commit 19a99ea

Browse files
committed
SILGen: Emit an addressable representation for immutable bindings on demand.
To ensure that dependent values have a persistent-enough memory representation to point into, when an immutable binding is referenced as an addressable argument to a call, have SILGen retroactively emit a stack allocation and materialization that covers the binding's scope.
1 parent ecaa041 commit 19a99ea

14 files changed

+625
-72
lines changed

include/swift/AST/Types.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,9 +4337,7 @@ inline bool isConsumedParameterInCaller(ParameterConvention conv) {
43374337
return isConsumedParameter<false>(conv);
43384338
}
43394339

4340-
/// Returns true if conv is a guaranteed parameter. This may look unnecessary
4341-
/// but this will allow code to generalize to handle Indirect_Guaranteed
4342-
/// parameters when they are added.
4340+
/// Returns true if conv is a guaranteed parameter.
43434341
template <bool InCallee>
43444342
bool isGuaranteedParameter(ParameterConvention conv) {
43454343
switch (conv) {

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,9 +1679,7 @@ AbstractionPattern::isFunctionParamAddressable(TypeConverter &TC,
16791679
auto type = getType();
16801680

16811681
if (type->isTypeParameter() || type->is<ArchetypeType>()) {
1682-
// If the function abstraction pattern is completely opaque, assume we
1683-
// may need to preserve the address for dependencies.
1684-
return true;
1682+
return false;
16851683
}
16861684

16871685
auto fnTy = cast<AnyFunctionType>(getType());

lib/SILGen/SILGenApply.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,11 +3462,9 @@ SILGenFunction::tryEmitAddressableParameterAsAddress(ArgumentSource &&arg,
34623462
expr = le->getSubExpr();
34633463
}
34643464
if (auto dre = dyn_cast<DeclRefExpr>(expr)) {
3465-
if (auto param = dyn_cast<ParamDecl>(dre->getDecl())) {
3466-
if (VarLocs.count(param)
3467-
&& VarLocs[param].addressable
3468-
&& param->getValueOwnership() == ownership) {
3469-
auto addr = VarLocs[param].value;
3465+
if (auto param = dyn_cast<VarDecl>(dre->getDecl())) {
3466+
if (auto addr = getLocalVariableAddressableBuffer(param, expr,
3467+
ownership)) {
34703468
return ManagedValue::forBorrowedAddressRValue(addr);
34713469
}
34723470
}
@@ -3577,12 +3575,20 @@ class ArgEmitter {
35773575
void emit(ArgumentSource &&arg, AbstractionPattern origParamType,
35783576
bool isAddressable,
35793577
std::optional<AnyFunctionType::Param> origParam = std::nullopt) {
3580-
if (isAddressable && origParam) {
3578+
if (isAddressable) {
35813579
// If the function takes an addressable parameter, and its argument is
35823580
// a reference to an addressable declaration with compatible ownership,
35833581
// forward the address along in-place.
3582+
ValueOwnership paramOwnership;
3583+
if (isGuaranteedParameterInCaller(ParamInfos.front().getConvention())) {
3584+
paramOwnership = ValueOwnership::Shared;
3585+
} else if (isMutatingParameter(ParamInfos.front().getConvention())) {
3586+
paramOwnership = ValueOwnership::InOut;
3587+
} else {
3588+
paramOwnership = ValueOwnership::Owned;
3589+
}
35843590
if (auto addr = SGF.tryEmitAddressableParameterAsAddress(std::move(arg),
3585-
origParam->getValueOwnership())) {
3591+
paramOwnership)) {
35863592
claimNextParameters(1);
35873593
Args.push_back(addr);
35883594
return;

lib/SILGen/SILGenBuilder.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,18 @@ class EndAccessCleanup final : public Cleanup {
10811081
};
10821082
}
10831083

1084+
SILValue
1085+
SILGenBuilder::emitBeginAccess(SILLocation loc,
1086+
SILValue address,
1087+
SILAccessKind kind,
1088+
SILAccessEnforcement enforcement) {
1089+
auto access = createBeginAccess(loc, address,
1090+
kind, enforcement,
1091+
/*no nested conflict*/ false, false);
1092+
SGF.Cleanups.pushCleanup<EndAccessCleanup>(access);
1093+
return access;
1094+
}
1095+
10841096
ManagedValue
10851097
SILGenBuilder::createOpaqueBorrowBeginAccess(SILLocation loc,
10861098
ManagedValue address) {

lib/SILGen/SILGenBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ class SILGenBuilder : public SILBuilder {
477477
ManagedValue base,
478478
MarkDependenceKind dependencekind);
479479

480+
SILValue emitBeginAccess(SILLocation loc, SILValue address,
481+
SILAccessKind kind,
482+
SILAccessEnforcement enforcement);
483+
480484
ManagedValue createOpaqueBorrowBeginAccess(SILLocation loc,
481485
ManagedValue address);
482486
ManagedValue createOpaqueConsumeBeginAccess(SILLocation loc,

lib/SILGen/SILGenConstructor.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,8 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
11731173
MarkUnresolvedNonCopyableValueInst::CheckKind::
11741174
ConsumableAndAssignable);
11751175
}
1176-
VarLocs[selfDecl] = VarLoc::get(selfArg.getValue());
1176+
VarLocs[selfDecl] = VarLoc(selfArg.getValue(),
1177+
SILAccessEnforcement::Static);
11771178
}
11781179
}
11791180

@@ -1696,7 +1697,7 @@ void SILGenFunction::emitIVarInitializer(SILDeclRef ivarInitializer) {
16961697
selfArg = B.createMarkUninitialized(selfDecl, selfArg,
16971698
MarkUninitializedInst::RootSelf);
16981699
assert(selfTy.hasReferenceSemantics() && "can't emit a value type ctor here");
1699-
VarLocs[selfDecl] = VarLoc::get(selfArg);
1700+
VarLocs[selfDecl] = VarLoc(selfArg, SILAccessEnforcement::Unknown);
17001701

17011702
auto cleanupLoc = CleanupLocation(loc);
17021703
prepareEpilog(cd, std::nullopt, std::nullopt, cleanupLoc);
@@ -1727,11 +1728,11 @@ void SILGenFunction::emitInitAccessor(AccessorDecl *accessor) {
17271728
loc.markAutoGenerated();
17281729

17291730
SILValue argValue = F.begin()->createFunctionArgument(type, arg);
1730-
VarLocs[arg] =
1731-
markUninitialized
1732-
? VarLoc::get(B.createMarkUninitializedOut(loc, argValue))
1733-
: VarLoc::get(argValue);
1734-
1731+
if (markUninitialized) {
1732+
argValue = B.createMarkUninitializedOut(loc, argValue);
1733+
}
1734+
1735+
VarLocs[arg] = VarLoc(argValue, SILAccessEnforcement::Static);
17351736
InitAccessorArgumentMappings[property] = arg;
17361737
};
17371738

lib/SILGen/SILGenDecl.cpp

Lines changed: 163 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ class DestroyLocalVariable : public Cleanup {
472472
Var->print(llvm::errs());
473473
llvm::errs() << "\n";
474474
if (isActive()) {
475-
auto loc = SGF.VarLocs[Var];
475+
auto &loc = SGF.VarLocs[Var];
476476
assert((loc.box || loc.value) && "One of box or value should be set");
477477
if (loc.box) {
478478
llvm::errs() << "Box: " << loc.box << "\n";
@@ -664,7 +664,8 @@ class LocalVariableInitialization : public SingleBufferInitialization {
664664
/// decl to.
665665
assert(SGF.VarLocs.count(decl) == 0 && "Already emitted the local?");
666666

667-
SGF.VarLocs[decl] = SILGenFunction::VarLoc::get(Addr, Box);
667+
SGF.VarLocs[decl] = SILGenFunction::VarLoc(Addr,
668+
SILAccessEnforcement::Dynamic, Box);
668669

669670
SingleBufferInitialization::finishInitialization(SGF);
670671
assert(!DidFinish &&
@@ -677,6 +678,54 @@ class LocalVariableInitialization : public SingleBufferInitialization {
677678
} // end anonymous namespace
678679

679680
namespace {
681+
682+
static void deallocateAddressable(SILGenFunction &SGF,
683+
SILLocation l,
684+
SILGenFunction::VarLoc &loc) {
685+
SGF.B.createEndBorrow(l, loc.addressableBuffer.state->storeBorrow);
686+
SGF.B.createDeallocStack(l, loc.addressableBuffer.state->allocStack);
687+
if (loc.addressableBuffer.state->reabstraction) {
688+
SGF.B.createDestroyValue(l, loc.addressableBuffer.state->reabstraction);
689+
}
690+
}
691+
692+
/// Cleanup to deallocate the addressable buffer for a parameter or let
693+
/// binding.
694+
class DeallocateLocalVariableAddressableBuffer : public Cleanup {
695+
ValueDecl *vd;
696+
public:
697+
DeallocateLocalVariableAddressableBuffer(ValueDecl *vd) : vd(vd) {}
698+
699+
void emit(SILGenFunction &SGF, CleanupLocation l,
700+
ForUnwind_t forUnwind) override {
701+
auto found = SGF.VarLocs.find(vd);
702+
if (found == SGF.VarLocs.end()) {
703+
return;
704+
}
705+
auto &loc = found->second;
706+
707+
if (loc.addressableBuffer.state) {
708+
// The addressable buffer was forced, so clean it up now.
709+
deallocateAddressable(SGF, l, loc);
710+
} else {
711+
// Remember this insert location in case we need to force the addressable
712+
// buffer later.
713+
SILInstruction *marker = SGF.B.createTuple(l, {});
714+
loc.addressableBuffer.cleanupPoints.emplace_back(marker);
715+
}
716+
}
717+
718+
void dump(SILGenFunction &SGF) const override {
719+
#ifndef NDEBUG
720+
llvm::errs() << "DeallocateLocalVariableAddressableBuffer\n"
721+
<< "State:" << getState() << "\n"
722+
<< "Decl: ";
723+
vd->print(llvm::errs());
724+
llvm::errs() << "\n";
725+
#endif
726+
}
727+
};
728+
680729
/// Initialize a writeback buffer that receives the value of a 'let'
681730
/// declaration.
682731
class LetValueInitialization : public Initialization {
@@ -755,7 +804,8 @@ class LetValueInitialization : public Initialization {
755804
if (isUninitialized)
756805
address = SGF.B.createMarkUninitializedVar(vd, address);
757806
DestroyCleanup = SGF.enterDormantTemporaryCleanup(address, *lowering);
758-
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(address);
807+
SGF.VarLocs[vd] = SILGenFunction::VarLoc(address,
808+
SILAccessEnforcement::Unknown);
759809
}
760810
// Push a cleanup to destroy the let declaration. This has to be
761811
// inactive until the variable is initialized: if control flow exits the
@@ -766,6 +816,10 @@ class LetValueInitialization : public Initialization {
766816
SGF.Cleanups.pushCleanupInState<DestroyLocalVariable>(
767817
CleanupState::Dormant, vd);
768818
DestroyCleanup = SGF.Cleanups.getTopCleanup();
819+
820+
// If the binding has an addressable buffer forced, it should be cleaned
821+
// up here.
822+
SGF.enterLocalVariableAddressableBufferScope(vd);
769823
}
770824

771825
~LetValueInitialization() override {
@@ -883,7 +937,8 @@ class LetValueInitialization : public Initialization {
883937
if (SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule()))
884938
value = getValueForLexicalLifetimeBinding(SGF, loc, value, wasPlusOne);
885939

886-
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(value);
940+
SGF.VarLocs[vd] = SILGenFunction::VarLoc(value,
941+
SILAccessEnforcement::Unknown);
887942

888943
// Emit a debug_value[_addr] instruction to record the start of this value's
889944
// lifetime, if permitted to do so.
@@ -1463,7 +1518,7 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable,
14631518
assert(SILDebugClient && "Debugger client doesn't support SIL");
14641519
SILValue SV = SILDebugClient->emitLValueForVariable(vd, B);
14651520

1466-
VarLocs[vd] = SILGenFunction::VarLoc::get(SV);
1521+
VarLocs[vd] = VarLoc(SV, SILAccessEnforcement::Dynamic);
14671522
return InitializationPtr(new KnownAddressInitialization(SV));
14681523
}
14691524

@@ -1494,7 +1549,7 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable,
14941549
if (isUninitialized)
14951550
addr = B.createMarkUninitializedVar(loc, addr);
14961551

1497-
VarLocs[vd] = SILGenFunction::VarLoc::get(addr);
1552+
VarLocs[vd] = VarLoc(addr, SILAccessEnforcement::Dynamic);
14981553
Result = InitializationPtr(new KnownAddressInitialization(addr));
14991554
} else {
15001555
std::optional<MarkUninitializedInst::Kind> uninitKind;
@@ -2309,7 +2364,7 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
23092364
}
23102365
};
23112366

2312-
auto loc = VarLocs[vd];
2367+
auto &loc = VarLocs[vd];
23132368

23142369
// For a heap variable, the box is responsible for the value. We just need
23152370
// to give up our retain count on it.
@@ -2406,6 +2461,101 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
24062461
llvm_unreachable("unhandled case");
24072462
}
24082463

2464+
void
2465+
SILGenFunction::enterLocalVariableAddressableBufferScope(VarDecl *decl) {
2466+
Cleanups.pushCleanup<DeallocateLocalVariableAddressableBuffer>(decl);
2467+
}
2468+
2469+
SILValue
2470+
SILGenFunction::getLocalVariableAddressableBuffer(VarDecl *decl,
2471+
SILLocation curLoc,
2472+
ValueOwnership ownership) {
2473+
auto foundVarLoc = VarLocs.find(decl);
2474+
if (foundVarLoc == VarLocs.end()) {
2475+
return SILValue();
2476+
}
2477+
2478+
auto &varLoc = foundVarLoc->second;
2479+
SILType fullyAbstractedTy = getLoweredType(AbstractionPattern::getOpaque(),
2480+
decl->getTypeInContext()->getRValueType());
2481+
2482+
// Check whether the bound value is inherently suitable for addressability.
2483+
// It must already be in memory and fully abstracted.
2484+
if (varLoc.value->getType().isAddress()
2485+
&& fullyAbstractedTy.getASTType()==varLoc.value->getType().getASTType()) {
2486+
SILValue address = varLoc.value;
2487+
// Begin an access if the address is mutable.
2488+
if (varLoc.access != SILAccessEnforcement::Unknown) {
2489+
address = B.emitBeginAccess(curLoc, address,
2490+
ownership == ValueOwnership::InOut ? SILAccessKind::Modify
2491+
: SILAccessKind::Read,
2492+
varLoc.access);
2493+
}
2494+
return address;
2495+
}
2496+
2497+
// We can't retroactively introduce a reabstracted representation for a
2498+
// mutable binding (since we would now have two mutable memory locations
2499+
// representing the same value).
2500+
if (varLoc.access != SILAccessEnforcement::Unknown) {
2501+
return SILValue();
2502+
}
2503+
2504+
assert(ownership == ValueOwnership::Shared);
2505+
2506+
// Check whether the in-memory representation has already been forced.
2507+
if (auto &state = varLoc.addressableBuffer.state) {
2508+
return state->storeBorrow;
2509+
}
2510+
2511+
// Otherwise, force the addressable representation.
2512+
SILValue reabstraction, allocStack, storeBorrow;
2513+
{
2514+
SavedInsertionPointRAII save(B);
2515+
B.setInsertionPoint(varLoc.value->getNextInstruction());
2516+
auto declarationLoc = varLoc.value->getDefiningInsertionPoint()->getLoc();
2517+
2518+
// Reabstract if necessary.
2519+
auto value = varLoc.value;
2520+
reabstraction = SILValue();
2521+
if (value->getType().getASTType() != fullyAbstractedTy.getASTType()){
2522+
auto reabstracted = emitSubstToOrigValue(curLoc,
2523+
ManagedValue::forBorrowedRValue(value),
2524+
AbstractionPattern::getOpaque(),
2525+
decl->getTypeInContext()->getCanonicalType(),
2526+
SGFContext());
2527+
reabstraction = reabstracted.forward(*this);
2528+
value = reabstraction;
2529+
}
2530+
// TODO: reabstract
2531+
allocStack = B.createAllocStack(declarationLoc, value->getType(),
2532+
std::nullopt,
2533+
DoesNotHaveDynamicLifetime,
2534+
IsNotLexical,
2535+
IsNotFromVarDecl,
2536+
DoesNotUseMoveableValueDebugInfo,
2537+
/*skipVarDeclAssert*/ true);
2538+
storeBorrow = B.createStoreBorrow(declarationLoc, value, allocStack);
2539+
}
2540+
2541+
// Record the addressable representation.
2542+
varLoc.addressableBuffer.state
2543+
= std::make_unique<VarLoc::AddressableBuffer::State>(reabstraction,
2544+
allocStack,
2545+
storeBorrow);
2546+
2547+
// Emit cleanups on any paths where we previously would have cleaned up
2548+
// the addressable representation if it had been forced earlier.
2549+
for (SILInstruction *cleanupPoint : varLoc.addressableBuffer.cleanupPoints) {
2550+
SavedInsertionPointRAII insertCleanup(B, cleanupPoint);
2551+
deallocateAddressable(*this, cleanupPoint->getLoc(), varLoc);
2552+
cleanupPoint->eraseFromParent();
2553+
}
2554+
varLoc.addressableBuffer.cleanupPoints.clear();
2555+
2556+
return storeBorrow;
2557+
}
2558+
24092559
void BlackHoleInitialization::performPackExpansionInitialization(
24102560
SILGenFunction &SGF,
24112561
SILLocation loc,
@@ -2437,3 +2587,9 @@ void BlackHoleInitialization::copyOrInitValueInto(SILGenFunction &SGF, SILLocati
24372587
value = SGF.B.createMoveValue(loc, value);
24382588
SGF.B.createIgnoredUse(loc, value.getValue());
24392589
}
2590+
2591+
SILGenFunction::VarLoc::AddressableBuffer::~AddressableBuffer() {
2592+
for (auto cleanupPoint : cleanupPoints) {
2593+
cleanupPoint->eraseFromParent();
2594+
}
2595+
}

lib/SILGen/SILGenFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
740740
}
741741
};
742742

743-
auto Entry = found->second;
743+
auto &Entry = found->second;
744744
auto val = Entry.value;
745745

746746
switch (SGM.Types.getDeclCaptureKind(capture, expansion)) {

0 commit comments

Comments
 (0)