@@ -188,6 +188,34 @@ static SourceLoc getSourceLocFromValue(SILValue value) {
188
188
llvm_unreachable (" Do not know how to get source loc for value?!" );
189
189
}
190
190
191
+ // ===----------------------------------------------------------------------===//
192
+ // Forward Declarations
193
+ // ===----------------------------------------------------------------------===//
194
+
195
+ namespace {
196
+
197
+ enum class DownwardScanResult {
198
+ Invalid,
199
+ Destroy,
200
+ Reinit,
201
+ // NOTE: We use UseForDiagnostic both for defer uses and normal uses.
202
+ UseForDiagnostic,
203
+ MoveOut,
204
+ ClosureConsume,
205
+ ClosureUse,
206
+ };
207
+
208
+ struct ClosureOperandState {
209
+ DownwardScanResult result = DownwardScanResult::Invalid;
210
+ TinyPtrVector<SILInstruction *> pairedConsumingInsts;
211
+ TinyPtrVector<SILInstruction *> pairedUseInsts;
212
+ bool isUpwardsUse = false ;
213
+ bool isUpwardsConsume = false ;
214
+ bool isUpwardsInit = false ;
215
+ };
216
+
217
+ } // namespace
218
+
191
219
// ===----------------------------------------------------------------------===//
192
220
// Use Gathering
193
221
// ===----------------------------------------------------------------------===//
@@ -233,6 +261,8 @@ struct UseState {
233
261
reinits.clear ();
234
262
reinitToIndexMap.clear ();
235
263
}
264
+
265
+ SILFunction *getFunction () const { return address->getFunction (); }
236
266
};
237
267
238
268
// / Visit all of the uses of a lexical lifetime, initializing useState as we go.
@@ -352,18 +382,6 @@ bool GatherLexicalLifetimeUseVisitor::visitUse(Operand *op,
352
382
// Dataflow
353
383
// ===----------------------------------------------------------------------===//
354
384
355
- namespace {
356
-
357
- enum class DownwardScanResult {
358
- Invalid,
359
- Destroy,
360
- Reinit,
361
- UseForDiagnostic,
362
- MoveOut
363
- };
364
-
365
- }
366
-
367
385
// / Returns true if we are move out, false otherwise. If we find an interesting
368
386
// / inst, we return it in foundInst. If no inst is returned, one must continue.
369
387
static DownwardScanResult
@@ -484,6 +502,303 @@ static bool upwardScanForInit(SILInstruction *inst, UseState &useState) {
484
502
return true ;
485
503
}
486
504
505
+ // ===----------------------------------------------------------------------===//
506
+ // Closure Argument Global Dataflow
507
+ // ===----------------------------------------------------------------------===//
508
+
509
+ namespace {
510
+
511
+ // / A utility class that analyzes a closure that captures a moved value. It is
512
+ // / used to perform move checking within the closure as well as to determine a
513
+ // / set of reinit/destroys that we will need to convert to init and or eliminate
514
+ // / while cloning the closure.
515
+ // /
516
+ // / NOTE: We do not need to consider if the closure reinitializes the memory
517
+ // / since there must be some sort of use for the closure to even reference it
518
+ // / and the compiler emits assigns when it reinitializes vars this early in the
519
+ // / pipeline.
520
+ struct ConsumingClosureArgDataflowState {
521
+ SmallVector<SILInstruction *, 32 > livenessWorklist;
522
+ SmallVector<SILInstruction *, 32 > consumingWorklist;
523
+ SmallBlotSetVector<SILInstruction *, 8 > postDominatingConsumingUsers;
524
+ PrunedLiveness livenessForConsumes;
525
+ UseState &useState;
526
+
527
+ public:
528
+ ConsumingClosureArgDataflowState (UseState &useState) : useState(useState) {}
529
+
530
+ bool isPostDominatingConsumingUser (SILInstruction *inst) const {
531
+ return postDominatingConsumingUsers.count (inst);
532
+ }
533
+
534
+ bool process (SILArgument *arg, ClosureOperandState &state);
535
+
536
+ void clear () {
537
+ postDominatingConsumingUsers.clear ();
538
+ livenessForConsumes.clear ();
539
+ }
540
+
541
+ private:
542
+ // / Perform our liveness dataflow. Returns true if we found any liveness uses
543
+ // / at all. These we will need to error upon.
544
+ bool performLivenessDataflow (const BasicBlockSet &initBlocks,
545
+ const BasicBlockSet &livenessBlocks,
546
+ const BasicBlockSet &consumingBlocks);
547
+
548
+ // / Perform our consuming dataflow. Returns true if we found an earliest set
549
+ // / of consuming uses that we can handle that post-dominate the argument.
550
+ // / Returns false otherwise.
551
+ bool performConsumingDataflow (const BasicBlockSet &initBlocks,
552
+ const BasicBlockSet &consumingBlocks);
553
+
554
+ void classifyUses (BasicBlockSet &initBlocks, BasicBlockSet &livenessBlocks,
555
+ BasicBlockSet &consumingBlocks);
556
+
557
+ bool handleSingleBlockCase (SILArgument *address, ClosureOperandState &state);
558
+ };
559
+
560
+ } // namespace
561
+
562
+ bool ConsumingClosureArgDataflowState::handleSingleBlockCase (
563
+ SILArgument *address, ClosureOperandState &state) {
564
+ // Walk the instructions from the beginning of the block to the end.
565
+ for (auto &inst : *address->getParent ()) {
566
+ assert (!useState.inits .count (&inst) &&
567
+ " Shouldn't see an init before a destroy or reinit" );
568
+
569
+ // If we see a destroy, then we know we are upwards consume... stash it so
570
+ // that we can destroy it
571
+ if (auto *dvi = dyn_cast<DestroyValueInst>(&inst)) {
572
+ if (useState.destroyToIndexMap .count (dvi)) {
573
+ state.pairedConsumingInsts .push_back (dvi);
574
+ state.isUpwardsConsume = true ;
575
+ state.result = DownwardScanResult::ClosureConsume;
576
+ return true ;
577
+ }
578
+ }
579
+
580
+ // Same for reinits.
581
+ if (useState.reinits .count (&inst)) {
582
+ state.pairedConsumingInsts .push_back (&inst);
583
+ state.isUpwardsConsume = true ;
584
+ state.result = DownwardScanResult::ClosureConsume;
585
+ return true ;
586
+ }
587
+
588
+ // Finally, if we have a liveness use, report it for a diagnostic.
589
+ if (useState.livenessUses .count (&inst)) {
590
+ state.pairedUseInsts .push_back (&inst);
591
+ state.isUpwardsUse = true ;
592
+ state.result = DownwardScanResult::ClosureUse;
593
+ return true ;
594
+ }
595
+ }
596
+
597
+ return false ;
598
+ }
599
+
600
+ bool ConsumingClosureArgDataflowState::performLivenessDataflow (
601
+ const BasicBlockSet &initBlocks, const BasicBlockSet &livenessBlocks,
602
+ const BasicBlockSet &consumingBlocks) {
603
+ bool foundSingleLivenessUse = false ;
604
+ auto *fn = useState.getFunction ();
605
+ auto *frontBlock = &*fn->begin ();
606
+ BasicBlockWorklist worklist (fn);
607
+ for (unsigned i : indices (livenessWorklist)) {
608
+ auto *&user = livenessWorklist[i];
609
+
610
+ if (frontBlock == user->getParent ())
611
+ continue ;
612
+
613
+ bool success = false ;
614
+ for (auto *predBlock : user->getParent ()->getPredecessorBlocks ()) {
615
+ worklist.pushIfNotVisited (predBlock);
616
+ }
617
+ while (auto *next = worklist.pop ()) {
618
+ if (livenessBlocks.contains (next) || initBlocks.contains (next) ||
619
+ consumingBlocks.contains (next)) {
620
+ continue ;
621
+ }
622
+
623
+ if (frontBlock == next) {
624
+ success = true ;
625
+ foundSingleLivenessUse = true ;
626
+ break ;
627
+ }
628
+
629
+ for (auto *predBlock : next->getPredecessorBlocks ()) {
630
+ worklist.pushIfNotVisited (predBlock);
631
+ }
632
+ }
633
+ if (!success) {
634
+ user = nullptr ;
635
+ }
636
+ }
637
+ return foundSingleLivenessUse;
638
+ }
639
+
640
+ bool ConsumingClosureArgDataflowState::performConsumingDataflow (
641
+ const BasicBlockSet &initBlocks, const BasicBlockSet &consumingBlocks) {
642
+ auto *fn = useState.getFunction ();
643
+ auto *frontBlock = &*fn->begin ();
644
+
645
+ bool foundSingleConsumingUse = false ;
646
+ BasicBlockWorklist worklist (fn);
647
+ for (unsigned i : indices (consumingWorklist)) {
648
+ auto *&user = consumingWorklist[i];
649
+
650
+ if (frontBlock == user->getParent ())
651
+ continue ;
652
+
653
+ bool success = false ;
654
+ for (auto *predBlock : user->getParent ()->getPredecessorBlocks ()) {
655
+ worklist.pushIfNotVisited (predBlock);
656
+ }
657
+ while (auto *next = worklist.pop ()) {
658
+ if (initBlocks.contains (next) || consumingBlocks.contains (next)) {
659
+ continue ;
660
+ }
661
+
662
+ if (frontBlock == next) {
663
+ success = true ;
664
+ foundSingleConsumingUse = true ;
665
+ break ;
666
+ }
667
+
668
+ for (auto *predBlock : next->getPredecessorBlocks ()) {
669
+ worklist.pushIfNotVisited (predBlock);
670
+ }
671
+ }
672
+ if (!success) {
673
+ user = nullptr ;
674
+ }
675
+ }
676
+ return foundSingleConsumingUse;
677
+ }
678
+
679
+ void ConsumingClosureArgDataflowState::classifyUses (
680
+ BasicBlockSet &initBlocks, BasicBlockSet &livenessBlocks,
681
+ BasicBlockSet &consumingBlocks) {
682
+
683
+ for (auto *user : useState.inits ) {
684
+ if (upwardScanForInit (user, useState)) {
685
+ initBlocks.insert (user->getParent ());
686
+ }
687
+ }
688
+
689
+ for (auto *user : useState.livenessUses ) {
690
+ if (upwardScanForUseOut (user, useState)) {
691
+ livenessBlocks.insert (user->getParent ());
692
+ livenessWorklist.push_back (user);
693
+ }
694
+ }
695
+
696
+ for (auto destroyOpt : useState.destroys ) {
697
+ assert (destroyOpt);
698
+
699
+ auto *destroy = *destroyOpt;
700
+
701
+ auto iter = useState.destroyToIndexMap .find (destroy);
702
+ assert (iter != useState.destroyToIndexMap .end ());
703
+
704
+ if (upwardScanForDestroys (destroy, useState)) {
705
+ LLVM_DEBUG (llvm::dbgs () << " Found destroy block at: " << *destroy);
706
+ consumingBlocks.insert (destroy->getParent ());
707
+ consumingWorklist.push_back (destroy);
708
+ }
709
+ }
710
+
711
+ for (auto reinitOpt : useState.reinits ) {
712
+ assert (reinitOpt);
713
+
714
+ auto *reinit = *reinitOpt;
715
+ auto iter = useState.reinitToIndexMap .find (reinit);
716
+ assert (iter != useState.reinitToIndexMap .end ());
717
+
718
+ if (upwardScanForDestroys (reinit, useState)) {
719
+ LLVM_DEBUG (llvm::dbgs () << " Found reinit block at: " << *reinit);
720
+ consumingBlocks.insert (reinit->getParent ());
721
+ consumingWorklist.push_back (reinit);
722
+ }
723
+ }
724
+ }
725
+
726
+ bool ConsumingClosureArgDataflowState::process (SILArgument *address,
727
+ ClosureOperandState &state) {
728
+ clear ();
729
+
730
+ SILFunction *fn = address->getFunction ();
731
+ assert (fn);
732
+
733
+ // First see if our function only has a single block. In such a case,
734
+ // summarize using the single processing routine.
735
+ if (address->getParent ()->getTerminator ()->isFunctionExiting ())
736
+ return handleSingleBlockCase (address, state);
737
+
738
+ // At this point, we begin by classifying the uses of our address into init
739
+ // blocks, liveness blocks, consuming blocks. We also seed the worklist for
740
+ // our two dataflows.
741
+ BasicBlockSet initBlocks (fn);
742
+ BasicBlockSet livenessBlocks (fn);
743
+ BasicBlockSet consumingBlocks (fn);
744
+ classifyUses (initBlocks, livenessBlocks, consumingBlocks);
745
+
746
+ // Liveness Dataflow:
747
+ //
748
+ // The way that we do this is that for each such instruction:
749
+ //
750
+ // 1. If the instruction is in the entrance block, then it is our only answer.
751
+ //
752
+ // 2. If the user is not in the entrance block, visit recursively its
753
+ // predecessor blocks until one either hits the entrance block (in which
754
+ // case this is the result) /or/ one hits a block in one of our basic block
755
+ // sets which means there is an earlier use. Consuming blocks only stop for
756
+ // consuming blocks and init blocks. Liveness blocks stop for all other
757
+ // blocks.
758
+ //
759
+ // The result is what remains in our set. Thus we start by processing
760
+ // liveness.
761
+ if (performLivenessDataflow (initBlocks, livenessBlocks, consumingBlocks)) {
762
+ for (unsigned i : indices (livenessWorklist)) {
763
+ if (auto *ptr = livenessWorklist[i]) {
764
+ state.pairedUseInsts .push_back (ptr);
765
+ }
766
+ }
767
+ state.isUpwardsUse = true ;
768
+ state.result = DownwardScanResult::ClosureUse;
769
+ return true ;
770
+ }
771
+
772
+ // Then perform the consuming use dataflow. In this case, we think we may have
773
+ // found a set of post-dominating consuming uses for our inout_aliasable
774
+ // parameter. We are going to change it to be an out parameter and eliminate
775
+ // these when we clone the closure.
776
+ if (performConsumingDataflow (initBlocks, consumingBlocks)) {
777
+ SWIFT_DEFER { livenessForConsumes.clear (); };
778
+ auto *frontBlock = &*fn->begin ();
779
+ livenessForConsumes.initializeDefBlock (frontBlock);
780
+
781
+ for (unsigned i : indices (livenessWorklist)) {
782
+ if (auto *ptr = livenessWorklist[i]) {
783
+ state.pairedConsumingInsts .push_back (ptr);
784
+ livenessForConsumes.updateForUse (ptr, true /* is lifetime ending*/ );
785
+ }
786
+ }
787
+
788
+ // If our consumes do not have a linear lifetime, bail. We will error on the
789
+ // move being unknown.
790
+ for (auto *ptr : state.pairedConsumingInsts ) {
791
+ if (livenessForConsumes.isWithinBoundary (ptr))
792
+ return false ;
793
+ postDominatingConsumingUsers.insert (ptr);
794
+ }
795
+ state.isUpwardsConsume = true ;
796
+ state.result = DownwardScanResult::ClosureConsume;
797
+ return true ;
798
+ }
799
+
800
+ return true ;
801
+ }
487
802
// ===----------------------------------------------------------------------===//
488
803
// Global Dataflow
489
804
// ===----------------------------------------------------------------------===//
@@ -815,6 +1130,9 @@ static bool performSingleBasicBlockAnalysis(DataflowState &dataflowState,
815
1130
auto &useState = dataflowState.useState ;
816
1131
SILInstruction *interestingUser = nullptr ;
817
1132
switch (downwardScanForMoveOut (mvi, useState, &interestingUser)) {
1133
+ case DownwardScanResult::ClosureConsume:
1134
+ case DownwardScanResult::ClosureUse:
1135
+ llvm_unreachable (" unhandled" );
818
1136
case DownwardScanResult::Invalid:
819
1137
llvm_unreachable (" invalid" );
820
1138
case DownwardScanResult::Destroy: {
0 commit comments