@@ -50,25 +50,38 @@ class CapturePropagation : public SILFunctionTransform
50
50
};
51
51
} // end anonymous namespace
52
52
53
- static LiteralInst *getConstant (SILValue V) {
53
+ static SILInstruction *getConstant (SILValue V) {
54
54
if (auto I = dyn_cast<ThinToThickFunctionInst>(V))
55
55
return getConstant (I->getOperand ());
56
56
if (auto I = dyn_cast<ConvertFunctionInst>(V))
57
57
return getConstant (I->getOperand ());
58
- return dyn_cast<LiteralInst>(V);
59
- }
60
58
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;
68
69
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 ;
72
85
}
73
86
74
87
static std::string getClonedName (PartialApplyInst *PAI, IsSerialized_t Serialized,
@@ -194,11 +207,15 @@ void CapturePropagationCloner::cloneClosure(
194
207
// Replace the rest of the old arguments with constants.
195
208
getBuilder ().setInsertionPoint (ClonedEntryBB);
196
209
IsCloningConstant = true ;
210
+ llvm::SmallVector<KeyPathInst *, 8 > toDestroy;
197
211
for (SILValue PartialApplyArg : PartialApplyArgs) {
198
- assert (isConstant (PartialApplyArg) &&
212
+ assert (getConstant (PartialApplyArg) &&
199
213
" expected a constant arg to partial apply" );
200
214
201
215
cloneConstValue (PartialApplyArg);
216
+ if (auto *kp = dyn_cast<KeyPathInst>(getMappedValue (PartialApplyArg))) {
217
+ toDestroy.push_back (kp);
218
+ }
202
219
203
220
// The PartialApplyArg from the caller is now mapped to its cloned
204
221
// instruction. Also map the original argument to the cloned instruction.
@@ -213,6 +230,22 @@ void CapturePropagationCloner::cloneClosure(
213
230
// Visit original BBs in depth-first preorder, starting with the
214
231
// entry block, cloning all instructions and terminators.
215
232
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
+ }
216
249
}
217
250
218
251
CanSILFunctionType getPartialApplyInterfaceResultType (PartialApplyInst *PAI) {
@@ -305,6 +338,20 @@ void CapturePropagation::rewritePartialApply(PartialApplyInst *OrigPAI,
305
338
LLVM_DEBUG (llvm::dbgs () << " Rewrote caller:\n " << *T2TF);
306
339
}
307
340
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
+
308
355
// / For now, we conservative only specialize if doing so can eliminate dynamic
309
356
// / dispatch.
310
357
// /
@@ -316,6 +363,8 @@ static bool isProfitable(SILFunction *Callee) {
316
363
if (FullApplySite FAS = FullApplySite::isa (Operand->getUser ())) {
317
364
if (FAS.getCallee () == Operand->get ())
318
365
return true ;
366
+ if (isKeyPathFunction (FAS, Arg))
367
+ return true ;
319
368
}
320
369
}
321
370
}
@@ -477,9 +526,35 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) {
477
526
}
478
527
479
528
// 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)
482
533
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
+ }
483
558
}
484
559
if (!isProfitable (SubstF))
485
560
return false ;
@@ -491,6 +566,8 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) {
491
566
SILFunction *NewF = specializeConstClosure (PAI, SubstF);
492
567
rewritePartialApply (PAI, NewF);
493
568
569
+ recursivelyDeleteTriviallyDeadInstructions (toDelete, /* force*/ true );
570
+
494
571
addFunctionToPassManagerWorklist (NewF, SubstF);
495
572
return true ;
496
573
}
0 commit comments