@@ -90,22 +90,22 @@ enum class PartitionOpKind : uint8_t {
90
90
Require,
91
91
};
92
92
93
- // PartitionOp represents a primitive operation that can be performed on
94
- // Partitions. This is part of the TransferNonSendable SIL pass workflow:
95
- // first SILBasicBlocks are compiled to vectors of PartitionOps, then a fixed
96
- // point partition is found over the CFG.
93
+ // / PartitionOp represents a primitive operation that can be performed on
94
+ // / Partitions. This is part of the TransferNonSendable SIL pass workflow:
95
+ // / first SILBasicBlocks are compiled to vectors of PartitionOps, then a fixed
96
+ // / point partition is found over the CFG.
97
97
class PartitionOp {
98
98
private:
99
99
PartitionOpKind OpKind;
100
100
llvm::SmallVector<Element, 2 > OpArgs;
101
101
102
- // Record the SILInstruction that this PartitionOp was generated from, if
103
- // generated during compilation from a SILBasicBlock
102
+ // / Record the SILInstruction that this PartitionOp was generated from, if
103
+ // / generated during compilation from a SILBasicBlock
104
104
SILInstruction *sourceInst;
105
105
106
- // Record an AST expression corresponding to this PartitionOp, currently
107
- // populated only for Consume expressions to indicate the value being
108
- // transferred
106
+ // / Record an AST expression corresponding to this PartitionOp, currently
107
+ // / populated only for Consume expressions to indicate the value being
108
+ // / transferred
109
109
Expr *sourceExpr;
110
110
111
111
// TODO: can the following declarations be merged?
@@ -154,7 +154,7 @@ class PartitionOp {
154
154
&& OpArgs == other.OpArgs
155
155
&& sourceInst == other.sourceInst ;
156
156
};
157
- // implemented for insertion into std::map
157
+
158
158
bool operator <(const PartitionOp &other) const {
159
159
if (OpKind != other.OpKind )
160
160
return OpKind < other.OpKind ;
@@ -202,12 +202,12 @@ class PartitionOp {
202
202
}
203
203
};
204
204
205
- // For the passed `map`, ensure that `key` maps to `val`. If `key` already
206
- // mapped to a different value, ensure that all other keys mapped to that
207
- // value also now map to `val`. This is a relatively expensive (linear time)
208
- // operation that's unfortunately used pervasively throughout PartitionOp
209
- // application. If this is a performance bottleneck, let's consider optimizing
210
- // it to a true union-find or other tree-based data structure.
205
+ // / For the passed `map`, ensure that `key` maps to `val`. If `key` already
206
+ // / mapped to a different value, ensure that all other keys mapped to that
207
+ // / value also now map to `val`. This is a relatively expensive (linear time)
208
+ // / operation that's unfortunately used pervasively throughout PartitionOp
209
+ // / application. If this is a performance bottleneck, let's consider optimizing
210
+ // / it to a true union-find or other tree-based data structure.
211
211
static void horizontalUpdate (std::map<Element, Region> &map, Element key,
212
212
Region val) {
213
213
if (!map.count (key)) {
@@ -224,121 +224,36 @@ static void horizontalUpdate(std::map<Element, Region> &map, Element key,
224
224
map.insert_or_assign (otherKey, val);
225
225
}
226
226
227
+ // / A map from Element -> Region that represents the current partition set.
228
+ // /
229
+ // /
227
230
class Partition {
228
231
public:
229
232
// / A class defined in PartitionUtils unittest used to grab state from
230
233
// / Partition without exposing it to other users.
231
234
struct PartitionTester ;
232
235
233
236
private:
234
- // Label each index with a non-negative (unsigned) label if it is associated
235
- // with a valid region, and with -1 if it is associated with a transferred
236
- // region in-order traversal relied upon.
237
+ // / Label each index with a non-negative (unsigned) label if it is associated
238
+ // / with a valid region, and with -1 if it is associated with a transferred
239
+ // / region in-order traversal relied upon.
237
240
std::map<Element, Region> labels;
238
241
239
- // Track a label that is guaranteed to be strictly larger than all in use,
240
- // and therefore safe for use as a fresh label.
242
+ // / Track a label that is guaranteed to be strictly larger than all in use,
243
+ // / and therefore safe for use as a fresh label.
241
244
Region fresh_label = Region(0 );
242
245
243
- // In a canonical partition, all regions are labelled with the smallest index
244
- // of any member. Certain operations like join and equals rely on canonicality
245
- // so when it's invalidated this boolean tracks that, and it must be
246
- // reestablished by a call to canonicalize().
246
+ // / In a canonical partition, all regions are labelled with the smallest index
247
+ // / of any member. Certain operations like join and equals rely on
248
+ // / canonicality so when it's invalidated this boolean tracks that, and it
249
+ // / must be reestablished by a call to canonicalize().
247
250
bool canonical;
248
251
249
- // Used only in assertions, check that Partitions promised to be canonical
250
- // are actually canonical
251
- bool is_canonical_correct () {
252
- if (!canonical)
253
- return true ; // vacuously correct
254
-
255
- auto fail = [&](Element i, int type) {
256
- llvm::dbgs () << " FAIL(i=" << i << " ; type=" << type << " ): " ;
257
- print (llvm::dbgs ());
258
- return false ;
259
- };
260
-
261
- for (auto &[i, label] : labels) {
262
- // correctness vacuous at transferred indices
263
- if (label.isTransferred ())
264
- continue ;
265
-
266
- // this label should not exceed fresh_label
267
- if (label >= fresh_label)
268
- return fail (i, 0 );
269
-
270
- // the label of a region should be at most as large as each index in it
271
- if ((unsigned )label > i)
272
- return fail (i, 1 );
273
-
274
- // each region label should also be an element of the partition
275
- if (!labels.count (Element (label)))
276
- return fail (i, 2 );
277
-
278
- // each element that is also a region label should be mapped to itself
279
- if (labels.at (Element (label)) != label)
280
- return fail (i, 3 );
281
- }
282
-
283
- return true ;
284
- }
285
-
286
- // linear time - For each region label that occurs, find the first index
287
- // at which it occurs and relabel all instances of it to that index.
288
- // This excludes the -1 label for transferred regions.
289
- void canonicalize () {
290
- if (canonical)
291
- return ;
292
- canonical = true ;
293
-
294
- std::map<Region, Region> relabel;
295
-
296
- // relies on in-order traversal of labels
297
- for (auto &[i, label] : labels) {
298
- // leave -1 (transferred region) as is
299
- if (label.isTransferred ())
300
- continue ;
301
-
302
- if (!relabel.count (label)) {
303
- // if this is the first time encountering this region label,
304
- // then this region label should be relabelled to this index,
305
- // so enter that into the map
306
- relabel.insert_or_assign (label, Region (i));
307
- }
308
-
309
- // update this label with either its own index, or a prior index that
310
- // shared a region with it
311
- label = relabel.at (label);
312
-
313
- // the maximum index iterated over will be used here to appropriately
314
- // set fresh_label
315
- fresh_label = Region (i + 1 );
316
- }
317
-
318
- assert (is_canonical_correct ());
319
- }
320
-
321
- // linear time - merge the regions of two indices, maintaining canonicality
322
- void merge (Element fst, Element snd) {
323
- assert (labels.count (fst) && labels.count (snd));
324
- if (labels.at (fst) == labels.at (snd))
325
- return ;
326
-
327
- // maintain canonicality by renaming the greater-numbered region
328
- if (labels.at (fst) < labels.at (snd))
329
- horizontalUpdate (labels, snd, labels.at (fst));
330
- else
331
- horizontalUpdate (labels, fst, labels.at (snd));
332
-
333
- assert (is_canonical_correct ());
334
- assert (labels.at (fst) == labels.at (snd));
335
- }
336
-
337
252
public:
338
253
Partition () : labels({}), canonical(true ) {}
339
254
340
- // 1-arg constructor used when canonicality will be immediately invalidated,
341
- // so set to false to begin with
255
+ // / 1-arg constructor used when canonicality will be immediately invalidated,
256
+ // / so set to false to begin with
342
257
Partition (bool canonical) : labels({}), canonical(canonical) {}
343
258
344
259
static Partition singleRegion (ArrayRef<Element> indices) {
@@ -371,8 +286,10 @@ class Partition {
371
286
return p;
372
287
}
373
288
374
- // linear time - Test two partititons for equality by first putting them
375
- // in canonical form then comparing for exact equality.
289
+ // / Test two partititons for equality by first putting them in canonical form
290
+ // / then comparing for exact equality.
291
+ // /
292
+ // / Runs in linear time.
376
293
static bool equals (Partition &fst, Partition &snd) {
377
294
fst.canonicalize ();
378
295
snd.canonicalize ();
@@ -386,10 +303,10 @@ class Partition {
386
303
return isTracked (val) && labels.at (val).isTransferred ();
387
304
}
388
305
389
- // quadratic time - Construct the partition corresponding to the join of the
390
- // two passed partitions; the join labels each index labelled by both operands
391
- // and two indices are in the same region of the join iff they are in the same
392
- // region in either operand .
306
+ // / Construct the partition corresponding to the union of the two passed
307
+ // / partitions.
308
+ // /
309
+ // / Runs in quadratic time .
393
310
static Partition join (const Partition &fst, const Partition &snd) {
394
311
// First copy and canonicalize our inputs.
395
312
Partition fst_reduced = fst;
@@ -460,14 +377,14 @@ class Partition {
460
377
return fst_reduced;
461
378
}
462
379
463
- // Apply the passed PartitionOp to this partition, performing its action. A
464
- // `handleFailure` closure can optionally be passed in that will be called if
465
- // a transferred region is required. The closure is given the PartitionOp that
466
- // failed, and the index of the SIL value that was required but
467
- // transferred. Additionally, a list of "nonconsumable" indices can be passed
468
- // in along with a handleConsumeNonConsumable closure. In the event that a
469
- // region containing one of the nonconsumable indices is transferred, the
470
- // closure will be called with the offending transfer.
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 "nonconsumable" indices can be passed
385
+ // / in along with a handleConsumeNonConsumable closure. In the event that a
386
+ // / region containing one of the nonconsumable indices is transferred, the
387
+ // / closure will be called with the offending transfer.
471
388
void apply (
472
389
PartitionOp op,
473
390
llvm::function_ref<void (const PartitionOp &, Element)> handleFailure =
@@ -578,20 +495,20 @@ class Partition {
578
495
assert (is_canonical_correct ());
579
496
}
580
497
581
- // return a vector of the transferred values in this partition
498
+ // / Return a vector of the transferred values in this partition.
582
499
std::vector<Element> getTransferredVals () const {
583
- // for effeciency, this could return an iterator not a vector
500
+ // For effeciency, this could return an iterator not a vector.
584
501
std::vector<Element> transferredVals;
585
502
for (auto [i, _] : labels)
586
503
if (isTransferred (i))
587
504
transferredVals.push_back (i);
588
505
return transferredVals;
589
506
}
590
507
591
- // return a vector of the non-transferred regions in this partition, each
592
- // represented as a vector of values
508
+ // / Return a vector of the non-transferred regions in this partition, each
509
+ // / represented as a vector of values.
593
510
std::vector<std::vector<Element>> getNonTransferredRegions () const {
594
- // for effeciency, this could return an iterator not a vector
511
+ // For effeciency, this could return an iterator not a vector.
595
512
std::map<Region, std::vector<Element>> buckets;
596
513
597
514
for (auto [i, label] : labels)
@@ -634,6 +551,97 @@ class Partition {
634
551
}
635
552
os << " ]\n " ;
636
553
}
554
+
555
+ private:
556
+ // / Used only in assertions, check that Partitions promised to be canonical
557
+ // / are actually canonical
558
+ bool is_canonical_correct () {
559
+ if (!canonical)
560
+ return true ; // vacuously correct
561
+
562
+ auto fail = [&](Element i, int type) {
563
+ llvm::dbgs () << " FAIL(i=" << i << " ; type=" << type << " ): " ;
564
+ print (llvm::dbgs ());
565
+ return false ;
566
+ };
567
+
568
+ for (auto &[i, label] : labels) {
569
+ // Correctness vacuous at transferred indices.
570
+ if (label.isTransferred ())
571
+ continue ;
572
+
573
+ // Labels should not exceed fresh_label.
574
+ if (label >= fresh_label)
575
+ return fail (i, 0 );
576
+
577
+ // The label of a region should be at most as large as each index in it.
578
+ if ((unsigned )label > i)
579
+ return fail (i, 1 );
580
+
581
+ // Each region label should also be an element of the partition.
582
+ if (!labels.count (Element (label)))
583
+ return fail (i, 2 );
584
+
585
+ // Each element that is also a region label should be mapped to itself.
586
+ if (labels.at (Element (label)) != label)
587
+ return fail (i, 3 );
588
+ }
589
+
590
+ return true ;
591
+ }
592
+
593
+ // / For each region label that occurs, find the first index at which it occurs
594
+ // / and relabel all instances of it to that index. This excludes the -1 label
595
+ // / for transferred regions.
596
+ // /
597
+ // / This runs in linear time.
598
+ void canonicalize () {
599
+ if (canonical)
600
+ return ;
601
+ canonical = true ;
602
+
603
+ std::map<Region, Region> relabel;
604
+
605
+ // relies on in-order traversal of labels
606
+ for (auto &[i, label] : labels) {
607
+ // leave -1 (transferred region) as is
608
+ if (label.isTransferred ())
609
+ continue ;
610
+
611
+ if (!relabel.count (label)) {
612
+ // if this is the first time encountering this region label,
613
+ // then this region label should be relabelled to this index,
614
+ // so enter that into the map
615
+ relabel.insert_or_assign (label, Region (i));
616
+ }
617
+
618
+ // update this label with either its own index, or a prior index that
619
+ // shared a region with it
620
+ label = relabel.at (label);
621
+
622
+ // the maximum index iterated over will be used here to appropriately
623
+ // set fresh_label
624
+ fresh_label = Region (i + 1 );
625
+ }
626
+
627
+ assert (is_canonical_correct ());
628
+ }
629
+
630
+ // linear time - merge the regions of two indices, maintaining canonicality
631
+ void merge (Element fst, Element snd) {
632
+ assert (labels.count (fst) && labels.count (snd));
633
+ if (labels.at (fst) == labels.at (snd))
634
+ return ;
635
+
636
+ // maintain canonicality by renaming the greater-numbered region
637
+ if (labels.at (fst) < labels.at (snd))
638
+ horizontalUpdate (labels, snd, labels.at (fst));
639
+ else
640
+ horizontalUpdate (labels, fst, labels.at (snd));
641
+
642
+ assert (is_canonical_correct ());
643
+ assert (labels.at (fst) == labels.at (snd));
644
+ }
637
645
};
638
646
639
647
} // namespace swift
0 commit comments