@@ -65,7 +65,8 @@ struct Region {
65
65
66
66
static Region transferred () { return Region (-1 ); }
67
67
};
68
- }
68
+
69
+ } // namespace PartitionPrimitives
69
70
70
71
using namespace PartitionPrimitives ;
71
72
@@ -180,24 +181,44 @@ class PartitionOp {
180
181
181
182
SWIFT_DEBUG_DUMP { print (llvm::dbgs ()); }
182
183
183
- void print (llvm::raw_ostream &os) const {
184
+ void print (llvm::raw_ostream &os, bool extraSpace = false ) const {
184
185
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 ];
187
192
break ;
193
+ }
188
194
case PartitionOpKind::AssignFresh:
189
195
os << " assign_fresh %%" << OpArgs[0 ];
190
196
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 ];
193
203
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 ];
196
211
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 ];
199
219
break ;
200
220
}
221
+ }
201
222
os << " : " << *getSourceInst (true );
202
223
}
203
224
};
@@ -224,6 +245,8 @@ static void horizontalUpdate(std::map<Element, Region> &map, Element key,
224
245
map.insert_or_assign (otherKey, val);
225
246
}
226
247
248
+ struct PartitionOpEvaluator ;
249
+
227
250
// / A map from Element -> Region that represents the current partition set.
228
251
// /
229
252
// /
@@ -232,6 +255,7 @@ class Partition {
232
255
// / A class defined in PartitionUtils unittest used to grab state from
233
256
// / Partition without exposing it to other users.
234
257
struct PartitionTester ;
258
+ friend PartitionOpEvaluator;
235
259
236
260
private:
237
261
// / Label each index with a non-negative (unsigned) label if it is associated
@@ -377,125 +401,6 @@ class Partition {
377
401
return fst_reduced;
378
402
}
379
403
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
-
499
404
// / Return a vector of the transferred values in this partition.
500
405
std::vector<Element> getTransferredVals () const {
501
406
// For effeciency, this could return an iterator not a vector.
@@ -645,6 +550,186 @@ class Partition {
645
550
}
646
551
};
647
552
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
+
648
733
} // namespace swift
649
734
650
735
#endif // SWIFT_PARTITIONUTILS_H
0 commit comments