@@ -363,6 +363,61 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
363363 return curr;
364364}
365365
366+ // / Returns the (single) "endAsyncLet" builtin if \p startAsyncLet is a
367+ // / "startAsyncLet" builtin.
368+ static BuiltinInst *getEndAsyncLet (BuiltinInst *startAsyncLet) {
369+ if (startAsyncLet->getBuiltinKind () != BuiltinValueKind::StartAsyncLet)
370+ return nullptr ;
371+
372+ BuiltinInst *endAsyncLet = nullptr ;
373+ for (Operand *op : startAsyncLet->getUses ()) {
374+ auto *endBI = dyn_cast<BuiltinInst>(op->getUser ());
375+ if (endBI && endBI->getBuiltinKind () == BuiltinValueKind::EndAsyncLet) {
376+ // At this stage of the pipeline, it's always the case that a
377+ // startAsyncLet has an endAsyncLet: that's how SILGen generates it.
378+ // Just to be on the safe side, do this check.
379+ if (endAsyncLet)
380+ return nullptr ;
381+ endAsyncLet = endBI;
382+ }
383+ }
384+ return endAsyncLet;
385+ }
386+
387+ // / Call the \p insertFn with a builder at all insertion points after
388+ // / a closure is used by \p closureUser.
389+ static void insertAfterClosureUser (SILInstruction *closureUser,
390+ function_ref<void (SILBuilder &)> insertFn) {
391+ // Don't insert any destroy or deallocation right before an unreachable.
392+ // It's not needed an will only add up to code size.
393+ auto insertAtNonUnreachable = [&](SILBuilder &builder) {
394+ if (isa<UnreachableInst>(builder.getInsertionPoint ()))
395+ return ;
396+ insertFn (builder);
397+ };
398+
399+ if (auto *startAsyncLet = dyn_cast<BuiltinInst>(closureUser)) {
400+ BuiltinInst *endAsyncLet = getEndAsyncLet (startAsyncLet);
401+ assert (endAsyncLet);
402+ SILBuilderWithScope builder (std::next (endAsyncLet->getIterator ()));
403+ insertAtNonUnreachable (builder);
404+ return ;
405+ }
406+ FullApplySite fas = FullApplySite::isa (closureUser);
407+ assert (fas);
408+ fas.insertAfterFullEvaluation (insertAtNonUnreachable);
409+ }
410+
411+ static SILValue skipConvert (SILValue v) {
412+ auto *cvt = dyn_cast<ConvertFunctionInst>(v);
413+ if (!cvt)
414+ return v;
415+ auto *pa = dyn_cast<PartialApplyInst>(cvt->getOperand ());
416+ if (!pa || !pa->hasOneUse ())
417+ return v;
418+ return pa;
419+ }
420+
366421// / Rewrite a partial_apply convert_escape_to_noescape sequence with a single
367422// / apply/try_apply user to a partial_apply [stack] terminated with a
368423// / dealloc_stack placed after the apply.
@@ -386,12 +441,15 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
386441// / dealloc_stack still needs to be balanced with other dealloc_stacks i.e the
387442// / caller needs to use the StackNesting utility to update the dealloc_stack
388443// / nesting.
389- static bool tryRewriteToPartialApplyStack (
390- SILLocation &loc, PartialApplyInst *origPA,
391- ConvertEscapeToNoEscapeInst *cvt, SILInstruction *singleApplyUser,
392- SILBasicBlock::iterator &advanceIfDelete,
444+ static SILValue tryRewriteToPartialApplyStack (
445+ ConvertEscapeToNoEscapeInst *cvt,
446+ SILInstruction *closureUser, SILBasicBlock::iterator &advanceIfDelete,
393447 llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized) {
394-
448+
449+ auto *origPA = dyn_cast<PartialApplyInst>(skipConvert (cvt->getOperand ()));
450+ if (!origPA)
451+ return SILValue ();
452+
395453 auto *convertOrPartialApply = cast<SingleValueInstruction>(origPA);
396454 if (cvt->getOperand () != origPA)
397455 convertOrPartialApply = cast<ConvertFunctionInst>(cvt->getOperand ());
@@ -415,7 +473,7 @@ static bool tryRewriteToPartialApplyStack(
415473 continue ;
416474 }
417475 if (singleNonDebugNonRefCountUser)
418- return false ;
476+ return SILValue () ;
419477 singleNonDebugNonRefCountUser = user;
420478 }
421479
@@ -480,39 +538,15 @@ static bool tryRewriteToPartialApplyStack(
480538 }
481539 }
482540
483- // Insert destroys of arguments after the apply and the dealloc_stack.
484- if (auto *apply = dyn_cast<ApplyInst>(singleApplyUser)) {
485- auto insertPt = std::next (SILBasicBlock::iterator (apply));
486- // Don't insert dealloc_stacks at unreachable.
487- if (isa<UnreachableInst>(*insertPt))
488- return true ;
489- SILBuilderWithScope b3 (insertPt);
490- b3.createDeallocStack (loc, newPA);
491- insertDestroyOfCapturedArguments (newPA, b3);
541+ // Insert destroys of arguments after the closure user and the dealloc_stack.
542+ insertAfterClosureUser (closureUser, [newPA](SILBuilder &builder) {
543+ auto loc = RegularLocation (builder.getInsertionPointLoc ());
544+ builder.createDeallocStack (loc, newPA);
545+ insertDestroyOfCapturedArguments (newPA, builder);
492546 // dealloc_stack of the in_guaranteed capture is inserted
493- insertDeallocOfCapturedArguments (newPA, b3);
494- } else if (auto *tai = dyn_cast<TryApplyInst>(singleApplyUser)) {
495- for (auto *succBB : tai->getSuccessorBlocks ()) {
496- SILBuilderWithScope b3 (succBB->begin ());
497- b3.createDeallocStack (loc, newPA);
498- insertDestroyOfCapturedArguments (newPA, b3);
499- // dealloc_stack of the in_guaranteed capture is inserted
500- insertDeallocOfCapturedArguments (newPA, b3);
501- }
502- } else {
503- llvm_unreachable (" Unknown FullApplySite instruction kind" );
504- }
505- return true ;
506- }
507-
508- static SILValue skipConvert (SILValue v) {
509- auto *cvt = dyn_cast<ConvertFunctionInst>(v);
510- if (!cvt)
511- return v;
512- auto *pa = dyn_cast<PartialApplyInst>(cvt->getOperand ());
513- if (!pa || !pa->hasOneUse ())
514- return v;
515- return pa;
547+ insertDeallocOfCapturedArguments (newPA, builder);
548+ });
549+ return closure;
516550}
517551
518552static bool tryExtendLifetimeToLastUse (
@@ -525,45 +559,52 @@ static bool tryExtendLifetimeToLastUse(
525559 if (!singleUser)
526560 return false ;
527561
528- // Handle an apply.
529- if (auto singleApplyUser = FullApplySite::isa (singleUser)) {
530- // FIXME: Don't know how-to handle begin_apply/end_apply yet.
531- if (isa<BeginApplyInst>(singleApplyUser.getInstruction ())) {
562+ // Handle apply instructions and startAsyncLet.
563+ BuiltinInst *endAsyncLet = nullptr ;
564+ if (FullApplySite::isa (singleUser)) {
565+ // TODO: Enable begin_apply/end_apply. It should work, but is not tested yet.
566+ if (isa<BeginApplyInst>(singleUser))
532567 return false ;
533- }
534-
535- auto loc = RegularLocation::getAutoGeneratedLocation ();
536- auto origPA = dyn_cast<PartialApplyInst>(skipConvert (cvt->getOperand ()));
537- if (origPA && tryRewriteToPartialApplyStack (
538- loc, origPA, cvt, singleApplyUser.getInstruction (),
539- advanceIfDelete, memoized))
540- return true ;
568+ } else if (auto *bi = dyn_cast<BuiltinInst>(singleUser)) {
569+ endAsyncLet = getEndAsyncLet (bi);
570+ if (!endAsyncLet)
571+ return false ;
572+ } else {
573+ return false ;
574+ }
541575
542- // Insert a copy at the convert_escape_to_noescape [not_guaranteed] and
543- // change the instruction to the guaranteed form.
544- auto escapingClosure = cvt->getOperand ();
545- auto *closureCopy =
546- SILBuilderWithScope (cvt).createCopyValue (loc, escapingClosure);
547- cvt->setLifetimeGuaranteed ();
548- cvt->setOperand (closureCopy);
549-
550- // Insert a destroy after the apply.
551- if (auto *apply = dyn_cast<ApplyInst>(singleApplyUser.getInstruction ())) {
552- auto insertPt = std::next (SILBasicBlock::iterator (apply));
553- SILBuilderWithScope (insertPt).createDestroyValue (loc, closureCopy);
554-
555- } else if (auto *tai =
556- dyn_cast<TryApplyInst>(singleApplyUser.getInstruction ())) {
557- for (auto *succBB : tai->getSuccessorBlocks ()) {
558- SILBuilderWithScope (succBB->begin ())
559- .createDestroyValue (loc, closureCopy);
560- }
561- } else {
562- llvm_unreachable (" Unknown FullApplySite instruction kind" );
576+ if (SILValue closure = tryRewriteToPartialApplyStack (cvt, singleUser,
577+ advanceIfDelete, memoized)) {
578+ if (auto *cfi = dyn_cast<ConvertFunctionInst>(closure))
579+ closure = cfi->getOperand ();
580+ if (endAsyncLet && isa<MarkDependenceInst>(closure)) {
581+ // Add the top-level mark_dependence (which keeps the closure arguments
582+ // alive) as a second operand to the endAsyncLet builtin.
583+ // This ensures that the closure arguments are kept alive until the
584+ // endAsyncLet builtin.
585+ assert (endAsyncLet->getNumOperands () == 1 );
586+ SILBuilderWithScope builder (endAsyncLet);
587+ builder.createBuiltin (endAsyncLet->getLoc (), endAsyncLet->getName (),
588+ endAsyncLet->getType (), endAsyncLet->getSubstitutions (),
589+ {endAsyncLet->getOperand (0 ), closure});
590+ endAsyncLet->eraseFromParent ();
563591 }
564592 return true ;
565593 }
566- return false ;
594+
595+ // Insert a copy at the convert_escape_to_noescape [not_guaranteed] and
596+ // change the instruction to the guaranteed form.
597+ auto escapingClosure = cvt->getOperand ();
598+ auto *closureCopy =
599+ SILBuilderWithScope (cvt).createCopyValue (cvt->getLoc (), escapingClosure);
600+ cvt->setLifetimeGuaranteed ();
601+ cvt->setOperand (closureCopy);
602+
603+ insertAfterClosureUser (singleUser, [closureCopy](SILBuilder &builder) {
604+ auto loc = RegularLocation (builder.getInsertionPointLoc ());
605+ builder.createDestroyValue (loc, closureCopy);
606+ });
607+ return true ;
567608}
568609
569610// / Ensure the lifetime of the closure across a two step optional conversion
0 commit comments