@@ -363,6 +363,61 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
363
363
return curr;
364
364
}
365
365
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
+
366
421
// / Rewrite a partial_apply convert_escape_to_noescape sequence with a single
367
422
// / apply/try_apply user to a partial_apply [stack] terminated with a
368
423
// / dealloc_stack placed after the apply.
@@ -386,12 +441,15 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
386
441
// / dealloc_stack still needs to be balanced with other dealloc_stacks i.e the
387
442
// / caller needs to use the StackNesting utility to update the dealloc_stack
388
443
// / 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,
393
447
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized) {
394
-
448
+
449
+ auto *origPA = dyn_cast<PartialApplyInst>(skipConvert (cvt->getOperand ()));
450
+ if (!origPA)
451
+ return SILValue ();
452
+
395
453
auto *convertOrPartialApply = cast<SingleValueInstruction>(origPA);
396
454
if (cvt->getOperand () != origPA)
397
455
convertOrPartialApply = cast<ConvertFunctionInst>(cvt->getOperand ());
@@ -415,7 +473,7 @@ static bool tryRewriteToPartialApplyStack(
415
473
continue ;
416
474
}
417
475
if (singleNonDebugNonRefCountUser)
418
- return false ;
476
+ return SILValue () ;
419
477
singleNonDebugNonRefCountUser = user;
420
478
}
421
479
@@ -480,39 +538,15 @@ static bool tryRewriteToPartialApplyStack(
480
538
}
481
539
}
482
540
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);
492
546
// 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;
516
550
}
517
551
518
552
static bool tryExtendLifetimeToLastUse (
@@ -525,45 +559,52 @@ static bool tryExtendLifetimeToLastUse(
525
559
if (!singleUser)
526
560
return false ;
527
561
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))
532
567
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
+ }
541
575
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 ();
563
591
}
564
592
return true ;
565
593
}
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 ;
567
608
}
568
609
569
610
// / Ensure the lifetime of the closure across a two step optional conversion
0 commit comments