@@ -634,6 +634,15 @@ namespace {
634
634
struct UseState {
635
635
MarkMustCheckInst *address;
636
636
637
+ // / The number of fields in the exploded type. Set in initializeLiveness.
638
+ unsigned fieldCount = UINT_MAX;
639
+
640
+ // / The blocks that consume fields of the value.
641
+ // /
642
+ // / A map from blocks to a bit vector recording which fields were destroyed
643
+ // / in each.
644
+ llvm::SmallMapVector<SILBasicBlock *, SmallBitVector, 8 > consumingBlocks;
645
+
637
646
// / A map from destroy_addr to the part of the type that it destroys.
638
647
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4 > destroys;
639
648
@@ -742,6 +751,7 @@ struct UseState {
742
751
743
752
void clear () {
744
753
address = nullptr ;
754
+ consumingBlocks.clear ();
745
755
destroys.clear ();
746
756
livenessUses.clear ();
747
757
borrows.clear ();
@@ -798,6 +808,16 @@ struct UseState {
798
808
}
799
809
}
800
810
811
+ void recordConsumingBlock (SILBasicBlock *block, TypeTreeLeafTypeRange range) {
812
+ if (consumingBlocks.find (block) == consumingBlocks.end ()) {
813
+ consumingBlocks.insert ({block, SmallBitVector (fieldCount)});
814
+ }
815
+ auto &vector = consumingBlocks[block];
816
+ for (auto field : range.getRange ()) {
817
+ vector.set (field);
818
+ }
819
+ }
820
+
801
821
void
802
822
initializeLiveness (FieldSensitiveMultiDefPrunedLiveRange &prunedLiveness);
803
823
@@ -899,6 +919,8 @@ struct UseState {
899
919
900
920
void UseState::initializeLiveness (
901
921
FieldSensitiveMultiDefPrunedLiveRange &liveness) {
922
+ fieldCount = liveness.getNumSubElements ();
923
+
902
924
// We begin by initializing all of our init uses.
903
925
for (auto initInstAndValue : initInsts) {
904
926
LLVM_DEBUG (llvm::dbgs () << " Found def: " << *initInstAndValue.first );
@@ -1005,6 +1027,8 @@ void UseState::initializeLiveness(
1005
1027
if (!isReinitToInitConvertibleInst (reinitInstAndValue.first )) {
1006
1028
liveness.updateForUse (reinitInstAndValue.first , reinitInstAndValue.second ,
1007
1029
false /* lifetime ending*/ );
1030
+ recordConsumingBlock (reinitInstAndValue.first ->getParent (),
1031
+ reinitInstAndValue.second );
1008
1032
LLVM_DEBUG (llvm::dbgs () << " Added liveness for reinit: "
1009
1033
<< *reinitInstAndValue.first ;
1010
1034
liveness.print (llvm::dbgs ()));
@@ -1016,18 +1040,27 @@ void UseState::initializeLiveness(
1016
1040
for (auto takeInstAndValue : takeInsts) {
1017
1041
liveness.updateForUse (takeInstAndValue.first , takeInstAndValue.second ,
1018
1042
true /* lifetime ending*/ );
1043
+ recordConsumingBlock (takeInstAndValue.first ->getParent (),
1044
+ takeInstAndValue.second );
1019
1045
LLVM_DEBUG (llvm::dbgs ()
1020
1046
<< " Added liveness for take: " << *takeInstAndValue.first ;
1021
1047
liveness.print (llvm::dbgs ()));
1022
1048
}
1023
1049
for (auto copyInstAndValue : copyInsts) {
1024
1050
liveness.updateForUse (copyInstAndValue.first , copyInstAndValue.second ,
1025
1051
true /* lifetime ending*/ );
1052
+ recordConsumingBlock (copyInstAndValue.first ->getParent (),
1053
+ copyInstAndValue.second );
1026
1054
LLVM_DEBUG (llvm::dbgs ()
1027
1055
<< " Added liveness for copy: " << *copyInstAndValue.first ;
1028
1056
liveness.print (llvm::dbgs ()));
1029
1057
}
1030
1058
1059
+ for (auto destroyInstAndValue : destroys) {
1060
+ recordConsumingBlock (destroyInstAndValue.first ->getParent (),
1061
+ destroyInstAndValue.second );
1062
+ }
1063
+
1031
1064
// Do the same for our borrow and liveness insts.
1032
1065
for (auto livenessInstAndValue : borrows) {
1033
1066
liveness.updateForUse (livenessInstAndValue.first ,
@@ -1303,6 +1336,44 @@ struct MoveOnlyAddressCheckerPImpl {
1303
1336
void handleSingleBlockDestroy (SILInstruction *destroy, bool isReinit);
1304
1337
};
1305
1338
1339
+ class ExtendUnconsumedLiveness {
1340
+ UseState addressUseState;
1341
+ FieldSensitiveMultiDefPrunedLiveRange &liveness;
1342
+ FieldSensitivePrunedLivenessBoundary &boundary;
1343
+
1344
+ enum class DestroyKind {
1345
+ Destroy,
1346
+ Take,
1347
+ Reinit,
1348
+ };
1349
+ using DestroysCollection =
1350
+ llvm::SmallMapVector<SILInstruction *, DestroyKind, 8 >;
1351
+ using ConsumingBlocksCollection = SmallPtrSetVector<SILBasicBlock *, 8 >;
1352
+
1353
+ public:
1354
+ ExtendUnconsumedLiveness (UseState addressUseState,
1355
+ FieldSensitiveMultiDefPrunedLiveRange &liveness,
1356
+ FieldSensitivePrunedLivenessBoundary &boundary)
1357
+ : addressUseState(addressUseState), liveness(liveness),
1358
+ boundary (boundary) {}
1359
+
1360
+ void run ();
1361
+
1362
+ void runOnField (unsigned element, DestroysCollection &destroys,
1363
+ ConsumingBlocksCollection &consumingBlocks);
1364
+
1365
+ private:
1366
+ bool hasDefAfter (SILInstruction *inst, unsigned element);
1367
+
1368
+ bool
1369
+ shouldAddDestroyToLiveness (SILInstruction *destroy, unsigned element,
1370
+ BasicBlockSet const &consumedAtExitBlocks,
1371
+ BasicBlockSetVector const &consumedAtEntryBlocks);
1372
+
1373
+ void addPreviousInstructionToLiveness (SILInstruction *inst, unsigned element,
1374
+ bool lifetimeEnding);
1375
+ };
1376
+
1306
1377
} // namespace
1307
1378
1308
1379
// ===----------------------------------------------------------------------===//
@@ -2634,6 +2705,249 @@ void MoveOnlyAddressCheckerPImpl::rewriteUses(
2634
2705
#endif
2635
2706
}
2636
2707
2708
+ void ExtendUnconsumedLiveness::run () {
2709
+ ConsumingBlocksCollection consumingBlocks;
2710
+ DestroysCollection destroys;
2711
+ for (unsigned element = 0 , count = liveness.getNumSubElements ();
2712
+ element < count; ++element) {
2713
+
2714
+ for (auto pair : addressUseState.consumingBlocks ) {
2715
+ if (pair.second .test (element)) {
2716
+ consumingBlocks.insert (pair.first );
2717
+ }
2718
+ }
2719
+
2720
+ for (auto pair : addressUseState.destroys ) {
2721
+ if (pair.second .contains (element)) {
2722
+ destroys[pair.first ] = DestroyKind::Destroy;
2723
+ }
2724
+ }
2725
+ for (auto pair : addressUseState.takeInsts ) {
2726
+ if (pair.second .contains (element)) {
2727
+ destroys[pair.first ] = DestroyKind::Take;
2728
+ }
2729
+ }
2730
+ for (auto pair : addressUseState.reinitInsts ) {
2731
+ if (pair.second .contains (element)) {
2732
+ destroys[pair.first ] = DestroyKind::Reinit;
2733
+ }
2734
+ }
2735
+
2736
+ runOnField (element, destroys, consumingBlocks);
2737
+
2738
+ consumingBlocks.clear ();
2739
+ destroys.clear ();
2740
+ }
2741
+ }
2742
+
2743
+ // / Extend liveness of each field as far as possible within the original live
2744
+ // / range as far as possible without incurring any copies.
2745
+ // /
2746
+ // / The strategy has two parts.
2747
+ // /
2748
+ // / (1) The global analysis:
2749
+ // / - Collect the blocks in which the field was live before canonicalization.
2750
+ // / These are the "original" live blocks (originalLiveBlocks).
2751
+ // / [Color these blocks green.]
2752
+ // / - From within that collection, collect the blocks which contain a _final_
2753
+ // / consuming, non-destroy use, and their iterative successors.
2754
+ // / These are the "consumed" blocks (consumedAtExitBlocks).
2755
+ // / [Color these blocks red.]
2756
+ // / - Extend liveness down to the boundary between originalLiveBlocks and
2757
+ // / consumedAtExitBlocks blocks.
2758
+ // / [Extend liveness down to the boundary between green blocks and red.]
2759
+ // / - In particular, in regions of originalLiveBlocks which have no boundary
2760
+ // / with consumedAtExitBlocks, liveness should be extended to its original
2761
+ // / extent.
2762
+ // / [Extend liveness down to the boundary between green blocks and uncolored.]
2763
+ // /
2764
+ // / (2) The local analysis:
2765
+ // / - For in-block lifetimes, extend liveness forward from non-consuming uses
2766
+ // / and dead defs to the original destroy.
2767
+ void ExtendUnconsumedLiveness::runOnField (
2768
+ unsigned element, DestroysCollection &destroys,
2769
+ ConsumingBlocksCollection &consumingBlocks) {
2770
+ SILValue currentDef = addressUseState.address ;
2771
+
2772
+ // First, collect the blocks that were _originally_ live. We can't use
2773
+ // liveness here because it doesn't include blocks that occur before a
2774
+ // destroy_addr.
2775
+ BasicBlockSet originalLiveBlocks (currentDef->getFunction ());
2776
+ {
2777
+ // Some of the work here was already done by initializeLiveness.
2778
+ // Specifically, it already discovered all blocks containing (transitive)
2779
+ // uses and blocks that appear between them and the def.
2780
+ //
2781
+ // Seed the set with what it already discovered.
2782
+ for (auto *discoveredBlock : liveness.getDiscoveredBlocks ())
2783
+ originalLiveBlocks.insert (discoveredBlock);
2784
+
2785
+ // Start the walk from the consuming blocks (which includes destroys as well
2786
+ // as the other consuming uses).
2787
+ BasicBlockWorklist worklist (currentDef->getFunction ());
2788
+ for (auto *consumingBlock : consumingBlocks) {
2789
+ worklist.push (consumingBlock);
2790
+ }
2791
+
2792
+ // Walk backwards from consuming blocks.
2793
+ while (auto *block = worklist.pop ()) {
2794
+ if (!originalLiveBlocks.insert (block)) {
2795
+ continue ;
2796
+ }
2797
+ for (auto *predecessor : block->getPredecessorBlocks ()) {
2798
+ // If the block was discovered by liveness, we already added it to the
2799
+ // set.
2800
+ if (originalLiveBlocks.contains (predecessor))
2801
+ continue ;
2802
+ worklist.pushIfNotVisited (predecessor);
2803
+ }
2804
+ }
2805
+ }
2806
+
2807
+ // Second, collect the blocks which occur after a consuming use.
2808
+ BasicBlockSet consumedAtExitBlocks (currentDef->getFunction ());
2809
+ BasicBlockSetVector consumedAtEntryBlocks (currentDef->getFunction ());
2810
+ {
2811
+ // Start the forward walk from blocks which contain non-destroy consumes not
2812
+ // followed by defs.
2813
+ //
2814
+ // Because they contain a consume not followed by a def, these are
2815
+ // consumed-at-exit.
2816
+ BasicBlockWorklist worklist (currentDef->getFunction ());
2817
+ for (auto iterator : boundary.getLastUsers ()) {
2818
+ if (!iterator.second .test (element))
2819
+ continue ;
2820
+ auto *instruction = iterator.first ;
2821
+ // Skip over destroys on the boundary.
2822
+ auto iter = destroys.find (instruction);
2823
+ if (iter != destroys.end () && iter->second != DestroyKind::Take) {
2824
+ continue ;
2825
+ }
2826
+ // Skip over non-consuming users.
2827
+ auto pair = liveness.isInterestingUser (instruction);
2828
+ assert (pair.first !=
2829
+ FieldSensitivePrunedLiveness::IsInterestingUser::NonUser);
2830
+ if (pair.first !=
2831
+ FieldSensitivePrunedLiveness::IsInterestingUser::LifetimeEndingUse) {
2832
+ continue ;
2833
+ }
2834
+ // Skip over users that aren't users of this element.
2835
+ assert (pair.second .has_value ());
2836
+ if (!pair.second ->contains (element)) {
2837
+ continue ;
2838
+ }
2839
+ // A consume with a subsequent def doesn't cause the block to be
2840
+ // consumed-at-exit.
2841
+ if (hasDefAfter (instruction, element))
2842
+ continue ;
2843
+ worklist.push (instruction->getParent ());
2844
+ }
2845
+ while (auto *block = worklist.pop ()) {
2846
+ consumedAtExitBlocks.insert (block);
2847
+ for (auto *successor : block->getSuccessorBlocks ()) {
2848
+ if (!originalLiveBlocks.contains (successor))
2849
+ continue ;
2850
+ worklist.pushIfNotVisited (successor);
2851
+ consumedAtEntryBlocks.insert (successor);
2852
+ }
2853
+ }
2854
+ }
2855
+
2856
+ // Third, find the blocks on the boundary between the originally-live blocks
2857
+ // and the originally-live-but-consumed blocks. Extend liveness "to the end"
2858
+ // of these blocks.
2859
+ for (auto *block : consumedAtEntryBlocks) {
2860
+ for (auto *predecessor : block->getPredecessorBlocks ()) {
2861
+ if (consumedAtExitBlocks.contains (predecessor))
2862
+ continue ;
2863
+ // Add "the instruction(s) before the terminator" of the predecessor to
2864
+ // liveness.
2865
+ addPreviousInstructionToLiveness (predecessor->getTerminator (), element,
2866
+ /* lifetimeEnding*/ false );
2867
+ }
2868
+ }
2869
+
2870
+ // Finally, preserve the destroys which weren't in the consumed region in
2871
+ // place: hoisting such destroys would not avoid copies.
2872
+ for (auto pair : destroys) {
2873
+ auto *destroy = pair.first ;
2874
+ if (!shouldAddDestroyToLiveness (destroy, element, consumedAtExitBlocks,
2875
+ consumedAtEntryBlocks))
2876
+ continue ;
2877
+ addPreviousInstructionToLiveness (destroy, element,
2878
+ /* lifetimeEnding*/ false );
2879
+ }
2880
+ }
2881
+
2882
+ bool ExtendUnconsumedLiveness::shouldAddDestroyToLiveness (
2883
+ SILInstruction *destroy, unsigned element,
2884
+ BasicBlockSet const &consumedAtExitBlocks,
2885
+ BasicBlockSetVector const &consumedAtEntryBlocks) {
2886
+ auto *block = destroy->getParent ();
2887
+ bool followedByDef = hasDefAfter (destroy, element);
2888
+ if (!followedByDef) {
2889
+ // This destroy is the last write to the field in the block.
2890
+ //
2891
+ // If the block is consumed-at-exit, then there is some other consuming use
2892
+ // before this destroy. Liveness can't be extended.
2893
+ return !consumedAtExitBlocks.contains (block);
2894
+ }
2895
+ for (auto *inst = destroy->getPreviousInstruction (); inst;
2896
+ inst = inst->getPreviousInstruction ()) {
2897
+ if (liveness.isDef (inst, element)) {
2898
+ // Found the corresponding def with no intervening users. Liveness
2899
+ // can be extended to the destroy.
2900
+ return true ;
2901
+ }
2902
+ auto pair = liveness.isInterestingUser (inst);
2903
+ switch (pair.first ) {
2904
+ case FieldSensitivePrunedLiveness::IsInterestingUser::NonUser:
2905
+ break ;
2906
+ case FieldSensitivePrunedLiveness::IsInterestingUser::NonLifetimeEndingUse:
2907
+ if (pair.second .has_value () && pair.second ->contains (element)) {
2908
+ // The first use seen is non-consuming. Liveness can be extended to the
2909
+ // destroy.
2910
+ return true ;
2911
+ }
2912
+ break ;
2913
+ case FieldSensitivePrunedLiveness::IsInterestingUser::LifetimeEndingUse:
2914
+ if (pair.second .has_value () && pair.second ->contains (element)) {
2915
+ // Found a consuming use. Liveness can't be extended to the destroy
2916
+ // (without creating a copy and triggering a diagnostic).
2917
+ return false ;
2918
+ }
2919
+ break ;
2920
+ }
2921
+ }
2922
+ // Found no uses or defs between the destroy and the top of the block. If the
2923
+ // block was not consumed at entry, liveness can be extended to the destroy.
2924
+ return !consumedAtEntryBlocks.contains (block);
2925
+ }
2926
+
2927
+ bool ExtendUnconsumedLiveness::hasDefAfter (SILInstruction *start,
2928
+ unsigned element) {
2929
+ // NOTE: Start iteration at \p start, not its sequel, because
2930
+ // it might be both a consuming use and a def.
2931
+ for (auto *inst = start; inst; inst = inst->getNextInstruction ()) {
2932
+ if (liveness.isDef (inst, element))
2933
+ return true ;
2934
+ }
2935
+ return false ;
2936
+ }
2937
+
2938
+ void ExtendUnconsumedLiveness::addPreviousInstructionToLiveness (
2939
+ SILInstruction *inst, unsigned element, bool lifetimeEnding) {
2940
+ auto range = TypeTreeLeafTypeRange (element);
2941
+ if (auto *previous = inst->getPreviousInstruction ()) {
2942
+ liveness.updateForUse (previous, range, lifetimeEnding);
2943
+ } else {
2944
+ for (auto *predecessor : inst->getParent ()->getPredecessorBlocks ()) {
2945
+ liveness.updateForUse (predecessor->getTerminator (), range,
2946
+ lifetimeEnding);
2947
+ }
2948
+ }
2949
+ }
2950
+
2637
2951
bool MoveOnlyAddressCheckerPImpl::performSingleCheck (
2638
2952
MarkMustCheckInst *markedAddress) {
2639
2953
SWIFT_DEFER { diagnosticEmitter.clearUsesWithDiagnostic (); };
@@ -2728,6 +3042,10 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
2728
3042
2729
3043
FieldSensitivePrunedLivenessBoundary boundary (liveness.getNumSubElements ());
2730
3044
liveness.computeBoundary (boundary);
3045
+ ExtendUnconsumedLiveness extension (addressUseState, liveness, boundary);
3046
+ extension.run ();
3047
+ boundary.clear ();
3048
+ liveness.computeBoundary (boundary);
2731
3049
insertDestroysOnBoundary (markedAddress, liveness, boundary);
2732
3050
rewriteUses (markedAddress, liveness, boundary);
2733
3051
0 commit comments