Skip to content

Commit ccbf25b

Browse files
Merge pull request #21933 from aschwaighofer/partial_apply_stack
Use stack allocation for Swift closures
2 parents 5e2e467 + ae00311 commit ccbf25b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1266
-347
lines changed

docs/SIL.rst

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3347,11 +3347,12 @@ partial_apply
33473347
`````````````
33483348
::
33493349

3350-
sil-instruction ::= 'partial_apply' callee-ownership-attr? sil-value
3350+
sil-instruction ::= 'partial_apply' callee-ownership-attr? on-stack-attr? sil-value
33513351
sil-apply-substitution-list?
33523352
'(' (sil-value (',' sil-value)*)? ')'
33533353
':' sil-type
33543354
callee-ownership-attr ::= '[callee_guaranteed]'
3355+
on-stack-attr ::= '[on_stack]'
33553356

33563357
%c = partial_apply %0(%1, %2, ...) : $(Z..., A, B, ...) -> R
33573358
// Note that the type of the callee '%0' is specified *after* the arguments
@@ -3369,12 +3370,18 @@ partial_apply
33693370
Creates a closure by partially applying the function ``%0`` to a partial
33703371
sequence of its arguments. In the instruction syntax, the type of the callee is
33713372
specified after the argument list; the types of the argument and of the defined
3372-
value are derived from the function type of the callee. The closure context will
3373-
be allocated with retain count 1 and initialized to contain the values ``%1``,
3373+
value are derived from the function type of the callee. If the ``partial_apply``
3374+
has an escaping function type (not ``[on_stack]``) the closure context will be
3375+
allocated with retain count 1 and initialized to contain the values ``%1``,
33743376
``%2``, etc. The closed-over values will not be retained; that must be done
3375-
separately before the ``partial_apply``. The closure does however take
3376-
ownership of the partially applied arguments; when the closure reference
3377-
count reaches zero, the contained values will be destroyed.
3377+
separately before the ``partial_apply``. The closure does however take ownership
3378+
of the partially applied arguments; when the closure reference count reaches
3379+
zero, the contained values will be destroyed. If the ``partial_apply`` has a
3380+
``@noescape`` function type (``partial_apply [on_stack]``) the closure context
3381+
is allocated on the stack and intialized to contain the closed-over values. The
3382+
closed-over values are not retained, lifetime of the closed-over values must be
3383+
managed separately. The lifetime of the stack context of a ``partial_apply
3384+
[stack]`` must be terminated with a ``dealloc_stack``.
33783385

33793386
If the callee is generic, all of its generic parameters must be bound by the
33803387
given substitution list. The arguments are given with these generic

include/swift/SIL/SILBuilder.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,12 @@ class SILBuilder {
327327
// Type remapping
328328
//===--------------------------------------------------------------------===//
329329

330-
static SILType getPartialApplyResultType(SILType Ty, unsigned ArgCount,
331-
SILModule &M,
332-
SubstitutionMap subs,
333-
ParameterConvention calleeConvention);
330+
static SILType
331+
getPartialApplyResultType(SILType Ty, unsigned ArgCount, SILModule &M,
332+
SubstitutionMap subs,
333+
ParameterConvention calleeConvention,
334+
PartialApplyInst::OnStackKind onStack =
335+
PartialApplyInst::OnStackKind::NotOnStack);
334336

335337
//===--------------------------------------------------------------------===//
336338
// CFG Manipulation
@@ -476,10 +478,12 @@ class SILBuilder {
476478
PartialApplyInst *createPartialApply(
477479
SILLocation Loc, SILValue Fn, SubstitutionMap Subs,
478480
ArrayRef<SILValue> Args, ParameterConvention CalleeConvention,
481+
PartialApplyInst::OnStackKind OnStack =
482+
PartialApplyInst::OnStackKind::NotOnStack,
479483
const GenericSpecializationInformation *SpecializationInfo = nullptr) {
480484
return insert(PartialApplyInst::create(
481485
getSILDebugLocation(Loc), Fn, Args, Subs, CalleeConvention, *F,
482-
C.OpenedArchetypes, SpecializationInfo));
486+
C.OpenedArchetypes, SpecializationInfo, OnStack));
483487
}
484488

485489
BeginApplyInst *createBeginApply(

include/swift/SIL/SILCloner.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ SILCloner<ImplClass>::visitPartialApplyInst(PartialApplyInst *Inst) {
878878
getOpLocation(Inst->getLoc()), getOpValue(Inst->getCallee()),
879879
getOpSubstitutionMap(Inst->getSubstitutionMap()), Args,
880880
Inst->getType().getAs<SILFunctionType>()->getCalleeConvention(),
881+
Inst->isOnStack(),
881882
GenericSpecializationInformation::create(Inst, getBuilder())));
882883
}
883884

include/swift/SIL/SILInstruction.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,12 @@ class PartialApplyInst final
20722072
public llvm::TrailingObjects<PartialApplyInst, Operand> {
20732073
friend SILBuilder;
20742074

2075+
public:
2076+
enum OnStackKind {
2077+
NotOnStack, OnStack
2078+
};
2079+
2080+
private:
20752081
PartialApplyInst(SILDebugLocation DebugLoc, SILValue Callee,
20762082
SILType SubstCalleeType,
20772083
SubstitutionMap Substitutions,
@@ -2084,7 +2090,8 @@ class PartialApplyInst final
20842090
create(SILDebugLocation DebugLoc, SILValue Callee, ArrayRef<SILValue> Args,
20852091
SubstitutionMap Substitutions, ParameterConvention CalleeConvention,
20862092
SILFunction &F, SILOpenedArchetypesState &OpenedArchetypes,
2087-
const GenericSpecializationInformation *SpecializationInfo);
2093+
const GenericSpecializationInformation *SpecializationInfo,
2094+
OnStackKind onStack);
20882095

20892096
public:
20902097
/// Return the result function type of this partial apply.
@@ -2094,6 +2101,10 @@ class PartialApplyInst final
20942101
bool hasCalleeGuaranteedContext() const {
20952102
return getType().castTo<SILFunctionType>()->isCalleeGuaranteed();
20962103
}
2104+
2105+
OnStackKind isOnStack() const {
2106+
return getFunctionType()->isNoEscape() ? OnStack : NotOnStack;
2107+
}
20972108
};
20982109

20992110
class BeginApplyInst;

include/swift/SIL/TypeSubstCloner.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ class TypeSubstCloner : public SILClonerWithScopes<ImplClass> {
215215
PartialApplyInst *N = getBuilder().createPartialApply(
216216
getOpLocation(Inst->getLoc()), Helper.getCallee(),
217217
Helper.getSubstitutions(), Helper.getArguments(), ParamConvention,
218-
GenericSpecializationInformation::create(
219-
Inst, getBuilder()));
218+
Inst->isOnStack(),
219+
GenericSpecializationInformation::create(Inst, getBuilder()));
220220
recordClonedInstruction(Inst, N);
221221
}
222222

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ void releasePartialApplyCapturedArg(
212212
SILBuilder &Builder, SILLocation Loc, SILValue Arg, SILParameterInfo PInfo,
213213
InstModCallbacks Callbacks = InstModCallbacks());
214214

215+
/// Insert destroys of captured arguments of partial_apply [stack].
216+
void insertDestroyOfCapturedArguments(
217+
PartialApplyInst *PAI, SILBuilder &B,
218+
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
219+
[](SILValue arg) -> bool { return true; });
220+
215221
/// This computes the lifetime of a single SILValue.
216222
///
217223
/// This does not compute a set of jointly postdominating use points. Instead it

include/swift/SILOptimizer/Utils/StackNesting.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,10 @@ class StackNesting {
9898
///
9999
/// Each stack location is allocated by a single allocation instruction.
100100
struct StackLoc {
101-
StackLoc(AllocationInst *Alloc) : Alloc(Alloc) { }
101+
StackLoc(SingleValueInstruction *Alloc) : Alloc(Alloc) { }
102102

103103
/// Back-link to the allocation instruction.
104-
AllocationInst *Alloc;
104+
SingleValueInstruction *Alloc;
105105

106106
/// Bit-set which represents all alive locations at this allocation.
107107
/// It obviously includes this location itself. And it includes all "outer"

include/swift/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5252
/// describe what change you made. The content of this comment isn't important;
5353
/// it just ensures a conflict if two people change the module format.
5454
/// Don't worry about adhering to the 80-column limit for this line.
55-
const uint16_t SWIFTMODULE_VERSION_MINOR = 471; // Last change: Add @available(_PackageDescription..)
55+
const uint16_t SWIFTMODULE_VERSION_MINOR = 472; // Last change: partial_apply [stack]
5656

5757
using DeclIDField = BCFixed<31>;
5858

lib/IRGen/GenFunc.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,7 +1254,7 @@ static llvm::Function *emitPartialApplicationForwarder(IRGenModule &IGM,
12541254

12551255
/// Emit a partial application thunk for a function pointer applied to a partial
12561256
/// set of argument values.
1257-
void irgen::emitFunctionPartialApplication(
1257+
Optional<StackAddress> irgen::emitFunctionPartialApplication(
12581258
IRGenFunction &IGF, SILFunction &SILFn, const FunctionPointer &fn,
12591259
llvm::Value *fnContext, Explosion &args, ArrayRef<SILParameterInfo> params,
12601260
SubstitutionMap subs, CanSILFunctionType origType,
@@ -1270,8 +1270,6 @@ void irgen::emitFunctionPartialApplication(
12701270
SmallVector<SILType, 4> argValTypes;
12711271
SmallVector<ParameterConvention, 4> argConventions;
12721272

1273-
assert(!outType->isNoEscape());
1274-
12751273
// Reserve space for polymorphic bindings.
12761274
auto bindings = NecessaryBindings::forFunctionInvocations(IGF.IGM,
12771275
origType, subs);
@@ -1398,7 +1396,7 @@ void irgen::emitFunctionPartialApplication(
13981396
llvm::Value *ctx = args.claimNext();
13991397
ctx = IGF.Builder.CreateBitCast(ctx, IGF.IGM.RefCountedPtrTy);
14001398
out.add(ctx);
1401-
return;
1399+
return {};
14021400
}
14031401

14041402
Optional<FunctionPointer> staticFn;
@@ -1437,14 +1435,20 @@ void irgen::emitFunctionPartialApplication(
14371435
llvm::Value *ctx = args.claimNext();
14381436
if (isIndirectFormalParameter(*singleRefcountedConvention))
14391437
ctx = IGF.Builder.CreateLoad(ctx, IGF.IGM.getPointerAlignment());
1438+
1439+
auto expectedClosureTy =
1440+
outType->isNoEscape() ? IGF.IGM.OpaquePtrTy : IGF.IGM.RefCountedPtrTy;
1441+
14401442
// We might get a struct containing a pointer e.g type <{ %AClass* }>
1441-
if (ctx->getType() != IGF.IGM.RefCountedPtrTy)
1442-
ctx = IGF.coerceValue(ctx, IGF.IGM.RefCountedPtrTy, IGF.IGM.DataLayout);
1443+
if (ctx->getType() != expectedClosureTy)
1444+
ctx = IGF.coerceValue(ctx, expectedClosureTy, IGF.IGM.DataLayout);
14431445
out.add(ctx);
1444-
return;
1446+
if (outType->isNoEscape())
1447+
return StackAddress();
1448+
return {};
14451449
}
14461450

1447-
// Store the context arguments on the heap.
1451+
// Store the context arguments on the heap/stack.
14481452
assert(argValTypes.size() == argTypeInfos.size()
14491453
&& argTypeInfos.size() == argConventions.size()
14501454
&& "argument info lists out of sync");
@@ -1457,13 +1461,26 @@ void irgen::emitFunctionPartialApplication(
14571461
layout);
14581462

14591463
llvm::Value *data;
1464+
1465+
Optional<StackAddress> stackAddr;
1466+
14601467
if (args.empty() && layout.isKnownEmpty()) {
1461-
data = IGF.IGM.RefCountedNull;
1468+
if (outType->isNoEscape())
1469+
data = llvm::ConstantPointerNull::get(IGF.IGM.OpaquePtrTy);
1470+
else
1471+
data = IGF.IGM.RefCountedNull;
14621472
} else {
1463-
// Allocate a new object.
1464-
HeapNonFixedOffsets offsets(IGF, layout);
14651473

1466-
data = IGF.emitUnmanagedAlloc(layout, "closure", descriptor, &offsets);
1474+
// Allocate a new object on the heap or stack.
1475+
HeapNonFixedOffsets offsets(IGF, layout);
1476+
if (outType->isNoEscape()) {
1477+
stackAddr = IGF.emitDynamicAlloca(
1478+
IGF.IGM.Int8Ty, layout.emitSize(IGF.IGM), Alignment(16));
1479+
stackAddr = stackAddr->withAddress(IGF.Builder.CreateBitCast(
1480+
stackAddr->getAddress(), IGF.IGM.OpaquePtrTy));
1481+
data = stackAddr->getAddress().getAddress();
1482+
} else
1483+
data = IGF.emitUnmanagedAlloc(layout, "closure", descriptor, &offsets);
14671484
Address dataAddr = layout.emitCastTo(IGF, data);
14681485

14691486
unsigned i = 0;
@@ -1531,6 +1548,7 @@ void irgen::emitFunctionPartialApplication(
15311548
forwarder = IGF.Builder.CreateBitCast(forwarder, IGF.IGM.Int8PtrTy);
15321549
out.add(forwarder);
15331550
out.add(data);
1551+
return stackAddr;
15341552
}
15351553

15361554
/// Emit the block copy helper for a block.

lib/IRGen/GenFunc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace irgen {
4747

4848
/// Emit a partial application thunk for a function pointer applied to a
4949
/// partial set of argument values.
50-
void emitFunctionPartialApplication(
50+
Optional<StackAddress> emitFunctionPartialApplication(
5151
IRGenFunction &IGF, SILFunction &SILFn, const FunctionPointer &fnPtr,
5252
llvm::Value *fnContext, Explosion &args,
5353
ArrayRef<SILParameterInfo> argTypes, SubstitutionMap subs,

lib/IRGen/GenOpaque.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,15 +555,17 @@ StackAddress IRGenFunction::emitDynamicAlloca(llvm::Type *eltTy,
555555
/// location before the dynamic alloca's call.
556556
void IRGenFunction::emitDeallocateDynamicAlloca(StackAddress address) {
557557
// In coroutines, unconditionally call llvm.coro.alloca.free.
558-
if (isCoroutine()) {
558+
// Except if the address is invalid, this happens when this is a StackAddress
559+
// for a partial_apply [stack] that did not need a context object on the
560+
// stack.
561+
if (isCoroutine() && address.getAddress().isValid()) {
559562
auto allocToken = address.getExtraInfo();
560563
assert(allocToken && "dynamic alloca in coroutine without alloc token?");
561564
auto freeFn = llvm::Intrinsic::getDeclaration(
562565
&IGM.Module, llvm::Intrinsic::ID::coro_alloca_free);
563566
Builder.CreateCall(freeFn, allocToken);
564567
return;
565568
}
566-
567569
// Otherwise, call llvm.stackrestore if an address was saved.
568570
auto savedSP = address.getExtraInfo();
569571
if (savedSP == nullptr)
@@ -1444,4 +1446,4 @@ void TypeInfo::storeExtraInhabitantTagDynamic(IRGenFunction &IGF,
14441446
} else {
14451447
storeEnumTagSinglePayload(IGF, tag, tag, address, T, isOutlined);
14461448
}
1447-
}
1449+
}

lib/IRGen/IRGenSIL.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ class IRGenSILFunction :
374374
{
375375
public:
376376
llvm::DenseMap<SILValue, LoweredValue> LoweredValues;
377+
llvm::DenseMap<SILValue, StackAddress> LoweredPartialApplyAllocations;
377378
llvm::DenseMap<SILType, LoweredValue> LoweredUndefs;
378379

379380
/// All alloc_ref instructions which allocate the object on the stack.
@@ -2527,11 +2528,16 @@ void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) {
25272528

25282529
// Create the thunk and function value.
25292530
Explosion function;
2530-
emitFunctionPartialApplication(
2531+
auto closureStackAddr = emitFunctionPartialApplication(
25312532
*this, *CurSILFn, calleeFn, innerContext, llArgs, params,
25322533
i->getSubstitutionMap(), origCalleeTy, i->getSubstCalleeType(),
25332534
i->getType().castTo<SILFunctionType>(), function, false);
25342535
setLoweredExplosion(v, function);
2536+
2537+
if (closureStackAddr) {
2538+
assert(i->isOnStack());
2539+
LoweredPartialApplyAllocations[v] = *closureStackAddr;
2540+
}
25352541
}
25362542

25372543
void IRGenSILFunction::visitIntegerLiteralInst(swift::IntegerLiteralInst *i) {
@@ -4074,6 +4080,13 @@ void IRGenSILFunction::visitAllocRefDynamicInst(swift::AllocRefDynamicInst *i) {
40744080
}
40754081

40764082
void IRGenSILFunction::visitDeallocStackInst(swift::DeallocStackInst *i) {
4083+
if (auto *closure = dyn_cast<PartialApplyInst>(i->getOperand())) {
4084+
assert(closure->isOnStack());
4085+
auto stackAddr = LoweredPartialApplyAllocations[i->getOperand()];
4086+
emitDeallocateDynamicAlloca(stackAddr);
4087+
return;
4088+
}
4089+
40774090
auto allocatedType = i->getOperand()->getType();
40784091
const TypeInfo &allocatedTI = getTypeInfo(allocatedType);
40794092
StackAddress stackAddr = getLoweredStackAddress(i->getOperand());

0 commit comments

Comments
 (0)