15
15
16
16
#include " swift/Basic/Defer.h"
17
17
#include " swift/Basic/FrozenMultiMap.h"
18
+ #include " swift/Basic/ImmutablePointerSet.h"
18
19
#include " swift/Basic/LLVM.h"
19
20
#include " swift/SIL/SILInstruction.h"
20
21
#include " llvm/ADT/SmallVector.h"
@@ -105,6 +106,34 @@ struct TransferringOperand {
105
106
bool isClosureCaptured () const { return value.getInt (); }
106
107
107
108
SILInstruction *getUser () const { return getOperand ()->getUser (); }
109
+
110
+ bool operator <(const TransferringOperand &other) const {
111
+ return value < other.value ;
112
+ }
113
+
114
+ bool operator >=(const TransferringOperand &other) const {
115
+ return !(value < other.value );
116
+ }
117
+
118
+ bool operator >(const TransferringOperand &other) const {
119
+ return value > other.value ;
120
+ }
121
+
122
+ bool operator <=(const TransferringOperand &other) const {
123
+ return !(value > other.value );
124
+ }
125
+
126
+ bool operator ==(const TransferringOperand &other) const {
127
+ return value == other.value ;
128
+ }
129
+
130
+ void print (llvm::raw_ostream &os) const {
131
+ os << " Op Num: " << getOperand ()->getOperandNumber () << " . "
132
+ << " Capture: " << (isClosureCaptured () ? " yes. " : " no. " )
133
+ << " User: " << *getUser ();
134
+ }
135
+
136
+ SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
108
137
};
109
138
110
139
} // namespace swift
@@ -344,8 +373,22 @@ class Partition {
344
373
345
374
using Element = PartitionPrimitives::Element;
346
375
using Region = PartitionPrimitives::Region;
376
+ using TransferringOperandSet = ImmutablePointerSet<TransferringOperand>;
377
+ using TransferringOperandSetFactory =
378
+ ImmutablePointerSetFactory<TransferringOperand>;
347
379
348
380
private:
381
+ // / A map from a region number to a instruction that consumes it.
382
+ // /
383
+ // / All we care is that we ever track a single SILInstruction for a region
384
+ // / since we are fine with emitting a single error per value and letting the
385
+ // / user recompile. If this is an ask for in the future, we can use a true
386
+ // / multi map here. The implication of this is that when we are performing
387
+ // / dataflow we use a union operation to combine CFG elements and just take
388
+ // / the first instruction that we see.
389
+ llvm::SmallDenseMap<Region, TransferringOperandSet *, 2 >
390
+ regionToTransferredOpMap;
391
+
349
392
// / Label each index with a non-negative (unsigned) label if it is associated
350
393
// / with a valid region.
351
394
std::map<Element, Region> elementToRegionMap;
@@ -360,16 +403,6 @@ class Partition {
360
403
// / must be reestablished by a call to canonicalize().
361
404
bool canonical;
362
405
363
- // / A map from a region number to a instruction that consumes it.
364
- // /
365
- // / All we care is that we ever track a single SILInstruction for a region
366
- // / since we are fine with emitting a single error per value and letting the
367
- // / user recompile. If this is an ask for in the future, we can use a true
368
- // / multi map here. The implication of this is that when we are performing
369
- // / dataflow we use a union operation to combine CFG elements and just take
370
- // / the first instruction that we see.
371
- llvm::SmallDenseMap<Region, TransferringOperand, 2 > regionToTransferredOpMap;
372
-
373
406
public:
374
407
Partition () : elementToRegionMap({}), canonical(true ) {}
375
408
@@ -420,25 +453,32 @@ class Partition {
420
453
421
454
bool isTracked (Element val) const { return elementToRegionMap.count (val); }
422
455
423
- // / Mark val as transferred. Returns true if we inserted \p
424
- // / transferOperand. We return false otherwise.
425
- bool markTransferred (Element val, TransferringOperand transferredOperand ) {
456
+ // / Mark val as transferred.
457
+ void markTransferred (Element val,
458
+ TransferringOperandSet *transferredOperandSet ) {
426
459
// First see if our val is tracked. If it is not tracked, insert it and mark
427
460
// its new region as transferred.
428
461
if (!isTracked (val)) {
429
462
elementToRegionMap.insert_or_assign (val, fresh_label);
430
- regionToTransferredOpMap.insert ({fresh_label, transferredOperand });
463
+ regionToTransferredOpMap.insert ({fresh_label, transferredOperandSet });
431
464
fresh_label = Region (fresh_label + 1 );
432
465
canonical = false ;
433
- return true ;
466
+ return ;
434
467
}
435
468
436
469
// Otherwise, we already have this value in the map. Try to insert it.
437
470
auto iter1 = elementToRegionMap.find (val);
438
471
assert (iter1 != elementToRegionMap.end ());
439
- auto iter2 =
440
- regionToTransferredOpMap.try_emplace (iter1->second , transferredOperand);
441
- return iter2.second ;
472
+ auto iter2 = regionToTransferredOpMap.try_emplace (iter1->second ,
473
+ transferredOperandSet);
474
+
475
+ // If we did insert, just return. We were not tracking any state.
476
+ if (iter2.second )
477
+ return ;
478
+
479
+ // Otherwise, we need to merge the sets.
480
+ iter2.first ->getSecond () =
481
+ iter2.first ->second ->merge (transferredOperandSet);
442
482
}
443
483
444
484
// / If val was marked as transferred, unmark it as transfer. Returns true if
@@ -481,8 +521,10 @@ class Partition {
481
521
sndReduced.canonicalize ();
482
522
483
523
// For each (sndEltNumber, sndRegionNumber) in snd_reduced...
484
- for (const auto &[sndEltNumber, sndRegionNumber] :
485
- sndReduced.elementToRegionMap ) {
524
+ for (auto pair : sndReduced.elementToRegionMap ) {
525
+ auto sndEltNumber = pair.first ;
526
+ auto sndRegionNumber = pair.second ;
527
+
486
528
// Check if fstReduced has sndEltNumber within it...
487
529
if (fstReduced.elementToRegionMap .count (sndEltNumber)) {
488
530
// If we do, we just merge sndEltNumber into fstRegion.
@@ -491,10 +533,15 @@ class Partition {
491
533
492
534
// Then if sndRegionNumber is transferred in sndReduced, make sure
493
535
// mergedRegion is transferred in fstReduced.
494
- auto iter = sndReduced.regionToTransferredOpMap .find (sndRegionNumber);
495
- if (iter != sndReduced.regionToTransferredOpMap .end ()) {
496
- fstReduced.regionToTransferredOpMap .try_emplace (mergedRegion,
497
- iter->second );
536
+ auto sndIter =
537
+ sndReduced.regionToTransferredOpMap .find (sndRegionNumber);
538
+ if (sndIter != sndReduced.regionToTransferredOpMap .end ()) {
539
+ auto fstIter = fstReduced.regionToTransferredOpMap .try_emplace (
540
+ mergedRegion, sndIter->second );
541
+ if (!fstIter.second ) {
542
+ fstIter.first ->getSecond () =
543
+ fstIter.first ->getSecond ()->merge (sndIter->second );
544
+ }
498
545
}
499
546
continue ;
500
547
}
@@ -527,10 +574,13 @@ class Partition {
527
574
// due to our traversal being in order. Thus just add this to fst_reduced.
528
575
assert (sndEltNumber == Element (sndRegionNumber));
529
576
fstReduced.elementToRegionMap .insert ({sndEltNumber, sndRegionNumber});
530
- auto iter = sndReduced.regionToTransferredOpMap .find (sndRegionNumber);
531
- if (iter != sndReduced.regionToTransferredOpMap .end ()) {
532
- fstReduced.regionToTransferredOpMap .insert (
533
- {sndRegionNumber, iter->second });
577
+ auto sndIter = sndReduced.regionToTransferredOpMap .find (sndRegionNumber);
578
+ if (sndIter != sndReduced.regionToTransferredOpMap .end ()) {
579
+ auto fstIter = fstReduced.regionToTransferredOpMap .try_emplace (
580
+ sndRegionNumber, sndIter->second );
581
+ if (!fstIter.second )
582
+ fstIter.first ->getSecond () =
583
+ fstIter.first ->second ->merge (sndIter->second );
534
584
}
535
585
if (fstReduced.fresh_label < sndRegionNumber)
536
586
fstReduced.fresh_label = Region (sndEltNumber + 1 );
@@ -598,8 +648,13 @@ class Partition {
598
648
for (auto [regionNo, elementNumbers] : multimap.getRange ()) {
599
649
auto iter = regionToTransferredOpMap.find (regionNo);
600
650
bool isTransferred = iter != regionToTransferredOpMap.end ();
601
- bool isClosureCaptured =
602
- isTransferred ? iter->getSecond ().isClosureCaptured () : false ;
651
+ bool isClosureCaptured = false ;
652
+ if (isTransferred) {
653
+ isClosureCaptured = llvm::any_of (
654
+ iter->getSecond ()->range (), [](const TransferringOperand &operand) {
655
+ return operand.isClosureCaptured ();
656
+ });
657
+ }
603
658
604
659
if (isTransferred) {
605
660
os << ' {' ;
@@ -624,6 +679,60 @@ class Partition {
624
679
os << " ]\n " ;
625
680
}
626
681
682
+ LLVM_ATTRIBUTE_USED void dumpVerbose () const { printVerbose (llvm::dbgs ()); }
683
+
684
+ void printVerbose (llvm::raw_ostream &os) const {
685
+ SmallFrozenMultiMap<Region, Element, 8 > multimap;
686
+
687
+ for (auto [eltNo, regionNo] : elementToRegionMap)
688
+ multimap.insert (regionNo, eltNo);
689
+
690
+ multimap.setFrozen ();
691
+
692
+ for (auto [regionNo, elementNumbers] : multimap.getRange ()) {
693
+ auto iter = regionToTransferredOpMap.find (regionNo);
694
+ bool isTransferred = iter != regionToTransferredOpMap.end ();
695
+ bool isClosureCaptured = false ;
696
+ if (isTransferred) {
697
+ isClosureCaptured = llvm::any_of (
698
+ iter->getSecond ()->range (), [](const TransferringOperand &operand) {
699
+ return operand.isClosureCaptured ();
700
+ });
701
+ }
702
+
703
+ os << " Region: " << regionNo << " . " ;
704
+ if (isTransferred) {
705
+ os << ' {' ;
706
+ if (isClosureCaptured)
707
+ os << ' *' ;
708
+ } else {
709
+ os << ' (' ;
710
+ }
711
+
712
+ int j = 0 ;
713
+ for (Element i : elementNumbers) {
714
+ os << (j++ ? " " : " " ) << i;
715
+ }
716
+ if (isTransferred) {
717
+ if (isClosureCaptured)
718
+ os << ' *' ;
719
+ os << ' }' ;
720
+ } else {
721
+ os << ' )' ;
722
+ }
723
+ os << " \n " ;
724
+ os << " TransferInsts:\n " ;
725
+ if (isTransferred) {
726
+ for (auto op : iter->getSecond ()->data ()) {
727
+ os << " " ;
728
+ op.print (os);
729
+ }
730
+ } else {
731
+ os << " None.\n " ;
732
+ }
733
+ }
734
+ }
735
+
627
736
bool isTransferred (Element val) const {
628
737
auto iter = elementToRegionMap.find (val);
629
738
if (iter == elementToRegionMap.end ())
@@ -633,14 +742,16 @@ class Partition {
633
742
634
743
// / Return the instruction that transferred \p val's region or nullptr
635
744
// / otherwise.
636
- std::optional<TransferringOperand> getTransferred (Element val) const {
745
+ TransferringOperandSet * getTransferred (Element val) const {
637
746
auto iter = elementToRegionMap.find (val);
638
747
if (iter == elementToRegionMap.end ())
639
- return std::nullopt ;
748
+ return nullptr ;
640
749
auto iter2 = regionToTransferredOpMap.find (iter->second );
641
750
if (iter2 == regionToTransferredOpMap.end ())
642
- return std::nullopt;
643
- return iter2->second ;
751
+ return nullptr ;
752
+ auto *set = iter2->second ;
753
+ assert (!set->empty ());
754
+ return set;
644
755
}
645
756
646
757
private:
@@ -723,7 +834,7 @@ class Partition {
723
834
//
724
835
// TODO: If we just used an array for this, we could just rewrite and
725
836
// re-sort and not have to deal with potential allocations.
726
- llvm::SmallDenseMap<Region, TransferringOperand, 2 > oldMap =
837
+ decltype (regionToTransferredOpMap) oldMap =
727
838
std::move (regionToTransferredOpMap);
728
839
for (auto &[oldReg, op] : oldMap) {
729
840
auto iter = oldRegionToRelabeledMap.find (oldReg);
@@ -816,6 +927,10 @@ class Partition {
816
927
struct PartitionOpEvaluator {
817
928
using Element = PartitionPrimitives::Element;
818
929
using Region = PartitionPrimitives::Region;
930
+ using TransferringOperandSetFactory =
931
+ Partition::TransferringOperandSetFactory;
932
+
933
+ TransferringOperandSetFactory &ptrSetFactory;
819
934
820
935
Partition &p;
821
936
@@ -864,7 +979,9 @@ struct PartitionOpEvaluator {
864
979
std::function<bool (Element elt, Operand *op)> isClosureCapturedCallback =
865
980
nullptr ;
866
981
867
- PartitionOpEvaluator (Partition &p) : p(p) {}
982
+ PartitionOpEvaluator (Partition &p,
983
+ TransferringOperandSetFactory &ptrSetFactory)
984
+ : ptrSetFactory(ptrSetFactory), p(p) {}
868
985
869
986
// / A wrapper around the failure callback that checks if it is nullptr.
870
987
void handleFailure (const PartitionOp &op, Element elt,
@@ -922,8 +1039,11 @@ struct PartitionOpEvaluator {
922
1039
" Assign PartitionOp's source argument should be already tracked" );
923
1040
// If we are using a region that was transferred as our assignment source
924
1041
// value... emit an error.
925
- if (auto transferredOperand = p.getTransferred (op.getOpArgs ()[1 ]))
926
- handleFailure (op, op.getOpArgs ()[1 ], *transferredOperand);
1042
+ if (auto *transferredOperandSet = p.getTransferred (op.getOpArgs ()[1 ])) {
1043
+ for (auto transferredOperand : transferredOperandSet->data ()) {
1044
+ handleFailure (op, op.getOpArgs ()[1 ], transferredOperand);
1045
+ }
1046
+ }
927
1047
928
1048
p.elementToRegionMap .insert_or_assign (
929
1049
op.getOpArgs ()[0 ], p.elementToRegionMap .at (op.getOpArgs ()[1 ]));
@@ -978,8 +1098,9 @@ struct PartitionOpEvaluator {
978
1098
}
979
1099
980
1100
// Mark op.getOpArgs()[0] as transferred.
981
- p.markTransferred (op.getOpArgs ()[0 ],
982
- {op.getSourceOp (), isClosureCapturedElt});
1101
+ p.markTransferred (
1102
+ op.getOpArgs ()[0 ],
1103
+ ptrSetFactory.get ({op.getSourceOp (), isClosureCapturedElt}));
983
1104
return ;
984
1105
}
985
1106
case PartitionOpKind::UndoTransfer: {
@@ -1000,10 +1121,16 @@ struct PartitionOpEvaluator {
1000
1121
" Merge PartitionOp's arguments should already be tracked" );
1001
1122
1002
1123
// if attempting to merge a transferred region, handle the failure
1003
- if (auto transferringOp = p.getTransferred (op.getOpArgs ()[0 ]))
1004
- handleFailure (op, op.getOpArgs ()[0 ], *transferringOp);
1005
- if (auto transferringOp = p.getTransferred (op.getOpArgs ()[1 ]))
1006
- handleFailure (op, op.getOpArgs ()[1 ], *transferringOp);
1124
+ if (auto *transferredOperandSet = p.getTransferred (op.getOpArgs ()[0 ])) {
1125
+ for (auto transferredOperand : transferredOperandSet->data ()) {
1126
+ handleFailure (op, op.getOpArgs ()[0 ], transferredOperand);
1127
+ }
1128
+ }
1129
+ if (auto *transferredOperandSet = p.getTransferred (op.getOpArgs ()[1 ])) {
1130
+ for (auto transferredOperand : transferredOperandSet->data ()) {
1131
+ handleFailure (op, op.getOpArgs ()[1 ], transferredOperand);
1132
+ }
1133
+ }
1007
1134
1008
1135
p.merge (op.getOpArgs ()[0 ], op.getOpArgs ()[1 ]);
1009
1136
return ;
@@ -1012,8 +1139,11 @@ struct PartitionOpEvaluator {
1012
1139
" Require PartitionOp should be passed 1 argument" );
1013
1140
assert (p.elementToRegionMap .count (op.getOpArgs ()[0 ]) &&
1014
1141
" Require PartitionOp's argument should already be tracked" );
1015
- if (auto transferringOp = p.getTransferred (op.getOpArgs ()[0 ]))
1016
- handleFailure (op, op.getOpArgs ()[0 ], *transferringOp);
1142
+ if (auto *transferredOperandSet = p.getTransferred (op.getOpArgs ()[0 ])) {
1143
+ for (auto transferredOperand : transferredOperandSet->data ()) {
1144
+ handleFailure (op, op.getOpArgs ()[0 ], transferredOperand);
1145
+ }
1146
+ }
1017
1147
return ;
1018
1148
}
1019
1149
0 commit comments