Skip to content

Commit 5d7861f

Browse files
committed
Support non-generic nonescaping partial_applys using an on-stack tuple
1 parent 8b08a74 commit 5d7861f

File tree

3 files changed

+159
-70
lines changed

3 files changed

+159
-70
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,11 +461,11 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
461461
locationStorage = loc.storage;
462462
}
463463

464-
/// Return the next instruction or nullptr if this is the last instruction in
465-
/// its block.
464+
/// Return the previous instruction, or nullptr if this is the first
465+
/// instruction in its block.
466466
SILInstruction *getPreviousInstruction();
467467

468-
/// Return the previous instruction or nullptr if this is the first
468+
/// Return the next instruction, or nullptr if this is the final
469469
/// instruction in its block.
470470
SILInstruction *getNextInstruction();
471471

lib/SILOptimizer/Transforms/PartialApplySimplification.cpp

Lines changed: 113 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,16 @@
4343

4444
STATISTIC(NumInvocationFunctionsChanged,
4545
"Number of invocation functions rewritten");
46-
STATISTIC(NumStaticPartialApplicationForwarders,
47-
"Number of static partial application forwarder thunks generated");
46+
STATISTIC(NumUnsupportedChangesToInvocationFunctions,
47+
"Number of invocation functions that could be rewritten, but aren't yet");
48+
STATISTIC(NumPartialApplyCalleesWithNonPartialApplyUses,
49+
"Number of invocation functions with non-partial_apply uses");
50+
STATISTIC(NumPartialApplyCalleesPossiblyUsedExternally,
51+
"Number of invocation functions possibly used externally");
52+
STATISTIC(NumPartialApplyCalleesDeclarationOnly,
53+
"Number of invocation functions that are declaration-only");
54+
STATISTIC(NumPartialApplyCalleesWithMismatchedPartialApplies,
55+
"Number of invocation functions that have mismatched partial_apply sites");
4856
STATISTIC(NumDynamicPartialApplicationForwarders,
4957
"Number of dynamic partial application forwarder thunks generated");
5058

@@ -61,8 +69,8 @@ struct KnownCallee {
6169
llvm::SetVector<FunctionRefInst *> FunctionRefs;
6270
/// The set of partial application sites.
6371
llvm::SetVector<PartialApplyInst *> PartialApplications;
64-
/// Whether the callee has non-partial-apply uses.
65-
bool HasNonPartialApplyUses = false;
72+
/// If the callee has a non-partial-apply use, this points to an arbitrary one.
73+
SILInstruction *NonPartialApplyUse = nullptr;
6674
};
6775

6876
class PartialApplySimplificationPass : public SILModuleTransform {
@@ -128,7 +136,9 @@ static bool isSimplePartialApply(PartialApplyInst *i) {
128136
auto argTy = i->getArguments()[0]->getType();
129137

130138
if (i->getFunctionType()->isNoEscape()) {
131-
// TODO
139+
if (argTy.isAddress()) {
140+
return true;
141+
}
132142
return false;
133143
} else {
134144
if (!argTy.isObject()) {
@@ -166,7 +176,7 @@ void PartialApplySimplificationPass::scanFunction(SILFunction *f,
166176
}
167177

168178
// Record if the function has uses that aren't partial applies.
169-
knownCallee.HasNonPartialApplyUses = true;
179+
knownCallee.NonPartialApplyUse = frUse->getUser();
170180
}
171181
}
172182

@@ -197,8 +207,10 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
197207

198208
// If the subject of the partial application has other uses that aren't
199209
// partial applications, then thunk it.
200-
if (pa.HasNonPartialApplyUses) {
201-
LLVM_DEBUG(llvm::dbgs() << "Callee has non-partial_apply uses; thunking\n");
210+
if (pa.NonPartialApplyUse) {
211+
LLVM_DEBUG(llvm::dbgs() << "Callee has non-partial_apply uses; thunking\n";
212+
pa.NonPartialApplyUse->print(llvm::dbgs()));
213+
++NumPartialApplyCalleesWithNonPartialApplyUses;
202214
goto create_forwarding_thunks;
203215
}
204216

@@ -207,10 +219,12 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
207219
// signature. We'll always use forwarding thunks in this case.
208220
if (callee->isPossiblyUsedExternally()) {
209221
LLVM_DEBUG(llvm::dbgs() << "Callee is possibly used externally; thunking\n");
222+
++NumPartialApplyCalleesPossiblyUsedExternally;
210223
goto create_forwarding_thunks;
211224
}
212225
if (callee->empty()) {
213226
LLVM_DEBUG(llvm::dbgs() << "Callee is a declaration only; thunking\n");
227+
++NumPartialApplyCalleesDeclarationOnly;
214228
goto create_forwarding_thunks;
215229
}
216230

@@ -229,11 +243,15 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
229243
i != e;
230244
++i) {
231245
auto thisPA = *i;
232-
if (examplePA->getType() != thisPA->getType()) {
246+
if (examplePA->getNumArguments() != thisPA->getNumArguments()
247+
|| examplePA->getFunctionType()->getCalleeConvention()
248+
!= thisPA->getFunctionType()->getCalleeConvention()
249+
|| !examplePA->getFunctionType()->getExtInfo()
250+
.isEqualTo(thisPA->getFunctionType()->getExtInfo(), true)) {
233251
LLVM_DEBUG(llvm::dbgs() << "Mismatched partial application arguments; thunking:\n";
234252
thisPA->print(llvm::dbgs());
235253
examplePA->print(llvm::dbgs()));
236-
254+
++NumPartialApplyCalleesWithMismatchedPartialApplies;
237255
goto create_forwarding_thunks;
238256
}
239257
}
@@ -290,44 +308,49 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
290308

291309
// Instead of the applied arguments, we receive a box containing the
292310
// values for those arguments. Work out what that box type is.
293-
// TODO: We need a representation of boxes that are nonescaping and/or
311+
// TODO: We need a representation of boxes that
294312
// capture the generic environment to represent partial applications in
295313
// their full generality.
296-
if (examplePA->getFunctionType()->isNoEscape()) {
297-
LLVM_DEBUG(llvm::dbgs() << "TODO: Nonescaping partial_apply not yet implemented\n");
298-
return;
299-
}
300314
if (origTy->getInvocationGenericSignature()) {
301315
LLVM_DEBUG(llvm::dbgs() << "TODO: generic partial_apply not yet implemented\n");
316+
++NumUnsupportedChangesToInvocationFunctions;
302317
return;
303318
}
304319

305-
// TODO: SILBoxType is only implemented for a single field right now, so
306-
// represent the captures as a tuple.
307-
#if MULTI_FIELD_BOXES_ARE_SUPPORTED
308-
auto newBoxLayout = SILLayout::get(C,
309-
origTy->getInvocationGenericSignature(),
310-
boxFields,
311-
/*capturesGenerics*/ !origTy->getInvocationGenericSignature().isNull());
312-
#else
320+
// TODO: SILBoxType is only implemented for a single field right now, and we
321+
// don't yet have a corresponding type for nonescaping captures, so
322+
// represent the captures as a tuple for now.
313323
llvm::SmallVector<TupleTypeElt, 4> tupleElts;
314324
for (auto field : boxFields) {
315325
tupleElts.push_back(TupleTypeElt(field.getLoweredType()));
316326
}
317327
auto tupleTy = TupleType::get(tupleElts, C)->getCanonicalType();
318-
SILField tupleField(tupleTy, /*mutable*/ false);
319328

320-
auto newBoxLayout = SILLayout::get(C,
321-
origTy->getInvocationGenericSignature(),
322-
tupleField,
323-
/*capturesGenerics*/ false);
324-
#endif
325-
SubstitutionMap identitySubstitutionMap;
326-
if (auto origSig = origTy->getInvocationGenericSignature()) {
327-
identitySubstitutionMap = origSig->getIdentitySubstitutionMap();
329+
CanType contextTy;
330+
SILParameterInfo contextParam;
331+
332+
bool isNoEscape = examplePA->getFunctionType()->isNoEscape();
333+
if (isNoEscape) {
334+
contextTy = tupleTy;
335+
// Nonescaping closures borrow their context from the outer frame.
336+
contextParam = SILParameterInfo(contextTy,
337+
ParameterConvention::Indirect_In_Guaranteed);
338+
} else {
339+
SILField tupleField(tupleTy, /*mutable*/ false);
340+
auto newBoxLayout = SILLayout::get(C,
341+
origTy->getInvocationGenericSignature(),
342+
tupleField,
343+
/*capturesGenerics*/ false);
344+
SubstitutionMap identitySubstitutionMap;
345+
if (auto origSig = origTy->getInvocationGenericSignature()) {
346+
identitySubstitutionMap = origSig->getIdentitySubstitutionMap();
347+
}
348+
contextTy = SILBoxType::get(C, newBoxLayout, identitySubstitutionMap);
349+
contextParam = SILParameterInfo(contextTy,
350+
paResultTy->getCalleeConvention());
328351
}
329-
auto newBoxTy = SILBoxType::get(C, newBoxLayout, identitySubstitutionMap);
330-
newParams.push_back(SILParameterInfo(newBoxTy, paResultTy->getCalleeConvention()));
352+
353+
newParams.push_back(contextParam);
331354

332355
auto newExtInfo = origTy->getExtInfo()
333356
.withRepresentation(SILFunctionTypeRepresentation::Method);
@@ -352,22 +375,25 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
352375
callee->rewriteLoweredTypeUnsafe(newTy);
353376

354377
// Update the entry block.
355-
auto boxConvention = examplePA->getFunctionType()->getCalleeConvention();
356378
{
357379
SILBuilder B(*callee);
358380
auto &entry = *callee->begin();
359381

360-
// Insert an argument for the box before the originally applied args.
361-
auto boxArgTy = callee->mapTypeIntoContext(
362-
SILType::getPrimitiveObjectType(newBoxTy));
363-
ValueOwnershipKind boxOwnership(*callee, boxArgTy,
364-
SILArgumentConvention(boxConvention));
382+
// Insert an argument for the context before the originally applied args.
383+
auto contextArgTy = callee->mapTypeIntoContext(
384+
SILType::getPrimitiveObjectType(contextTy));
385+
if (isIndirectFormalParameter(contextParam.getConvention())) {
386+
contextArgTy = contextArgTy.getAddressType();
387+
}
365388

389+
ValueOwnershipKind contextOwnership(*callee, contextArgTy,
390+
SILArgumentConvention(contextParam.getConvention()));
391+
366392
auto numUnappliedArgs = numUnapplied + origTy->getNumIndirectFormalResults();
367393

368-
auto boxArg = entry.insertFunctionArgument(numUnappliedArgs,
369-
boxArgTy,
370-
boxOwnership);
394+
auto contextArg = entry.insertFunctionArgument(numUnappliedArgs,
395+
contextArgTy,
396+
contextOwnership);
371397
auto appliedBBArgs = entry.getArguments().slice(numUnappliedArgs + 1);
372398

373399
// Replace the original arguments applied by the partial_apply, by
@@ -379,15 +405,16 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
379405
auto appliedArg = appliedBBArgs[i];
380406
auto param = partiallyAppliedParams[i];
381407

382-
#if MULTI_FIELD_BOXES_ARE_SUPPORTED
383-
SILValue proj = B.createProjectBox(loc, boxArg, i);
384-
#else
385-
SILValue proj = B.createProjectBox(loc, boxArg, 0);
408+
SILValue proj;
409+
if (isNoEscape) {
410+
proj = contextArg;
411+
} else {
412+
proj = B.createProjectBox(loc, contextArg, 0);
413+
}
386414
if (boxFields.size() > 1) {
387415
proj = B.createTupleElementAddr(loc, proj, i);
388416
}
389-
#endif
390-
// Load the value out of the box according to the current ownership
417+
// Load the value out of the context according to the current ownership
391418
// mode of the function and the calling convention for the parameter.
392419
SILValue projectedArg;
393420
if (callee->hasOwnership()) {
@@ -466,7 +493,7 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
466493
case ParameterConvention::Indirect_InoutAliasable: {
467494
// The box capture is a RawPointer with the value of the capture
468495
// address.
469-
auto ptrVal = B.createLoad(loc, proj, LoadOwnershipQualifier::Trivial);
496+
auto ptrVal = B.createLoad(loc, proj, LoadOwnershipQualifier::Unqualified);
470497
projectedArg = B.createPointerToAddress(loc, ptrVal,
471498
appliedArg->getType(),
472499
/*strict*/ conv == ParameterConvention::Indirect_Inout);
@@ -480,11 +507,11 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
480507
}
481508

482509
// If the box is callee-consumed, we can release it now.
483-
if (boxConvention == ParameterConvention::Direct_Owned) {
510+
if (contextParam.getConvention() == ParameterConvention::Direct_Owned) {
484511
if (callee->hasOwnership()) {
485-
B.createDestroyValue(loc, boxArg);
512+
B.createDestroyValue(loc, contextArg);
486513
} else {
487-
B.createStrongRelease(loc, boxArg, Atomicity::Atomic);
514+
B.createStrongRelease(loc, contextArg, Atomicity::Atomic);
488515
}
489516
}
490517

@@ -509,24 +536,39 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
509536
B.setInsertionPoint(pa);
510537

511538
auto newFunctionRef = B.createFunctionRef(loc, callee);
512-
auto boxTy = SILBoxType::get(C, newBoxLayout, pa->getSubstitutionMap());
513-
auto newBox = B.createAllocBox(loc, boxTy,
514-
/*debug variable*/ None,
515-
/*dynamic lifetime*/ false,
516-
/*reflection*/ true);
539+
SILValue contextBuffer, contextProj;
540+
auto contextStorageTy = SILType::getPrimitiveAddressType(contextTy)
541+
.subst(getModule()->Types, pa->getSubstitutionMap());
542+
if (isNoEscape) {
543+
auto contextAlloc = B.createAllocStack(loc, contextStorageTy);
544+
contextBuffer = contextProj = contextAlloc;
545+
546+
// We'll need to deallocate the context buffer after the end of the
547+
// original partial_apply's lifetime.
548+
auto deallocStackUses = pa->getUsersOfType<DeallocStackInst>();
549+
assert(deallocStackUses.begin() != deallocStackUses.end());
550+
for (auto use : deallocStackUses) {
551+
B.setInsertionPoint(use->getNextInstruction());
552+
B.createDeallocStack(loc, contextBuffer);
553+
}
554+
B.setInsertionPoint(contextAlloc->getNextInstruction());
555+
} else {
556+
contextBuffer = B.createAllocBox(loc,
557+
contextStorageTy.castTo<SILBoxType>(),
558+
/*debug variable*/ None,
559+
/*dynamic lifetime*/ false,
560+
/*reflection*/ true);
561+
contextProj = B.createProjectBox(loc, contextBuffer, 0);
562+
}
517563

518564
// Transfer the formerly partially-applied arguments into the box.
519565
auto appliedArgs = pa->getArguments();
520566
for (unsigned i = 0; i < appliedArgs.size(); ++i) {
521567
auto arg = appliedArgs[i];
522-
#if MULTI_FIELD_BOXES_ARE_SUPPORTED
523-
SILValue proj = B.createProjectBox(loc, newBox, i);
524-
#else
525-
SILValue proj = B.createProjectBox(loc, newBox, 0);
568+
SILValue proj = contextProj;
526569
if (boxFields.size() > 1) {
527570
proj = B.createTupleElementAddr(loc, proj, i);
528571
}
529-
#endif
530572
auto param = partiallyAppliedParams[i];
531573

532574
switch (auto conv = param.getConvention()) {
@@ -563,10 +605,15 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
563605
}
564606

565607
// Partially apply the new box to create the closure.
608+
auto paConvention = isNoEscape ? ParameterConvention::Direct_Guaranteed
609+
: contextParam.getConvention();
610+
auto paOnStack = isNoEscape ? PartialApplyInst::OnStack
611+
: PartialApplyInst::NotOnStack;
566612
auto newPA = B.createPartialApply(loc, newFunctionRef,
567613
pa->getSubstitutionMap(),
568-
SILValue(newBox),
569-
boxConvention);
614+
contextBuffer,
615+
paConvention,
616+
paOnStack);
570617
assert(isSimplePartialApply(newPA)
571618
&& "partial apply wasn't simple after transformation?");
572619
pa->replaceAllUsesWith(newPA);
@@ -592,6 +639,7 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
592639

593640
void PartialApplySimplificationPass::processDynamicCallee(PartialApplyInst *pa){
594641
// TODO
642+
++NumDynamicPartialApplicationForwarders;
595643
}
596644

597645
SILTransform *swift::createPartialApplySimplification() {

0 commit comments

Comments
 (0)