Skip to content

Commit da81345

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 1a08018 commit da81345

14 files changed

+644
-78
lines changed

include/swift/AST/Types.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -4334,9 +4334,7 @@ inline bool isConsumedParameterInCaller(ParameterConvention conv) {
43344334
return isConsumedParameter<false>(conv);
43354335
}
43364336

4337-
/// Returns true if conv is a guaranteed parameter. This may look unnecessary
4338-
/// but this will allow code to generalize to handle Indirect_Guaranteed
4339-
/// parameters when they are added.
4337+
/// Returns true if conv is a guaranteed parameter.
43404338
template <bool InCallee>
43414339
bool isGuaranteedParameter(ParameterConvention conv) {
43424340
switch (conv) {

lib/SIL/IR/AbstractionPattern.cpp

+1-3
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

+13-7
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

+12
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

+4
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

+8-7
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

+171-10
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+
const SILGenFunction::VarLoc::AddressableBuffer::State &state) {
685+
SGF.B.createEndBorrow(l, state.storeBorrow);
686+
SGF.B.createDeallocStack(l, state.allocStack);
687+
if (state.reabstraction) {
688+
SGF.B.createDestroyValue(l, 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 (auto &state = loc.addressableBuffer.state) {
708+
// The addressable buffer was forced, so clean it up now.
709+
deallocateAddressable(SGF, l, *state);
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,11 +2364,9 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
23092364
}
23102365
};
23112366

2312-
auto loc = VarLocs[vd];
2313-
23142367
// For a heap variable, the box is responsible for the value. We just need
23152368
// to give up our retain count on it.
2316-
if (auto boxValue = loc.box) {
2369+
if (auto boxValue = VarLocs[vd].box) {
23172370
if (!getASTContext().SILOpts.supportsLexicalLifetimes(getModule())) {
23182371
emitDestroy(boxValue);
23192372
return;
@@ -2329,7 +2382,7 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
23292382

23302383
// For 'let' bindings, we emit a release_value or destroy_addr, depending on
23312384
// whether we have an address or not.
2332-
SILValue Val = loc.value;
2385+
SILValue Val = VarLocs[vd].value;
23332386

23342387
if (Val->getType().isAddress()) {
23352388
B.createDestroyAddr(silLoc, Val);
@@ -2406,6 +2459,108 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) {
24062459
llvm_unreachable("unhandled case");
24072460
}
24082461

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

lib/SILGen/SILGenFunction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
770770
}
771771
};
772772

773-
auto Entry = found->second;
773+
auto &Entry = found->second;
774774
auto val = Entry.value;
775775

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

0 commit comments

Comments
 (0)