@@ -31,6 +31,8 @@ STATISTIC(NumCapturesPropagated, "Number of constant captures propagated");
3131namespace {
3232// / Propagate constants through closure captures by specializing the partially
3333// / applied function.
34+ // / Also optimize away partial_apply instructions where all partially applied
35+ // / arguments are dead.
3436class CapturePropagation : public SILFunctionTransform
3537{
3638public:
@@ -258,15 +260,11 @@ void CapturePropagation::rewritePartialApply(PartialApplyInst *OrigPAI,
258260 SILFunction *SpecialF) {
259261 SILBuilderWithScope Builder (OrigPAI);
260262 auto FuncRef = Builder.createFunctionRef (OrigPAI->getLoc (), SpecialF);
261- auto NewPAI = Builder.createPartialApply (OrigPAI->getLoc (),
262- FuncRef,
263- SpecialF->getLoweredType (),
264- ArrayRef<Substitution>(),
265- ArrayRef<SILValue>(),
266- OrigPAI->getType ());
267- OrigPAI->replaceAllUsesWith (NewPAI);
263+ auto *T2TF = Builder.createThinToThickFunction (OrigPAI->getLoc (),
264+ FuncRef, OrigPAI->getType ());
265+ OrigPAI->replaceAllUsesWith (T2TF);
268266 recursivelyDeleteTriviallyDeadInstructions (OrigPAI, true );
269- DEBUG (llvm::dbgs () << " Rewrote caller:\n " << *NewPAI );
267+ DEBUG (llvm::dbgs () << " Rewrote caller:\n " << *T2TF );
270268}
271269
272270// / For now, we conservative only specialize if doing so can eliminate dynamic
@@ -286,6 +284,92 @@ static bool isProfitable(SILFunction *Callee) {
286284 return false ;
287285}
288286
287+ // / Returns true if block \p BB only contains a return or throw of the first
288+ // / block argument and side-effect-free instructions.
289+ static bool isArgReturnOrThrow (SILBasicBlock *BB) {
290+ for (SILInstruction &I : *BB) {
291+ if (isa<ReturnInst>(&I) || isa<ThrowInst>(&I)) {
292+ SILValue RetVal = I.getOperand (0 );
293+ if (BB->getNumBBArg () == 1 && RetVal == BB->getBBArg (0 ))
294+ return true ;
295+ return false ;
296+ }
297+ if (I.mayHaveSideEffects () || isa<TermInst>(&I))
298+ return false ;
299+ }
300+ llvm_unreachable (" should have seen a terminator instruction" );
301+ }
302+
303+ // / Checks if \p Orig is a thunk which calls another function but without
304+ // / passing the trailing \p numDeadParams dead parameters.
305+ static SILFunction *getSpecializedWithDeadParams (SILFunction *Orig,
306+ int numDeadParams) {
307+ SILBasicBlock &EntryBB = *Orig->begin ();
308+ unsigned NumArgs = EntryBB.getNumBBArg ();
309+ SILModule &M = Orig->getModule ();
310+
311+ // Check if all dead parameters have trivial types. We don't support non-
312+ // trivial types because it's very hard to find places where we can release
313+ // those parameters (as a replacement for the removed partial_apply).
314+ // TODO: maybe we can skip this restrication when we have semantic ARC.
315+ for (unsigned Idx = NumArgs - numDeadParams; Idx < NumArgs; ++Idx) {
316+ SILType ArgTy = EntryBB.getBBArg (Idx)->getType ();
317+ if (!ArgTy.isTrivial (M))
318+ return nullptr ;
319+ }
320+ SILFunction *Specialized = nullptr ;
321+ SILValue RetValue;
322+
323+ // Check all instruction of the entry block.
324+ for (SILInstruction &I : EntryBB) {
325+ if (auto FAS = FullApplySite::isa (&I)) {
326+
327+ // Check if this is the call of the specialized function.
328+ // As the original function is not generic, also the specialized function
329+ // must be not generic.
330+ if (FAS.hasSubstitutions ())
331+ return nullptr ;
332+ // Is it the only call?
333+ if (Specialized)
334+ return nullptr ;
335+
336+ Specialized = FAS.getReferencedFunction ();
337+ if (!Specialized)
338+ return nullptr ;
339+
340+ // Check if parameters are passes 1-to-1
341+ unsigned NumArgs = FAS.getNumArguments ();
342+ if (EntryBB.getNumBBArg () - numDeadParams != NumArgs)
343+ return nullptr ;
344+
345+ for (unsigned Idx = 0 ; Idx < NumArgs; ++Idx) {
346+ if (FAS.getArgument (Idx) != (ValueBase *)EntryBB.getBBArg (Idx))
347+ return nullptr ;
348+ }
349+
350+ if (TryApplyInst *TAI = dyn_cast<TryApplyInst>(&I)) {
351+ // Check the normal and throw blocks of the try_apply.
352+ if (isArgReturnOrThrow (TAI->getNormalBB ()) &&
353+ isArgReturnOrThrow (TAI->getErrorBB ()))
354+ return Specialized;
355+ return nullptr ;
356+ }
357+ assert (isa<ApplyInst>(&I) && " unknown FullApplySite instruction" );
358+ RetValue = &I;
359+ continue ;
360+ }
361+ if (auto *RI = dyn_cast<ReturnInst>(&I)) {
362+ // Check if we return the result of the apply.
363+ if (RI->getOperand () != RetValue)
364+ return nullptr ;
365+ continue ;
366+ }
367+ if (I.mayHaveSideEffects () || isa<TermInst>(&I))
368+ return nullptr ;
369+ }
370+ return Specialized;
371+ }
372+
289373bool CapturePropagation::optimizePartialApply (PartialApplyInst *PAI) {
290374 // Check if the partial_apply has generic substitutions.
291375 // FIXME: We could handle generic thunks if it's worthwhile.
@@ -295,15 +379,26 @@ bool CapturePropagation::optimizePartialApply(PartialApplyInst *PAI) {
295379 SILFunction *SubstF = PAI->getReferencedFunction ();
296380 if (!SubstF)
297381 return false ;
382+ if (SubstF->isExternalDeclaration ())
383+ return false ;
298384
299385 assert (!SubstF->getLoweredFunctionType ()->isPolymorphic () &&
300386 " cannot specialize generic partial apply" );
301387
388+ // First possibility: Is it a partial_apply where all partially applied
389+ // arguments are dead?
390+ if (SILFunction *NewFunc = getSpecializedWithDeadParams (SubstF,
391+ PAI->getNumArguments ())) {
392+ rewritePartialApply (PAI, NewFunc);
393+ return true ;
394+ }
395+
396+ // Second possibility: Are all partially applied arguments constant?
302397 for (auto Arg : PAI->getArguments ()) {
303398 if (!isConstant (Arg))
304399 return false ;
305400 }
306- if (SubstF-> isExternalDeclaration () || !isProfitable (SubstF))
401+ if (!isProfitable (SubstF))
307402 return false ;
308403
309404 DEBUG (llvm::dbgs () << " Specializing closure for constant arguments:\n "
0 commit comments