1919#include " swift/Basic/LLVM.h"
2020#include " swift/SIL/SILFunction.h"
2121#include " swift/SIL/SILInstruction.h"
22+
23+ #include " llvm/ADT/MapVector.h"
24+ #include " llvm/ADT/STLExtras.h"
2225#include " llvm/ADT/SmallVector.h"
2326#include " llvm/Support/Debug.h"
27+
2428#include < algorithm>
2529#include < variant>
2630
@@ -223,6 +227,7 @@ class SILIsolationInfo {
223227};
224228
225229class Partition ;
230+ class TransferringOperandToStateMap ;
226231
227232// / A persistent data structure that is used to "rewind" partition history so
228233// / that we can discover when values become part of the same region.
@@ -244,6 +249,7 @@ class IsolationHistory {
244249
245250 // TODO: This shouldn't need to be a friend.
246251 friend class Partition ;
252+ friend TransferringOperandToStateMap;
247253
248254 // / First node in the immutable linked list.
249255 Node *head = nullptr ;
@@ -297,6 +303,11 @@ class IsolationHistory {
297303 // / Push that \p other should be merged into this region.
298304 void pushCFGHistoryJoin (Node *otherNode);
299305
306+ // / Push the top node of \p history as a CFG history join.
307+ void pushCFGHistoryJoin (IsolationHistory history) {
308+ return pushCFGHistoryJoin (history.getHead ());
309+ }
310+
300311 Node *pop ();
301312};
302313
@@ -456,10 +467,7 @@ class IsolationHistory::Factory {
456467 IsolationHistory get () { return IsolationHistory (this ); }
457468};
458469
459- class TransferringOperand {
460- using ValueType = llvm::PointerIntPair<Operand *, 1 >;
461- ValueType value;
462-
470+ struct TransferringOperandState {
463471 // / The dynamic isolation info of the region of value when we transferred.
464472 // /
465473 // / This will contain the isolated value if we found one.
@@ -468,65 +476,30 @@ class TransferringOperand {
468476 // / The dynamic isolation history at this point.
469477 IsolationHistory isolationHistory;
470478
471- TransferringOperand (ValueType newValue, SILIsolationInfo isolationRegionInfo,
472- IsolationHistory isolationHistory)
473- : value(newValue), isolationInfo(isolationRegionInfo),
474- isolationHistory (isolationHistory) {
475- assert (isolationInfo && " Should never see unknown isolation info" );
476- }
477-
478- public:
479- TransferringOperand (Operand *op, bool isClosureCaptured,
480- SILIsolationInfo isolationRegionInfo,
481- IsolationHistory isolationHistory)
482- : TransferringOperand({op, isClosureCaptured}, isolationRegionInfo,
483- isolationHistory) {}
484- explicit TransferringOperand (Operand *op,
485- SILIsolationInfo isolationRegionInfo,
486- IsolationHistory isolationHistory)
487- : TransferringOperand({op, false }, isolationRegionInfo,
488- isolationHistory) {}
489-
490- operator bool () const { return bool (value.getPointer ()); }
491-
492- Operand *getOperand () const { return value.getPointer (); }
493-
494- SILValue get () const { return getOperand ()->get (); }
495-
496- bool isClosureCaptured () const { return value.getInt (); }
479+ // / Set to true if the element associated with the operand's vlaue is closure
480+ // / captured by the user. In such a case, if our element is a sendable var of
481+ // / a non-Sendable type, we cannot access it since we could race against an
482+ // / assignment to the var in a closure.
483+ bool isClosureCaptured;
497484
498- SILInstruction *getUser () const { return getOperand ()->getUser (); }
499-
500- SILIsolationInfo getIsolationInfo () const { return isolationInfo; }
501-
502- IsolationHistory getIsolationHistory () const { return isolationHistory; }
503-
504- unsigned getOperandNumber () const { return getOperand ()->getOperandNumber (); }
505-
506- void print (llvm::raw_ostream &os) const {
507- os << " Op Num: " << getOperand ()->getOperandNumber () << " . "
508- << " Capture: " << (isClosureCaptured () ? " yes. " : " no. " )
509- << " IsolationInfo: " ;
510- isolationInfo.print (os);
511- os << " \n User: " << *getUser ();
512- }
485+ TransferringOperandState (IsolationHistory history)
486+ : isolationInfo(), isolationHistory(history), isClosureCaptured(false ) {}
487+ };
513488
514- static void Profile (llvm::FoldingSetNodeID &id, Operand *op,
515- bool isClosureCaptured,
516- SILIsolationInfo isolationRegionInfo,
517- IsolationHistory isolationHistory) {
518- id.AddPointer (op);
519- id.AddBoolean (isClosureCaptured);
520- isolationRegionInfo.Profile (id);
521- id.AddPointer (isolationHistory.getHead ());
522- }
489+ class TransferringOperandToStateMap {
490+ llvm::SmallDenseMap<Operand *, TransferringOperandState> internalMap;
491+ IsolationHistory::Factory &isolationHistoryFactory;
523492
524- void Profile (llvm::FoldingSetNodeID &id) const {
525- Profile (id, getOperand (), isClosureCaptured (), isolationInfo,
526- isolationHistory);
493+ public:
494+ TransferringOperandToStateMap (
495+ IsolationHistory::Factory &isolationHistoryFactory)
496+ : isolationHistoryFactory(isolationHistoryFactory) {}
497+ TransferringOperandState &get (Operand *op) const {
498+ auto *self = const_cast <TransferringOperandToStateMap *>(this );
499+ auto history = IsolationHistory (&isolationHistoryFactory);
500+ return self->internalMap .try_emplace (op, TransferringOperandState (history))
501+ .first ->getSecond ();
527502 }
528-
529- SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
530503};
531504
532505} // namespace swift
@@ -675,9 +648,8 @@ class Partition {
675648
676649 using Element = PartitionPrimitives::Element;
677650 using Region = PartitionPrimitives::Region;
678- using TransferringOperandSet = ImmutablePointerSet<TransferringOperand *>;
679- using TransferringOperandSetFactory =
680- ImmutablePointerSetFactory<TransferringOperand *>;
651+ using TransferringOperandSet = ImmutablePointerSet<Operand *>;
652+ using TransferringOperandSetFactory = ImmutablePointerSetFactory<Operand *>;
681653 using IsolationHistoryNode = IsolationHistory::Node;
682654
683655private:
@@ -689,7 +661,7 @@ class Partition {
689661 // / multi map here. The implication of this is that when we are performing
690662 // / dataflow we use a union operation to combine CFG elements and just take
691663 // / the first instruction that we see.
692- llvm::SmallDenseMap <Region, TransferringOperandSet *, 2 >
664+ llvm::SmallMapVector <Region, TransferringOperandSet *, 2 >
693665 regionToTransferredOpMap;
694666
695667 // / Label each index with a non-negative (unsigned) label if it is associated
@@ -736,7 +708,16 @@ class Partition {
736708 fst.canonicalize ();
737709 snd.canonicalize ();
738710
739- return fst.elementToRegionMap == snd.elementToRegionMap ;
711+ return fst.elementToRegionMap == snd.elementToRegionMap &&
712+ fst.regionToTransferredOpMap .size () ==
713+ snd.regionToTransferredOpMap .size () &&
714+ llvm::all_of (
715+ fst.regionToTransferredOpMap ,
716+ [&snd](const std::pair<Region, TransferringOperandSet *> &p) {
717+ auto sndIter = snd.regionToTransferredOpMap .find (p.first );
718+ return sndIter != snd.regionToTransferredOpMap .end () &&
719+ sndIter->second == p.second ;
720+ });
740721 }
741722
742723 bool isTrackingElement (Element val) const {
@@ -1013,13 +994,16 @@ struct PartitionOpEvaluator {
1013994
1014995protected:
1015996 TransferringOperandSetFactory &ptrSetFactory;
997+ TransferringOperandToStateMap &operandToStateMap;
1016998
1017999 Partition &p;
10181000
10191001public:
10201002 PartitionOpEvaluator (Partition &p,
1021- TransferringOperandSetFactory &ptrSetFactory)
1022- : ptrSetFactory(ptrSetFactory), p(p) {}
1003+ TransferringOperandSetFactory &ptrSetFactory,
1004+ TransferringOperandToStateMap &operandToStateMap)
1005+ : ptrSetFactory(ptrSetFactory), operandToStateMap(operandToStateMap),
1006+ p (p) {}
10231007
10241008 // / Call shouldEmitVerboseLogging on our CRTP subclass.
10251009 bool shouldEmitVerboseLogging () const {
@@ -1028,7 +1012,7 @@ struct PartitionOpEvaluator {
10281012
10291013 // / Call handleLocalUseAfterTransfer on our CRTP subclass.
10301014 void handleLocalUseAfterTransfer (const PartitionOp &op, Element elt,
1031- TransferringOperand *transferringOp) const {
1015+ Operand *transferringOp) const {
10321016 return asImpl ().handleLocalUseAfterTransfer (op, elt, transferringOp);
10331017 }
10341018
@@ -1193,9 +1177,13 @@ struct PartitionOpEvaluator {
11931177 }
11941178
11951179 // Mark op.getOpArgs()[0] as transferred.
1196- auto *ptrSet = ptrSetFactory.emplace (
1197- op.getSourceOp (), isClosureCapturedElt, transferredRegionIsolation,
1198- p.getIsolationHistory ());
1180+ TransferringOperandState &state = operandToStateMap.get (op.getSourceOp ());
1181+ state.isClosureCaptured |= isClosureCapturedElt;
1182+ state.isolationInfo =
1183+ state.isolationInfo .merge (transferredRegionIsolation);
1184+ assert (state.isolationInfo && " Cannot have unknown" );
1185+ state.isolationHistory .pushCFGHistoryJoin (p.getIsolationHistory ());
1186+ auto *ptrSet = ptrSetFactory.get (op.getSourceOp ());
11991187 p.markTransferred (op.getOpArgs ()[0 ], ptrSet);
12001188 return ;
12011189 }
@@ -1265,9 +1253,8 @@ struct PartitionOpEvaluator {
12651253private:
12661254 // Private helper that squelches the error if our transfer instruction and our
12671255 // use have the same isolation.
1268- void
1269- handleLocalUseAfterTransferHelper (const PartitionOp &op, Element elt,
1270- TransferringOperand *transferringOp) const {
1256+ void handleLocalUseAfterTransferHelper (const PartitionOp &op, Element elt,
1257+ Operand *transferringOp) const {
12711258 if (shouldTryToSquelchErrors ()) {
12721259 if (auto isolationInfo = SILIsolationInfo::get (op.getSourceInst ())) {
12731260 if (isolationInfo.isActorIsolated () &&
@@ -1305,8 +1292,9 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13051292 using Super = PartitionOpEvaluator<Subclass>;
13061293
13071294 PartitionOpEvaluatorBaseImpl (Partition &workingPartition,
1308- TransferringOperandSetFactory &ptrSetFactory)
1309- : Super(workingPartition, ptrSetFactory) {}
1295+ TransferringOperandSetFactory &ptrSetFactory,
1296+ TransferringOperandToStateMap &operandToStateMap)
1297+ : Super(workingPartition, ptrSetFactory, operandToStateMap) {}
13101298
13111299 // / Should we emit extra verbose logging statements when evaluating
13121300 // / PartitionOps.
@@ -1325,7 +1313,7 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13251313 // / region. Can be used to get the immediate value transferred or the
13261314 // / transferring instruction.
13271315 void handleLocalUseAfterTransfer (const PartitionOp &op, Element elt,
1328- TransferringOperand *transferringOp) const {}
1316+ Operand *transferringOp) const {}
13291317
13301318 // / This is called if we detect a never transferred element that was passed to
13311319 // / a transfer instruction.
@@ -1373,8 +1361,10 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
13731361struct PartitionOpEvaluatorBasic final
13741362 : PartitionOpEvaluatorBaseImpl<PartitionOpEvaluatorBasic> {
13751363 PartitionOpEvaluatorBasic (Partition &workingPartition,
1376- TransferringOperandSetFactory &ptrSetFactory)
1377- : PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory) {}
1364+ TransferringOperandSetFactory &ptrSetFactory,
1365+ TransferringOperandToStateMap &operandToStateMap)
1366+ : PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory,
1367+ operandToStateMap) {}
13781368};
13791369
13801370} // namespace swift
0 commit comments