Skip to content

Commit 13b9915

Browse files
authored
Use in_guaranteed for let captures (#29812)
* Use in_guaranteed for let captures With this all let values will be captured with in_guaranteed convention by the closure. Following are the main changes : SILGen changes: - A new CaptureKind::Immutable is introduced, to capture let values as in_guaranteed. - SILGen of in_guaranteed capture had to be fixed. in_guaranteed captures as per convention are consumed by the closure. And so SILGen should not generate a destroy_addr for an in_guaranteed capture. But LetValueInitialization can push Dealloc and Release states of the captured arg in the Cleanup stack, and there is no way to access the CleanupHandle and disable the emission of destroy_addr while emitting the captures in SILGenFunction::emitCaptures. So we now create, temporary allocation of the in_guaranteed capture iduring SILGenFunction::emitCaptures without emitting destroy_addr for it. SILOptimizer changes: - Handle in_guaranteed in CopyForwarding. - Adjust dealloc_stack of in_guaranteed capture to occur after destroy_addr for on_stack closures in ClosureLifetimeFixup. IRGen changes : - Since HeapLayout can be non-fixed now, make sure emitSize is used conditionally - Don't consider ClassPointerSource kind parameter type for fulfillments while generating code for partial apply forwarder. The TypeMetadata of ClassPointSource kind sources are not populated in HeapLayout's NecessaryBindings. If we have a generic parameter on the HeapLayout which can be fulfilled by a ClassPointerSource, its TypeMetaData will not be found while constructing the dtor function of the HeapLayout. So it is important to skip considering sources of ClassPointerSource kind, so that TypeMetadata of a dependent generic parameters gets populated in HeapLayout's NecessaryBindings.
1 parent aeabe28 commit 13b9915

23 files changed

+293
-115
lines changed

include/swift/SIL/TypeLowering.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,8 @@ enum class CaptureKind {
561561
StorageAddress,
562562
/// A local value captured as a constant.
563563
Constant,
564+
/// A let constant captured as a pointer to storage
565+
Immutable
564566
};
565567

566568

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,19 @@ void releasePartialApplyCapturedArg(
364364
SILParameterInfo paramInfo,
365365
InstModCallbacks callbacks = InstModCallbacks());
366366

367+
void deallocPartialApplyCapturedArg(
368+
SILBuilder &builder, SILLocation loc, SILValue arg,
369+
SILParameterInfo paramInfo);
370+
367371
/// Insert destroys of captured arguments of partial_apply [stack].
368372
void insertDestroyOfCapturedArguments(
369373
PartialApplyInst *pai, SILBuilder &builder,
370374
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
371375
[](SILValue arg) -> bool { return true; });
372376

377+
void insertDeallocOfCapturedArguments(
378+
PartialApplyInst *pai, SILBuilder &builder);
379+
373380
/// This iterator 'looks through' one level of builtin expect users exposing all
374381
/// users of the looked through builtin expect instruction i.e it presents a
375382
/// view that shows all users as if there were no builtin expect instructions

lib/IRGen/GenFunc.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,9 +1319,25 @@ Optional<StackAddress> irgen::emitFunctionPartialApplication(
13191319
SmallVector<SILType, 4> argValTypes;
13201320
SmallVector<ParameterConvention, 4> argConventions;
13211321

1322+
// Go over the params and check if any of them can cause the HeapLayout to be non-fixed
1323+
// This is needed because we should not consider parameters of kind ClassPointer and Metadata sources
1324+
// If not, we may end up with missing TypeMetadata for a type dependent generic parameter
1325+
// while generating code for destructor of HeapLayout.
1326+
bool considerParameterSources = true;
1327+
for (auto param : params) {
1328+
SILType argType = IGF.IGM.silConv.getSILType(param, origType);
1329+
auto argLoweringTy = getArgumentLoweringType(argType.getASTType(), param);
1330+
auto &ti = IGF.getTypeInfoForLowered(argLoweringTy);
1331+
1332+
if (!isa<FixedTypeInfo>(ti)) {
1333+
considerParameterSources = false;
1334+
break;
1335+
}
1336+
}
1337+
13221338
// Reserve space for polymorphic bindings.
13231339
auto bindings =
1324-
NecessaryBindings::forPartialApplyForwarder(IGF.IGM, origType, subs);
1340+
NecessaryBindings::forPartialApplyForwarder(IGF.IGM, origType, subs, considerParameterSources);
13251341

13261342
if (!bindings.empty()) {
13271343
hasSingleSwiftRefcountedContext = No;
@@ -1524,7 +1540,7 @@ Optional<StackAddress> irgen::emitFunctionPartialApplication(
15241540
HeapNonFixedOffsets offsets(IGF, layout);
15251541
if (outType->isNoEscape()) {
15261542
stackAddr = IGF.emitDynamicAlloca(
1527-
IGF.IGM.Int8Ty, layout.emitSize(IGF.IGM), Alignment(16));
1543+
IGF.IGM.Int8Ty, layout.isFixedLayout() ? layout.emitSize(IGF.IGM) : offsets.getSize() , Alignment(16));
15281544
stackAddr = stackAddr->withAddress(IGF.Builder.CreateBitCast(
15291545
stackAddr->getAddress(), IGF.IGM.OpaquePtrTy));
15301546
data = stackAddr->getAddress().getAddress();

lib/IRGen/GenProto.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class PolymorphicConvention {
112112
}
113113

114114
public:
115-
PolymorphicConvention(IRGenModule &IGM, CanSILFunctionType fnType);
115+
PolymorphicConvention(IRGenModule &IGM, CanSILFunctionType fnType, bool considerParameterSources);
116116

117117
ArrayRef<MetadataSource> getSources() const { return Sources; }
118118

@@ -179,8 +179,9 @@ class PolymorphicConvention {
179179
} // end anonymous namespace
180180

181181
PolymorphicConvention::PolymorphicConvention(IRGenModule &IGM,
182-
CanSILFunctionType fnType)
183-
: IGM(IGM), M(*IGM.getSwiftModule()), FnType(fnType) {
182+
CanSILFunctionType fnType,
183+
bool considerParameterSources = true)
184+
: IGM(IGM), M(*IGM.getSwiftModule()), FnType(fnType){
184185
initGenerics();
185186

186187
auto rep = fnType->getRepresentation();
@@ -212,16 +213,18 @@ PolymorphicConvention::PolymorphicConvention(IRGenModule &IGM,
212213
unsigned selfIndex = ~0U;
213214
auto params = fnType->getParameters();
214215

215-
// Consider 'self' first.
216-
if (fnType->hasSelfParam()) {
217-
selfIndex = params.size() - 1;
218-
considerParameter(params[selfIndex], selfIndex, true);
219-
}
216+
if (considerParameterSources) {
217+
// Consider 'self' first.
218+
if (fnType->hasSelfParam()) {
219+
selfIndex = params.size() - 1;
220+
considerParameter(params[selfIndex], selfIndex, true);
221+
}
220222

221-
// Now consider the rest of the parameters.
222-
for (auto index : indices(params)) {
223-
if (index != selfIndex)
224-
considerParameter(params[index], index, false);
223+
// Now consider the rest of the parameters.
224+
for (auto index : indices(params)) {
225+
if (index != selfIndex)
226+
considerParameter(params[index], index, false);
227+
}
225228
}
226229
}
227230
}
@@ -3000,14 +3003,16 @@ NecessaryBindings::forFunctionInvocations(IRGenModule &IGM,
30003003
NecessaryBindings
30013004
NecessaryBindings::forPartialApplyForwarder(IRGenModule &IGM,
30023005
CanSILFunctionType origType,
3003-
SubstitutionMap subs) {
3006+
SubstitutionMap subs,
3007+
bool considerParameterSources) {
30043008
return computeBindings(IGM, origType, subs,
3005-
true /*forPartialApplyForwarder*/);
3009+
true /*forPartialApplyForwarder*/,
3010+
considerParameterSources);
30063011
}
30073012

30083013
NecessaryBindings NecessaryBindings::computeBindings(
30093014
IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs,
3010-
bool forPartialApplyForwarder) {
3015+
bool forPartialApplyForwarder, bool considerParameterSources) {
30113016

30123017
NecessaryBindings bindings;
30133018
bindings.forPartialApply = forPartialApplyForwarder;
@@ -3017,7 +3022,7 @@ NecessaryBindings NecessaryBindings::computeBindings(
30173022
return bindings;
30183023

30193024
// Figure out what we're actually required to pass:
3020-
PolymorphicConvention convention(IGM, origType);
3025+
PolymorphicConvention convention(IGM, origType, considerParameterSources);
30213026

30223027
// - unfulfilled requirements
30233028
convention.enumerateUnfulfilledRequirements(

lib/IRGen/NecessaryBindings.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ class NecessaryBindings {
5959
SubstitutionMap subs);
6060
static NecessaryBindings forPartialApplyForwarder(IRGenModule &IGM,
6161
CanSILFunctionType origType,
62-
SubstitutionMap subs);
62+
SubstitutionMap subs,
63+
bool considerParameterSources = true);
6364

6465
/// Add whatever information is necessary to reconstruct type metadata
6566
/// for the given type.
@@ -98,7 +99,8 @@ class NecessaryBindings {
9899
static NecessaryBindings computeBindings(IRGenModule &IGM,
99100
CanSILFunctionType origType,
100101
SubstitutionMap subs,
101-
bool forPartialApplyForwarder);
102+
bool forPartialApplyForwarder,
103+
bool considerParameterSources = true);
102104
};
103105

104106
} // end namespace irgen

lib/SIL/SILFunctionType.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,9 +1460,8 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
14601460
case CaptureKind::Constant: {
14611461
// Constants are captured by value.
14621462
ParameterConvention convention;
1463-
if (loweredTL.isAddressOnly()) {
1464-
convention = ParameterConvention::Indirect_In_Guaranteed;
1465-
} else if (loweredTL.isTrivial()) {
1463+
assert (!loweredTL.isAddressOnly());
1464+
if (loweredTL.isTrivial()) {
14661465
convention = ParameterConvention::Direct_Unowned;
14671466
} else {
14681467
convention = ParameterConvention::Direct_Guaranteed;
@@ -1495,6 +1494,16 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
14951494
inputs.push_back(param);
14961495
break;
14971496
}
1497+
case CaptureKind::Immutable: {
1498+
// 'let' constants that are address-only are captured as the address of the value
1499+
// and will be consumed by the closure.
1500+
SILType ty = loweredTy.getAddressType();
1501+
auto param =
1502+
SILParameterInfo(ty.getASTType(),
1503+
ParameterConvention::Indirect_In_Guaranteed);
1504+
inputs.push_back(param);
1505+
break;
1506+
}
14981507
}
14991508
}
15001509
}

lib/SIL/SILVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
11171117
if (VarInfo)
11181118
if (unsigned ArgNo = VarInfo->ArgNo) {
11191119
// It is a function argument.
1120-
if (ArgNo < DebugVars.size() && !DebugVars[ArgNo].empty()) {
1120+
if (ArgNo < DebugVars.size() && !DebugVars[ArgNo].empty() && !VarInfo->Name.empty()) {
11211121
require(
11221122
DebugVars[ArgNo] == VarInfo->Name,
11231123
"Scope contains conflicting debug variables for one function "

lib/SIL/TypeLowering.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture,
126126
return CaptureKind::Box;
127127
}
128128

129+
// For 'let' constants
130+
if (!var->supportsMutation()) {
131+
return CaptureKind::Immutable;
132+
}
133+
129134
// If we're capturing into a non-escaping closure, we can generally just
130135
// capture the address of the value as no-escape.
131136
return (capture.isNoEscape()

lib/SILGen/SILGenFunction.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ void SILGenFunction::emitCaptures(SILLocation loc,
262262
case CaptureKind::Constant:
263263
capturedArgs.push_back(emitUndef(getLoweredType(type)));
264264
break;
265+
case CaptureKind::Immutable:
265266
case CaptureKind::StorageAddress:
266267
capturedArgs.push_back(emitUndef(getLoweredType(type).getAddressType()));
267268
break;
@@ -333,7 +334,23 @@ void SILGenFunction::emitCaptures(SILLocation loc,
333334
capturedArgs.push_back(emitManagedRValueWithCleanup(Val));
334335
break;
335336
}
336-
337+
case CaptureKind::Immutable: {
338+
if (canGuarantee) {
339+
auto entryValue = getAddressValue(Entry.value);
340+
// No-escaping stored declarations are captured as the
341+
// address of the value.
342+
assert(entryValue->getType().isAddress() && "no address for captured var!");
343+
capturedArgs.push_back(ManagedValue::forLValue(entryValue));
344+
}
345+
else {
346+
auto entryValue = getAddressValue(Entry.value);
347+
auto addr = emitTemporaryAllocation(vd, entryValue->getType());
348+
auto val = B.createCopyAddr(loc, entryValue, addr, IsNotTake,
349+
IsInitialization);
350+
capturedArgs.push_back(ManagedValue::forLValue(addr));
351+
}
352+
break;
353+
}
337354
case CaptureKind::StorageAddress: {
338355
auto entryValue = getAddressValue(Entry.value);
339356
// No-escaping stored declarations are captured as the

lib/SILGen/SILGenProlog.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ static void emitCaptureArguments(SILGenFunction &SGF,
420420
SGF.B.createDebugValueAddr(Loc, addr, DbgVar);
421421
break;
422422
}
423+
case CaptureKind::Immutable:
423424
case CaptureKind::StorageAddress: {
424425
// Non-escaping stored decls are captured as the address of the value.
425426
auto type = getVarTypeInCaptureContext();

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,27 @@ static bool tryRewriteToPartialApplyStack(
459459
saveDeleteInst(convertOrPartialApply);
460460
saveDeleteInst(origPA);
461461

462+
ApplySite site(newPA);
463+
SILFunctionConventions calleeConv(site.getSubstCalleeType(),
464+
newPA->getModule());
465+
466+
// Since we create temporary allocation for in_guaranteed captures during SILGen,
467+
// the dealloc_stack of it can occur before the apply due to conversion scopes.
468+
// When we insert destroy_addr of the in_guaranteed capture after the apply,
469+
// we may end up with a situation when the dealloc_stack occurs before the destroy_addr.
470+
// The code below proactively removes the dealloc_stack of in_guaranteed capture,
471+
// so that it can be reinserted at the correct place after the destroy_addr below.
472+
for (auto &arg : newPA->getArgumentOperands()) {
473+
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
474+
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
475+
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
476+
if (paramInfo.getConvention() == ParameterConvention::Indirect_In_Guaranteed)
477+
// go over all the dealloc_stack, remove it
478+
for (auto *use : arg.get()->getUses())
479+
if (auto *deallocInst = dyn_cast<DeallocStackInst>(use->getUser()))
480+
deallocInst->eraseFromParent();
481+
}
482+
462483
// Insert destroys of arguments after the apply and the dealloc_stack.
463484
if (auto *apply = dyn_cast<ApplyInst>(singleApplyUser)) {
464485
auto insertPt = std::next(SILBasicBlock::iterator(apply));
@@ -468,11 +489,15 @@ static bool tryRewriteToPartialApplyStack(
468489
SILBuilderWithScope b3(insertPt);
469490
b3.createDeallocStack(loc, newPA);
470491
insertDestroyOfCapturedArguments(newPA, b3);
492+
// dealloc_stack of the in_guaranteed capture is inserted
493+
insertDeallocOfCapturedArguments(newPA, b3);
471494
} else if (auto *tai = dyn_cast<TryApplyInst>(singleApplyUser)) {
472495
for (auto *succBB : tai->getSuccessorBlocks()) {
473496
SILBuilderWithScope b3(succBB->begin());
474497
b3.createDeallocStack(loc, newPA);
475498
insertDestroyOfCapturedArguments(newPA, b3);
499+
// dealloc_stack of the in_guaranteed capture is inserted
500+
insertDeallocOfCapturedArguments(newPA, b3);
476501
}
477502
} else {
478503
llvm_unreachable("Unknown FullApplySite instruction kind");

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,23 @@ bool TempRValueOptPass::collectLoads(
147147
LLVM_DEBUG(llvm::dbgs()
148148
<< " Temp use may write/destroy its source" << *user);
149149
return false;
150-
151150
case SILInstructionKind::BeginAccessInst:
152151
return cast<BeginAccessInst>(user)->getAccessKind() == SILAccessKind::Read;
153152

153+
case SILInstructionKind::MarkDependenceInst: {
154+
auto mdi = cast<MarkDependenceInst>(user);
155+
if (mdi->getBase() == address) {
156+
return true;
157+
}
158+
for (auto *mdiUseOper : mdi->getUses()) {
159+
if (!collectLoads(mdiUseOper, mdiUseOper->getUser(), mdi, srcAddr,
160+
loadInsts))
161+
return false;
162+
}
163+
return true;
164+
}
154165
case SILInstructionKind::ApplyInst:
166+
case SILInstructionKind::PartialApplyInst:
155167
case SILInstructionKind::TryApplyInst: {
156168
ApplySite apply(user);
157169

@@ -650,7 +662,14 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
650662
toDelete.push_back(fli);
651663
break;
652664
}
653-
665+
case SILInstructionKind::MarkDependenceInst: {
666+
SILBuilderWithScope builder(user);
667+
auto mdi = cast<MarkDependenceInst>(user);
668+
auto newInst = builder.createMarkDependence(user->getLoc(), mdi->getValue(), si->getSrc());
669+
mdi->replaceAllUsesWith(newInst);
670+
toDelete.push_back(user);
671+
break;
672+
}
654673
// ASSUMPTION: no operations that may be handled by this default clause can
655674
// destroy tempObj. This includes operations that load the value from memory
656675
// and release it.

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,15 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
13721372
callbacks.createdNewInst(u.get<ReleaseValueInst *>());
13731373
}
13741374

1375+
void swift::deallocPartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
1376+
SILValue arg,
1377+
SILParameterInfo paramInfo) {
1378+
if (!paramInfo.isIndirectInGuaranteed())
1379+
return;
1380+
1381+
builder.createDeallocStack(loc, arg);
1382+
}
1383+
13751384
static bool
13761385
deadMarkDependenceUser(SILInstruction *inst,
13771386
SmallVectorImpl<SILInstruction *> &deleteInsts) {
@@ -1937,6 +1946,22 @@ void swift::insertDestroyOfCapturedArguments(
19371946
}
19381947
}
19391948

1949+
void swift::insertDeallocOfCapturedArguments(
1950+
PartialApplyInst *pai, SILBuilder &builder) {
1951+
assert(pai->isOnStack());
1952+
1953+
ApplySite site(pai);
1954+
SILFunctionConventions calleeConv(site.getSubstCalleeType(),
1955+
pai->getModule());
1956+
auto loc = RegularLocation::getAutoGeneratedLocation();
1957+
for (auto &arg : pai->getArgumentOperands()) {
1958+
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
1959+
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
1960+
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
1961+
deallocPartialApplyCapturedArg(builder, loc, arg.get(), paramInfo);
1962+
}
1963+
}
1964+
19401965
AbstractFunctionDecl *swift::getBaseMethod(AbstractFunctionDecl *FD) {
19411966
while (FD->getOverriddenDecl()) {
19421967
FD = FD->getOverriddenDecl();

0 commit comments

Comments
 (0)