@@ -65,7 +65,8 @@ struct Region {
6565
6666 static Region transferred () { return Region (-1 ); }
6767};
68- }
68+
69+ } // namespace PartitionPrimitives
6970
7071using namespace PartitionPrimitives ;
7172
@@ -180,24 +181,44 @@ class PartitionOp {
180181
181182 SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
182183
183- void print (llvm::raw_ostream &os) const {
184+ void print (llvm::raw_ostream &os, bool extraSpace = false ) const {
184185 switch (OpKind) {
185- case PartitionOpKind::Assign:
186- os << " assign %%" << OpArgs[0 ] << " = %%" << OpArgs[1 ];
186+ case PartitionOpKind::Assign: {
187+ constexpr static char extraSpaceLiteral[10 ] = " " ;
188+ os << " assign " ;
189+ if (extraSpace)
190+ os << extraSpaceLiteral;
191+ os << " %%" << OpArgs[0 ] << " = %%" << OpArgs[1 ];
187192 break ;
193+ }
188194 case PartitionOpKind::AssignFresh:
189195 os << " assign_fresh %%" << OpArgs[0 ];
190196 break ;
191- case PartitionOpKind::Transfer:
192- os << " transfer %%" << OpArgs[0 ];
197+ case PartitionOpKind::Transfer: {
198+ constexpr static char extraSpaceLiteral[10 ] = " " ;
199+ os << " transfer " ;
200+ if (extraSpace)
201+ os << extraSpaceLiteral;
202+ os << " %%" << OpArgs[0 ];
193203 break ;
194- case PartitionOpKind::Merge:
195- os << " merge %%" << OpArgs[0 ] << " with %%" << OpArgs[1 ];
204+ }
205+ case PartitionOpKind::Merge: {
206+ constexpr static char extraSpaceLiteral[10 ] = " " ;
207+ os << " merge " ;
208+ if (extraSpace)
209+ os << extraSpaceLiteral;
210+ os << " %%" << OpArgs[0 ] << " with %%" << OpArgs[1 ];
196211 break ;
197- case PartitionOpKind::Require:
198- os << " require %%" << OpArgs[0 ];
212+ }
213+ case PartitionOpKind::Require: {
214+ constexpr static char extraSpaceLiteral[10 ] = " " ;
215+ os << " require " ;
216+ if (extraSpace)
217+ os << extraSpaceLiteral;
218+ os << " %%" << OpArgs[0 ];
199219 break ;
200220 }
221+ }
201222 os << " : " << *getSourceInst (true );
202223 }
203224};
@@ -224,6 +245,8 @@ static void horizontalUpdate(std::map<Element, Region> &map, Element key,
224245 map.insert_or_assign (otherKey, val);
225246}
226247
248+ struct PartitionOpEvaluator ;
249+
227250// / A map from Element -> Region that represents the current partition set.
228251// /
229252// /
@@ -232,6 +255,7 @@ class Partition {
232255 // / A class defined in PartitionUtils unittest used to grab state from
233256 // / Partition without exposing it to other users.
234257 struct PartitionTester ;
258+ friend PartitionOpEvaluator;
235259
236260private:
237261 // / Label each index with a non-negative (unsigned) label if it is associated
@@ -377,125 +401,6 @@ class Partition {
377401 return fst_reduced;
378402 }
379403
380- // / Apply the passed PartitionOp to this partition, performing its action. A
381- // / `handleFailure` closure can optionally be passed in that will be called if
382- // / a transferred region is required. The closure is given the PartitionOp
383- // / that failed, and the index of the SIL value that was required but
384- // / transferred. Additionally, a list of "nontransferrable" indices can be
385- // / passed in along with a handleTransferNonTransferrable closure. In the
386- // / event that a region containing one of the nontransferrable indices is
387- // / transferred, the closure will be called with the offending transfer.
388- void apply (
389- PartitionOp op,
390- llvm::function_ref<void (const PartitionOp &, Element)> handleFailure =
391- [](const PartitionOp &, Element) {},
392- ArrayRef<Element> nontransferrables = {},
393- llvm::function_ref<void (const PartitionOp &, Element)>
394- handleTransferNonTransferrable = [](const PartitionOp &, Element) {},
395- llvm::function_ref<bool (Element)> isActorDerived = nullptr ) {
396-
397- REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " Applying: " ;
398- op.print (llvm::dbgs ()));
399- REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " Before: " ;
400- print (llvm::dbgs ()));
401- SWIFT_DEFER {
402- REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " After: " ;
403- print (llvm::dbgs ()));
404- };
405- switch (op.OpKind ) {
406- case PartitionOpKind::Assign:
407- assert (op.OpArgs .size () == 2 &&
408- " Assign PartitionOp should be passed 2 arguments" );
409- assert (labels.count (op.OpArgs [1 ]) &&
410- " Assign PartitionOp's source argument should be already tracked" );
411- // if assigning to a missing region, handle the failure
412- if (isTransferred (op.OpArgs [1 ]))
413- handleFailure (op, op.OpArgs [1 ]);
414-
415- labels.insert_or_assign (op.OpArgs [0 ], labels.at (op.OpArgs [1 ]));
416-
417- // assignment could have invalidated canonicality of either the old region
418- // of op.OpArgs[0] or the region of op.OpArgs[1], or both
419- canonical = false ;
420- break ;
421- case PartitionOpKind::AssignFresh:
422- assert (op.OpArgs .size () == 1 &&
423- " AssignFresh PartitionOp should be passed 1 argument" );
424-
425- // map index op.OpArgs[0] to a fresh label
426- labels.insert_or_assign (op.OpArgs [0 ], fresh_label);
427-
428- // increment the fresh label so it remains fresh
429- fresh_label = Region (fresh_label + 1 );
430- canonical = false ;
431- break ;
432- case PartitionOpKind::Transfer: {
433- assert (op.OpArgs .size () == 1 &&
434- " Transfer PartitionOp should be passed 1 argument" );
435- assert (labels.count (op.OpArgs [0 ]) &&
436- " Transfer PartitionOp's argument should already be tracked" );
437-
438- // check if any nontransferrables are transferred here, and handle the
439- // failure if so
440- for (Element nonTransferrable : nontransferrables) {
441- assert (
442- labels.count (nonTransferrable) &&
443- " nontransferrables should be function args and self, and therefore"
444- " always present in the label map because of initialization at "
445- " entry" );
446- if (!isTransferred (nonTransferrable) &&
447- labels.at (nonTransferrable) == labels.at (op.OpArgs [0 ])) {
448- handleTransferNonTransferrable (op, nonTransferrable);
449- break ;
450- }
451- }
452-
453- // If this value is actor derived or if any elements in its region are
454- // actor derived, we need to treat as nontransferrable.
455- if (isActorDerived && isActorDerived (op.OpArgs [0 ]))
456- return handleTransferNonTransferrable (op, op.OpArgs [0 ]);
457- Region elementRegion = labels.at (op.OpArgs [0 ]);
458- if (llvm::any_of (labels,
459- [&](const std::pair<Element, Region> &pair) -> bool {
460- if (pair.second != elementRegion)
461- return false ;
462- return isActorDerived && isActorDerived (pair.first );
463- }))
464- return handleTransferNonTransferrable (op, op.OpArgs [0 ]);
465-
466- // Ensure if the region is transferred...
467- if (!isTransferred (op.OpArgs [0 ]))
468- // that all elements associated with the region are marked as
469- // transferred.
470- horizontalUpdate (labels, op.OpArgs [0 ], Region::transferred ());
471- break ;
472- }
473- case PartitionOpKind::Merge:
474- assert (op.OpArgs .size () == 2 &&
475- " Merge PartitionOp should be passed 2 arguments" );
476- assert (labels.count (op.OpArgs [0 ]) && labels.count (op.OpArgs [1 ]) &&
477- " Merge PartitionOp's arguments should already be tracked" );
478-
479- // if attempting to merge a transferred region, handle the failure
480- if (isTransferred (op.OpArgs [0 ]))
481- handleFailure (op, op.OpArgs [0 ]);
482- if (isTransferred (op.OpArgs [1 ]))
483- handleFailure (op, op.OpArgs [1 ]);
484-
485- merge (op.OpArgs [0 ], op.OpArgs [1 ]);
486- break ;
487- case PartitionOpKind::Require:
488- assert (op.OpArgs .size () == 1 &&
489- " Require PartitionOp should be passed 1 argument" );
490- assert (labels.count (op.OpArgs [0 ]) &&
491- " Require PartitionOp's argument should already be tracked" );
492- if (isTransferred (op.OpArgs [0 ]))
493- handleFailure (op, op.OpArgs [0 ]);
494- }
495-
496- assert (is_canonical_correct ());
497- }
498-
499404 // / Return a vector of the transferred values in this partition.
500405 std::vector<Element> getTransferredVals () const {
501406 // For effeciency, this could return an iterator not a vector.
@@ -645,6 +550,186 @@ class Partition {
645550 }
646551};
647552
553+ // / A data structure that applies a series of PartitionOps to a single Partition
554+ // / that it modifies.
555+ // /
556+ // / Apply the passed PartitionOp to this partition, performing its action. A
557+ // / `handleFailure` closure can optionally be passed in that will be called if
558+ // / a transferred region is required. The closure is given the PartitionOp
559+ // / that failed, and the index of the SIL value that was required but
560+ // / transferred. Additionally, a list of "nontransferrable" indices can be
561+ // / passed in along with a handleTransferNonTransferrable closure. In the
562+ // / event that a region containing one of the nontransferrable indices is
563+ // / transferred, the closure will be called with the offending transfer.
564+ struct PartitionOpEvaluator {
565+ Partition &p;
566+
567+ // / If this PartitionOp evaluator should emit log statements.
568+ bool emitLog = true ;
569+
570+ // / If set to a non-null function, then this callback will be called if we
571+ // / discover a transferred value was used after it was transferred.
572+ std::function<void (const PartitionOp &, Element)> failureCallback = nullptr ;
573+
574+ // / A list of elements that cannot be transferred. Whenever we transfer, we
575+ // / check this list to see if we are transferring the element and then call
576+ // / transferNonTransferrableCallback. This should consist only of function
577+ // / arguments.
578+ ArrayRef<Element> nonTransferrableElements = {};
579+
580+ // / If set to a non-null function_ref, this is called if we detect a never
581+ // / transferred element that was passed to a transfer instruction.
582+ std::function<void (const PartitionOp &, Element)>
583+ transferredNonTransferrableCallback = nullptr ;
584+
585+ // / If set to a non-null function_ref, then this is used to determine if an
586+ // / element is actor derived. If we determine that a region containing such an
587+ // / element is transferred, we emit an error since actor regions cannot be
588+ // / transferred.
589+ std::function<bool (Element)> isActorDerivedCallback = nullptr ;
590+
591+ PartitionOpEvaluator (Partition &p) : p(p) {}
592+
593+ // / A wrapper around the failure callback that checks if it is nullptr.
594+ void handleFailure (const PartitionOp &op, Element elt) const {
595+ if (!failureCallback)
596+ return ;
597+ failureCallback (op, elt);
598+ }
599+
600+ // / A wrapper around transferNonTransferrableCallback that only calls it if it
601+ // / is not null.
602+ void handleTransferNonTransferrable (const PartitionOp &op,
603+ Element elt) const {
604+ if (!transferredNonTransferrableCallback)
605+ return ;
606+ transferredNonTransferrableCallback (op, elt);
607+ }
608+
609+ // / A wrapper around isActorDerivedCallback that returns false if
610+ // / isActorDerivedCallback is nullptr and otherwise returns
611+ // / isActorDerivedCallback().
612+ bool isActorDerived (Element elt) const {
613+ return bool (isActorDerivedCallback) && isActorDerivedCallback (elt);
614+ }
615+
616+ // / Apply \p op to the partition op.
617+ void apply (PartitionOp op) {
618+ if (emitLog) {
619+ REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " Applying: " ;
620+ op.print (llvm::dbgs ()));
621+ REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " Before: " ;
622+ p.print (llvm::dbgs ()));
623+ }
624+ SWIFT_DEFER {
625+ if (emitLog) {
626+ REGIONBASEDISOLATION_VERBOSE_LOG (llvm::dbgs () << " After: " ;
627+ p.print (llvm::dbgs ()));
628+ }
629+ };
630+
631+ switch (op.getKind ()) {
632+ case PartitionOpKind::Assign:
633+ assert (op.getOpArgs ().size () == 2 &&
634+ " Assign PartitionOp should be passed 2 arguments" );
635+ assert (p.labels .count (op.getOpArgs ()[1 ]) &&
636+ " Assign PartitionOp's source argument should be already tracked" );
637+ // if assigning to a missing region, handle the failure
638+ if (p.isTransferred (op.getOpArgs ()[1 ]))
639+ handleFailure (op, op.getOpArgs ()[1 ]);
640+
641+ p.labels .insert_or_assign (op.getOpArgs ()[0 ],
642+ p.labels .at (op.getOpArgs ()[1 ]));
643+
644+ // assignment could have invalidated canonicality of either the old region
645+ // of op.getOpArgs()[0] or the region of op.getOpArgs()[1], or both
646+ p.canonical = false ;
647+ break ;
648+ case PartitionOpKind::AssignFresh:
649+ assert (op.getOpArgs ().size () == 1 &&
650+ " AssignFresh PartitionOp should be passed 1 argument" );
651+
652+ // map index op.getOpArgs()[0] to a fresh label
653+ p.labels .insert_or_assign (op.getOpArgs ()[0 ], p.fresh_label );
654+
655+ // increment the fresh label so it remains fresh
656+ p.fresh_label = Region (p.fresh_label + 1 );
657+ p.canonical = false ;
658+ break ;
659+ case PartitionOpKind::Transfer: {
660+ assert (op.getOpArgs ().size () == 1 &&
661+ " Transfer PartitionOp should be passed 1 argument" );
662+ assert (p.labels .count (op.getOpArgs ()[0 ]) &&
663+ " Transfer PartitionOp's argument should already be tracked" );
664+
665+ // check if any nontransferrables are transferred here, and handle the
666+ // failure if so
667+ for (Element nonTransferrable : nonTransferrableElements) {
668+ assert (
669+ p.labels .count (nonTransferrable) &&
670+ " nontransferrables should be function args and self, and therefore"
671+ " always present in the label map because of initialization at "
672+ " entry" );
673+ if (!p.isTransferred (nonTransferrable) &&
674+ p.labels .at (nonTransferrable) == p.labels .at (op.getOpArgs ()[0 ])) {
675+ handleTransferNonTransferrable (op, nonTransferrable);
676+ break ;
677+ }
678+ }
679+
680+ // If this value is actor derived or if any elements in its region are
681+ // actor derived, we need to treat as nontransferrable.
682+ if (isActorDerived (op.getOpArgs ()[0 ]))
683+ return handleTransferNonTransferrable (op, op.getOpArgs ()[0 ]);
684+ Region elementRegion = p.labels .at (op.getOpArgs ()[0 ]);
685+ if (llvm::any_of (p.labels ,
686+ [&](const std::pair<Element, Region> &pair) -> bool {
687+ if (pair.second != elementRegion)
688+ return false ;
689+ return isActorDerived (pair.first );
690+ }))
691+ return handleTransferNonTransferrable (op, op.getOpArgs ()[0 ]);
692+
693+ // Ensure if the region is transferred...
694+ if (!p.isTransferred (op.getOpArgs ()[0 ]))
695+ // that all elements associated with the region are marked as
696+ // transferred.
697+ horizontalUpdate (p.labels , op.getOpArgs ()[0 ], Region::transferred ());
698+ break ;
699+ }
700+ case PartitionOpKind::Merge:
701+ assert (op.getOpArgs ().size () == 2 &&
702+ " Merge PartitionOp should be passed 2 arguments" );
703+ assert (p.labels .count (op.getOpArgs ()[0 ]) &&
704+ p.labels .count (op.getOpArgs ()[1 ]) &&
705+ " Merge PartitionOp's arguments should already be tracked" );
706+
707+ // if attempting to merge a transferred region, handle the failure
708+ if (p.isTransferred (op.getOpArgs ()[0 ]))
709+ handleFailure (op, op.getOpArgs ()[0 ]);
710+ if (p.isTransferred (op.getOpArgs ()[1 ]))
711+ handleFailure (op, op.getOpArgs ()[1 ]);
712+
713+ p.merge (op.getOpArgs ()[0 ], op.getOpArgs ()[1 ]);
714+ break ;
715+ case PartitionOpKind::Require:
716+ assert (op.getOpArgs ().size () == 1 &&
717+ " Require PartitionOp should be passed 1 argument" );
718+ assert (p.labels .count (op.getOpArgs ()[0 ]) &&
719+ " Require PartitionOp's argument should already be tracked" );
720+ if (p.isTransferred (op.getOpArgs ()[0 ]))
721+ handleFailure (op, op.getOpArgs ()[0 ]);
722+ }
723+
724+ assert (p.is_canonical_correct ());
725+ }
726+
727+ void apply (std::initializer_list<PartitionOp> ops) {
728+ for (auto &o : ops)
729+ apply (o);
730+ }
731+ };
732+
648733} // namespace swift
649734
650735#endif // SWIFT_PARTITIONUTILS_H
0 commit comments