@@ -368,15 +368,39 @@ static bool isReinitToInitConvertibleInst(SILInstruction *memInst) {
368
368
}
369
369
}
370
370
371
- // / Returns true if \p value a function argument from an inout argument or a
372
- // / value extracted from a closure captured box that we did not convert to an
373
- // / address.
371
+ using ScopeRequiringFinalInit = DiagnosticEmitter::ScopeRequiringFinalInit;
372
+
373
+ // / If \p markedAddr's operand must be initialized at the end of the scope it
374
+ // / introduces, visit those scope ending ends.
375
+ // /
376
+ // / Examples:
377
+ // / (1) inout function argument. Must be initialized at function exit.
378
+ // /
379
+ // / sil [ossa] @f : $(inout MOV) -> ()
380
+ // / entry(%addr : $*MOV):
381
+ // / ...
382
+ // / return %t : $() // %addr must be initialized here
383
+ // /
384
+ // / (2) coroutine. Must be initialized at end_apply/abort_apply.
385
+ // /
386
+ // / (%addr, %token) = begin_apply ... -> @yields @inout MOV
387
+ // / bbN:
388
+ // / end_apply %token // %addr must be initialized here
389
+ // / bbM:
390
+ // / abort_apply %token // %addr must be initialized here
391
+ // /
392
+ // / (3) modify access. Must be initialized at end_access.
393
+ // /
394
+ // / %addr = begin_access [modify] %location
374
395
// /
375
- // / These are cases where we want to treat the end of the function as a liveness
376
- // / use to ensure that we reinitialize \p value before the end of the function
377
- // / if we consume \p value in the function body.
378
- static bool isInOutDefThatNeedsEndOfFunctionLiveness (
379
- MarkUnresolvedNonCopyableValueInst *markedAddr) {
396
+ // / end_access %addr // %addr must be initialized here
397
+ // /
398
+ // / To enforce this requirement, function exiting instructions are treated as
399
+ // / liveness uses of such addresses, ensuring that the address is initialized at
400
+ // / that point.
401
+ static bool visitScopeEndsRequiringInit (
402
+ MarkUnresolvedNonCopyableValueInst *markedAddr,
403
+ llvm::function_ref<void (SILInstruction *, ScopeRequiringFinalInit)> visit) {
380
404
SILValue operand = markedAddr->getOperand ();
381
405
382
406
// TODO: This should really be a property of the marker instruction.
@@ -403,17 +427,32 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(
403
427
case SILArgumentConvention::Indirect_InoutAliasable:
404
428
case SILArgumentConvention::Pack_Inout:
405
429
LLVM_DEBUG (llvm::dbgs () << " Found inout arg: " << *fArg );
430
+ SmallVector<SILBasicBlock *, 8 > exitBlocks;
431
+ markedAddr->getFunction ()->findExitingBlocks (exitBlocks);
432
+ for (auto *block : exitBlocks) {
433
+ visit (block->getTerminator (), ScopeRequiringFinalInit::InoutArgument);
434
+ }
406
435
return true ;
407
436
}
408
437
}
409
438
// Check for yields from a modify coroutine.
410
439
if (auto bai =
411
440
dyn_cast_or_null<BeginApplyInst>(operand->getDefiningInstruction ())) {
441
+ for (auto *inst : bai->getTokenResult ()->getUsers ()) {
442
+ assert (isa<EndApplyInst>(inst) || isa<AbortApplyInst>(inst));
443
+ visit (inst, ScopeRequiringFinalInit::Coroutine);
444
+ }
412
445
return true ;
413
446
}
414
447
// Check for modify accesses.
415
448
if (auto access = dyn_cast<BeginAccessInst>(operand)) {
416
- return access->getAccessKind () == SILAccessKind::Modify;
449
+ if (access->getAccessKind () != SILAccessKind::Modify) {
450
+ return false ;
451
+ }
452
+ for (auto *inst : access->getEndAccesses ()) {
453
+ visit (inst, ScopeRequiringFinalInit::ModifyMemoryAccess);
454
+ }
455
+ return true ;
417
456
}
418
457
419
458
return false ;
@@ -534,12 +573,15 @@ struct UseState {
534
573
// / The set of drop_deinits of this mark_unresolved_non_copyable_value
535
574
llvm::SmallSetVector<SILInstruction *, 2 > dropDeinitInsts;
536
575
537
- // / A "inout terminator use" is an implicit liveness use of the entire value
538
- // / placed on a terminator. We use this both so we add liveness for the
539
- // / terminator user and so that we can use the set to quickly identify later
540
- // / while emitting diagnostics that a liveness use is a terminator user and
541
- // / emit a specific diagnostic message.
542
- llvm::SmallSetVector<SILInstruction *, 2 > implicitEndOfLifetimeLivenessUses;
576
+ // / Instructions indicating the end of a scope at which addr must be
577
+ // / initialized.
578
+ // /
579
+ // / Adding such instructions to liveness forces the value to be initialized at
580
+ // / them as required.
581
+ // /
582
+ // / See visitScopeEndsRequiringInit.
583
+ llvm::MapVector<SILInstruction *, ScopeRequiringFinalInit>
584
+ scopeEndsRequiringInit;
543
585
544
586
// / We add debug_values to liveness late after we diagnose, but before we
545
587
// / hoist destroys to ensure that we do not hoist destroys out of access
@@ -604,8 +646,13 @@ struct UseState {
604
646
// / instruction.
605
647
// / 2. In the case of a ref_element_addr or a global, this will contain the
606
648
// / end_access.
607
- bool isImplicitEndOfLifetimeLivenessUses (SILInstruction *inst) const {
608
- return implicitEndOfLifetimeLivenessUses.count (inst);
649
+ llvm::Optional<ScopeRequiringFinalInit>
650
+ isImplicitEndOfLifetimeLivenessUses (SILInstruction *inst) const {
651
+ auto iter = scopeEndsRequiringInit.find (inst);
652
+ if (iter == scopeEndsRequiringInit.end ()) {
653
+ return llvm::None;
654
+ }
655
+ return {iter->second };
609
656
}
610
657
611
658
// / Returns true if the given instruction is within the same block as a reinit
@@ -647,7 +694,7 @@ struct UseState {
647
694
reinitInsts.clear ();
648
695
reinitToValueMultiMap.reset ();
649
696
dropDeinitInsts.clear ();
650
- implicitEndOfLifetimeLivenessUses .clear ();
697
+ scopeEndsRequiringInit .clear ();
651
698
debugValue = nullptr ;
652
699
}
653
700
@@ -686,8 +733,8 @@ struct UseState {
686
733
llvm::dbgs () << *inst;
687
734
}
688
735
llvm::dbgs () << " Implicit End Of Lifetime Liveness Users:\n " ;
689
- for (auto *inst : implicitEndOfLifetimeLivenessUses ) {
690
- llvm::dbgs () << *inst ;
736
+ for (auto pair : scopeEndsRequiringInit ) {
737
+ llvm::dbgs () << pair. first ;
691
738
}
692
739
llvm::dbgs () << " Debug Value User:\n " ;
693
740
if (debugValue) {
@@ -724,25 +771,20 @@ struct UseState {
724
771
initializeLiveness (FieldSensitiveMultiDefPrunedLiveRange &prunedLiveness);
725
772
726
773
void initializeImplicitEndOfLifetimeLivenessUses () {
727
- if (isInOutDefThatNeedsEndOfFunctionLiveness (address)) {
728
- SmallVector<SILBasicBlock *, 8 > exitBlocks;
729
- address->getFunction ()->findExitingBlocks (exitBlocks);
730
- for (auto *block : exitBlocks) {
731
- LLVM_DEBUG (llvm::dbgs () << " Adding term as liveness user: "
732
- << *block->getTerminator ());
733
- implicitEndOfLifetimeLivenessUses.insert (block->getTerminator ());
734
- }
735
- return ;
736
- }
737
-
774
+ visitScopeEndsRequiringInit (address, [&](auto *inst, auto kind) {
775
+ LLVM_DEBUG (llvm::dbgs ()
776
+ << " Adding scope end as liveness user: " << *inst);
777
+ scopeEndsRequiringInit[inst] = kind;
778
+ });
738
779
if (address->getCheckKind () == MarkUnresolvedNonCopyableValueInst::
739
780
CheckKind::AssignableButNotConsumable) {
740
781
if (auto *bai = dyn_cast<BeginAccessInst>(address->getOperand ())) {
741
782
for (auto *eai : bai->getEndAccesses ()) {
742
783
LLVM_DEBUG (llvm::dbgs () << " Adding end_access as implicit end of "
743
784
" lifetime liveness user: "
744
785
<< *eai);
745
- implicitEndOfLifetimeLivenessUses.insert (eai);
786
+ scopeEndsRequiringInit[eai] =
787
+ ScopeRequiringFinalInit::ModifyMemoryAccess;
746
788
}
747
789
}
748
790
}
@@ -804,7 +846,7 @@ struct UseState {
804
846
// An "inout terminator use" is an implicit liveness use of the entire
805
847
// value. This is because we need to ensure that our inout value is
806
848
// reinitialized along exit paths.
807
- if (implicitEndOfLifetimeLivenessUses .count (inst))
849
+ if (scopeEndsRequiringInit .count (inst))
808
850
return true ;
809
851
810
852
return false ;
@@ -1191,10 +1233,10 @@ void UseState::initializeLiveness(
1191
1233
// ref_element_addr or global_addr, add a liveness use of the entire value on
1192
1234
// the implicit end lifetime instruction. For inout this is terminators for
1193
1235
// ref_element_addr, global_addr it is the end_access instruction.
1194
- for (auto *inst : implicitEndOfLifetimeLivenessUses ) {
1195
- liveness.updateForUse (inst , TypeTreeLeafTypeRange (address),
1236
+ for (auto pair : scopeEndsRequiringInit ) {
1237
+ liveness.updateForUse (pair. first , TypeTreeLeafTypeRange (address),
1196
1238
false /* lifetime ending*/ );
1197
- LLVM_DEBUG (llvm::dbgs () << " Added liveness for inoutTermUser : " << *inst ;
1239
+ LLVM_DEBUG (llvm::dbgs () << " Added liveness for scope end : " << pair. first ;
1198
1240
liveness.print (llvm::dbgs ()));
1199
1241
}
1200
1242
0 commit comments