@@ -50,25 +50,38 @@ class CapturePropagation : public SILFunctionTransform
5050};
5151} // end anonymous namespace
5252
53- static LiteralInst *getConstant (SILValue V) {
53+ static SILInstruction *getConstant (SILValue V) {
5454 if (auto I = dyn_cast<ThinToThickFunctionInst>(V))
5555 return getConstant (I->getOperand ());
5656 if (auto I = dyn_cast<ConvertFunctionInst>(V))
5757 return getConstant (I->getOperand ());
58- return dyn_cast<LiteralInst>(V);
59- }
6058
61- static bool isOptimizableConstant (SILValue V) {
62- // We do not optimize string literals of length > 32 since we would need to
63- // encode them into the symbol name for uniqueness.
64- if (auto *SLI = dyn_cast<StringLiteralInst>(V))
65- return SLI->getValue ().size () <= 32 ;
66- return true ;
67- }
59+ if (auto *SLI = dyn_cast<StringLiteralInst>(V)) {
60+ // We do not optimize string literals of length > 32 since we would need to
61+ // encode them into the symbol name for uniqueness.
62+ if (SLI->getValue ().size () > 32 )
63+ return nullptr ;
64+ return SLI;
65+ }
66+
67+ if (auto *lit = dyn_cast<LiteralInst>(V))
68+ return lit;
6869
69- static bool isConstant (SILValue V) {
70- V = getConstant (V);
71- return V && isOptimizableConstant (V);
70+ if (auto *kp = dyn_cast<KeyPathInst>(V)) {
71+ // We could support operands, if they are constants, to enable propagation
72+ // of subscript keypaths. This would require to add the operands in the
73+ // mangling scheme.
74+ // But currently it's not worth it because we do not optimize subscript
75+ // keypaths in SILCombine.
76+ if (kp->getNumOperands () != 0 )
77+ return nullptr ;
78+ if (!kp->hasPattern ())
79+ return nullptr ;
80+ if (kp->getSubstitutions ().hasAnySubstitutableParams ())
81+ return nullptr ;
82+ return kp;
83+ }
84+ return nullptr ;
7285}
7386
7487static std::string getClonedName (PartialApplyInst *PAI, IsSerialized_t Serialized,
@@ -194,11 +207,15 @@ void CapturePropagationCloner::cloneClosure(
194207 // Replace the rest of the old arguments with constants.
195208 getBuilder ().setInsertionPoint (ClonedEntryBB);
196209 IsCloningConstant = true ;
210+ llvm::SmallVector<KeyPathInst *, 8 > toDestroy;
197211 for (SILValue PartialApplyArg : PartialApplyArgs) {
198- assert (isConstant (PartialApplyArg) &&
212+ assert (getConstant (PartialApplyArg) &&
199213 " expected a constant arg to partial apply" );
200214
201215 cloneConstValue (PartialApplyArg);
216+ if (auto *kp = dyn_cast<KeyPathInst>(getMappedValue (PartialApplyArg))) {
217+ toDestroy.push_back (kp);
218+ }
202219
203220 // The PartialApplyArg from the caller is now mapped to its cloned
204221 // instruction. Also map the original argument to the cloned instruction.
@@ -213,6 +230,22 @@ void CapturePropagationCloner::cloneClosure(
213230 // Visit original BBs in depth-first preorder, starting with the
214231 // entry block, cloning all instructions and terminators.
215232 cloneFunctionBody (OrigF, ClonedEntryBB, entryArgs);
233+
234+ // Destroy all the inserted keypaths at the function exits.
235+ for (KeyPathInst *kpToDestroy : toDestroy) {
236+ SILLocation loc = RegularLocation::getAutoGeneratedLocation ();
237+ for (SILBasicBlock &clonedBB : CloneF) {
238+ TermInst *term = clonedBB.getTerminator ();
239+ if (term->isFunctionExiting ()) {
240+ SILBuilder builder (term);
241+ if (CloneF.hasOwnership ()) {
242+ builder.createDestroyValue (loc, kpToDestroy);
243+ } else {
244+ builder.createStrongRelease (loc, kpToDestroy, builder.getDefaultAtomicity ());
245+ }
246+ }
247+ }
248+ }
216249}
217250
218251CanSILFunctionType getPartialApplyInterfaceResultType (PartialApplyInst *PAI) {
@@ -305,6 +338,20 @@ void CapturePropagation::rewritePartialApply(PartialApplyInst *OrigPAI,
305338 LLVM_DEBUG (llvm::dbgs () << " Rewrote caller:\n " << *T2TF);
306339}
307340
341+ static bool isKeyPathFunction (FullApplySite FAS, SILValue keyPath) {
342+ SILFunction *callee = FAS.getReferencedFunctionOrNull ();
343+ if (!callee)
344+ return false ;
345+ if (callee->getName () == " swift_setAtWritableKeyPath" ||
346+ callee->getName () == " swift_setAtReferenceWritableKeyPath" ) {
347+ return FAS.getArgument (1 ) == keyPath;
348+ }
349+ if (callee->getName () == " swift_getAtKeyPath" ) {
350+ return FAS.getArgument (2 ) == keyPath;
351+ }
352+ return false ;
353+ }
354+
308355// / For now, we conservative only specialize if doing so can eliminate dynamic
309356// / dispatch.
310357// /
@@ -316,6 +363,8 @@ static bool isProfitable(SILFunction *Callee) {
316363 if (FullApplySite FAS = FullApplySite::isa (Operand->getUser ())) {
317364 if (FAS.getCallee () == Operand->get ())
318365 return true ;
366+ if (isKeyPathFunction (FAS, Arg))
367+ return true ;
319368 }
320369 }
321370 }
@@ -477,9 +526,35 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) {
477526 }
478527
479528 // Second possibility: Are all partially applied arguments constant?
480- for (auto Arg : PAI->getArguments ()) {
481- if (!isConstant (Arg))
529+ llvm::SmallVector<SILInstruction *, 8 > toDelete;
530+ for (const Operand &argOp : PAI->getArgumentOperands ()) {
531+ SILInstruction *constInst = getConstant (argOp.get ());
532+ if (!constInst)
482533 return false ;
534+ if (auto *kp = dyn_cast<KeyPathInst>(constInst)) {
535+ auto argConv = ApplySite (PAI).getArgumentConvention (argOp).Value ;
536+ // Only handle the common case of a guaranteed keypath arguments. That
537+ // refers to the callee function.
538+ if (argConv != SILArgumentConvention::Direct_Guaranteed)
539+ return false ;
540+
541+ // For escaping closures:
542+ // To keep things simple, we don't do a liferange analysis to insert
543+ // compensating destroys of the keypath.
544+ // Instead we require that the PAI is the only use of the keypath (= the
545+ // common case). This allows us to just delete the now unused keypath
546+ // instruction.
547+ //
548+ // For non-escaping closures:
549+ // The keypath is not consumed by the PAI. We don't need todelete the
550+ // keypath instruction in this pass, but let dead-object-elimination clean
551+ // it up later.
552+ if (!PAI->isOnStack ()) {
553+ if (getSingleNonDebugUser (kp) != PAI)
554+ return false ;
555+ toDelete.push_back (kp);
556+ }
557+ }
483558 }
484559 if (!isProfitable (SubstF))
485560 return false ;
@@ -491,6 +566,8 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) {
491566 SILFunction *NewF = specializeConstClosure (PAI, SubstF);
492567 rewritePartialApply (PAI, NewF);
493568
569+ recursivelyDeleteTriviallyDeadInstructions (toDelete, /* force*/ true );
570+
494571 addFunctionToPassManagerWorklist (NewF, SubstF);
495572 return true ;
496573}
0 commit comments