1515
1616#include " swift/Basic/Defer.h"
1717#include " swift/Basic/FrozenMultiMap.h"
18+ #include " swift/Basic/ImmutablePointerSet.h"
1819#include " swift/Basic/LLVM.h"
1920#include " swift/SIL/SILInstruction.h"
2021#include " llvm/ADT/SmallVector.h"
@@ -105,6 +106,34 @@ struct TransferringOperand {
105106 bool isClosureCaptured () const { return value.getInt (); }
106107
107108 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 ()); }
108137};
109138
110139} // namespace swift
@@ -344,8 +373,22 @@ class Partition {
344373
345374 using Element = PartitionPrimitives::Element;
346375 using Region = PartitionPrimitives::Region;
376+ using TransferringOperandSet = ImmutablePointerSet<TransferringOperand>;
377+ using TransferringOperandSetFactory =
378+ ImmutablePointerSetFactory<TransferringOperand>;
347379
348380private:
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+
349392 // / Label each index with a non-negative (unsigned) label if it is associated
350393 // / with a valid region.
351394 std::map<Element, Region> elementToRegionMap;
@@ -360,16 +403,6 @@ class Partition {
360403 // / must be reestablished by a call to canonicalize().
361404 bool canonical;
362405
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-
373406public:
374407 Partition () : elementToRegionMap({}), canonical(true ) {}
375408
@@ -420,25 +453,32 @@ class Partition {
420453
421454 bool isTracked (Element val) const { return elementToRegionMap.count (val); }
422455
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 ) {
426459 // First see if our val is tracked. If it is not tracked, insert it and mark
427460 // its new region as transferred.
428461 if (!isTracked (val)) {
429462 elementToRegionMap.insert_or_assign (val, fresh_label);
430- regionToTransferredOpMap.insert ({fresh_label, transferredOperand });
463+ regionToTransferredOpMap.insert ({fresh_label, transferredOperandSet });
431464 fresh_label = Region (fresh_label + 1 );
432465 canonical = false ;
433- return true ;
466+ return ;
434467 }
435468
436469 // Otherwise, we already have this value in the map. Try to insert it.
437470 auto iter1 = elementToRegionMap.find (val);
438471 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);
442482 }
443483
444484 // / If val was marked as transferred, unmark it as transfer. Returns true if
@@ -481,8 +521,10 @@ class Partition {
481521 sndReduced.canonicalize ();
482522
483523 // 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+
486528 // Check if fstReduced has sndEltNumber within it...
487529 if (fstReduced.elementToRegionMap .count (sndEltNumber)) {
488530 // If we do, we just merge sndEltNumber into fstRegion.
@@ -491,10 +533,15 @@ class Partition {
491533
492534 // Then if sndRegionNumber is transferred in sndReduced, make sure
493535 // 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+ }
498545 }
499546 continue ;
500547 }
@@ -527,10 +574,13 @@ class Partition {
527574 // due to our traversal being in order. Thus just add this to fst_reduced.
528575 assert (sndEltNumber == Element (sndRegionNumber));
529576 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 );
534584 }
535585 if (fstReduced.fresh_label < sndRegionNumber)
536586 fstReduced.fresh_label = Region (sndEltNumber + 1 );
@@ -598,8 +648,13 @@ class Partition {
598648 for (auto [regionNo, elementNumbers] : multimap.getRange ()) {
599649 auto iter = regionToTransferredOpMap.find (regionNo);
600650 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+ }
603658
604659 if (isTransferred) {
605660 os << ' {' ;
@@ -624,6 +679,60 @@ class Partition {
624679 os << " ]\n " ;
625680 }
626681
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+
627736 bool isTransferred (Element val) const {
628737 auto iter = elementToRegionMap.find (val);
629738 if (iter == elementToRegionMap.end ())
@@ -633,14 +742,16 @@ class Partition {
633742
634743 // / Return the instruction that transferred \p val's region or nullptr
635744 // / otherwise.
636- std::optional<TransferringOperand> getTransferred (Element val) const {
745+ TransferringOperandSet * getTransferred (Element val) const {
637746 auto iter = elementToRegionMap.find (val);
638747 if (iter == elementToRegionMap.end ())
639- return std:: nullopt ;
748+ return nullptr ;
640749 auto iter2 = regionToTransferredOpMap.find (iter->second );
641750 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;
644755 }
645756
646757private:
@@ -723,7 +834,7 @@ class Partition {
723834 //
724835 // TODO: If we just used an array for this, we could just rewrite and
725836 // re-sort and not have to deal with potential allocations.
726- llvm::SmallDenseMap<Region, TransferringOperand, 2 > oldMap =
837+ decltype (regionToTransferredOpMap) oldMap =
727838 std::move (regionToTransferredOpMap);
728839 for (auto &[oldReg, op] : oldMap) {
729840 auto iter = oldRegionToRelabeledMap.find (oldReg);
@@ -816,6 +927,10 @@ class Partition {
816927struct PartitionOpEvaluator {
817928 using Element = PartitionPrimitives::Element;
818929 using Region = PartitionPrimitives::Region;
930+ using TransferringOperandSetFactory =
931+ Partition::TransferringOperandSetFactory;
932+
933+ TransferringOperandSetFactory &ptrSetFactory;
819934
820935 Partition &p;
821936
@@ -864,7 +979,9 @@ struct PartitionOpEvaluator {
864979 std::function<bool (Element elt, Operand *op)> isClosureCapturedCallback =
865980 nullptr ;
866981
867- PartitionOpEvaluator (Partition &p) : p(p) {}
982+ PartitionOpEvaluator (Partition &p,
983+ TransferringOperandSetFactory &ptrSetFactory)
984+ : ptrSetFactory(ptrSetFactory), p(p) {}
868985
869986 // / A wrapper around the failure callback that checks if it is nullptr.
870987 void handleFailure (const PartitionOp &op, Element elt,
@@ -922,8 +1039,11 @@ struct PartitionOpEvaluator {
9221039 " Assign PartitionOp's source argument should be already tracked" );
9231040 // If we are using a region that was transferred as our assignment source
9241041 // 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+ }
9271047
9281048 p.elementToRegionMap .insert_or_assign (
9291049 op.getOpArgs ()[0 ], p.elementToRegionMap .at (op.getOpArgs ()[1 ]));
@@ -978,8 +1098,9 @@ struct PartitionOpEvaluator {
9781098 }
9791099
9801100 // 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}));
9831104 return ;
9841105 }
9851106 case PartitionOpKind::UndoTransfer: {
@@ -1000,10 +1121,16 @@ struct PartitionOpEvaluator {
10001121 " Merge PartitionOp's arguments should already be tracked" );
10011122
10021123 // 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+ }
10071134
10081135 p.merge (op.getOpArgs ()[0 ], op.getOpArgs ()[1 ]);
10091136 return ;
@@ -1012,8 +1139,11 @@ struct PartitionOpEvaluator {
10121139 " Require PartitionOp should be passed 1 argument" );
10131140 assert (p.elementToRegionMap .count (op.getOpArgs ()[0 ]) &&
10141141 " 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+ }
10171147 return ;
10181148 }
10191149
0 commit comments