Skip to content

Commit bf64711

Browse files
authored
Merge pull request #69342 from gottesmm/small-region-isolation-fixes
[region-based-isolation] Stack of smaller fixes before a larger fix
2 parents 708f059 + 2c0efe6 commit bf64711

File tree

9 files changed

+461
-358
lines changed

9 files changed

+461
-358
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -877,12 +877,12 @@ NOTE(sil_referencebinding_inout_binding_here, none,
877877

878878
// Warnings arising from the flow-sensitive checking of Sendability of
879879
// non-Sendable values
880-
WARNING(arg_region_consumed, none,
880+
WARNING(arg_region_transferred, none,
881881
"call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller", ())
882-
WARNING(consumption_yields_race, none,
882+
WARNING(transfer_yields_race, none,
883883
"non-sendable value sent across isolation domains that could be concurrently accessed later in this function (%0 access site%select{|s}1 displayed%select{|, %3 more hidden}2)",
884884
(unsigned, bool, bool, unsigned))
885-
WARNING(call_site_consumption_yields_race, none,
885+
WARNING(call_site_transfer_yields_race, none,
886886
"passing argument of non-sendable type %0 from %1 context to %2 context at this call site could yield a race with accesses later in this function (%3 access site%select{|s}4 displayed%select{|, %6 more hidden}5)",
887887
(Type, ActorIsolation, ActorIsolation, unsigned, bool, bool, unsigned))
888888
NOTE(possible_racy_access_site, none,

include/swift/SIL/SILType.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,8 @@ class SILType {
878878

879879
bool isMarkedAsImmortal() const;
880880

881+
bool isActor() const { return getASTType()->isActorType(); }
882+
881883
ProtocolConformanceRef conformsToProtocol(SILFunction *fn,
882884
ProtocolDecl *protocol) const;
883885

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 72 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef SWIFT_PARTITIONUTILS_H
22
#define SWIFT_PARTITIONUTILS_H
33

4+
#include "swift/Basic/Defer.h"
45
#include "swift/Basic/LLVM.h"
56
#include "swift/SIL/SILInstruction.h"
67
#include "llvm/ADT/SmallVector.h"
@@ -13,6 +14,18 @@ namespace swift {
1314

1415
namespace PartitionPrimitives {
1516

17+
#ifndef NDEBUG
18+
extern bool REGIONBASEDISOLATION_ENABLE_VERBOSE_LOGGING;
19+
#define REGIONBASEDISOLATION_VERBOSE_LOG(...) \
20+
do { \
21+
if (REGIONBASEDISOLATION_ENABLE_VERBOSE_LOGGING) { \
22+
LLVM_DEBUG(__VA_ARGS__); \
23+
} \
24+
} while (0);
25+
#else
26+
#define REGIONBASEDISOLATION_VERBOSE_LOG(...)
27+
#endif
28+
1629
struct Element {
1730
unsigned num;
1831

@@ -36,9 +49,9 @@ struct Region {
3649

3750
operator signed() const { return num; }
3851

39-
bool isConsumed() const { return num < 0; }
52+
bool isTransferred() const { return num < 0; }
4053

41-
static Region consumed() { return Region(-1); }
54+
static Region transferred() { return Region(-1); }
4255
};
4356
}
4457

@@ -48,20 +61,20 @@ using namespace PartitionPrimitives;
4861
/// SILInstructions can be translated to
4962
enum class PartitionOpKind : uint8_t {
5063
/// Assign one value to the region of another, takes two args, second arg
51-
/// must already be tracked with a non-consumed region
64+
/// must already be tracked with a non-transferred region
5265
Assign,
5366

5467
/// Assign one value to a fresh region, takes one arg.
5568
AssignFresh,
5669

5770
/// Merge the regions of two values, takes two args, both must be from
58-
/// non-consumed regions.
71+
/// non-transferred regions.
5972
Merge,
6073

61-
/// Consume the region of a value if not already consumed, takes one arg.
74+
/// Consume the region of a value if not already transferred, takes one arg.
6275
Transfer,
6376

64-
/// Require the region of a value to be non-consumed, takes one arg.
77+
/// Require the region of a value to be non-transferred, takes one arg.
6578
Require,
6679
};
6780

@@ -79,7 +92,8 @@ class PartitionOp {
7992
SILInstruction *sourceInst;
8093

8194
// Record an AST expression corresponding to this PartitionOp, currently
82-
// populated only for Consume expressions to indicate the value being consumed
95+
// populated only for Consume expressions to indicate the value being
96+
// transferred
8397
Expr *sourceExpr;
8498

8599
// TODO: can the following declarations be merged?
@@ -163,7 +177,7 @@ class PartitionOp {
163177
os << "assign_fresh %%" << OpArgs[0] << "\n";
164178
break;
165179
case PartitionOpKind::Transfer:
166-
os << "consume %%" << OpArgs[0] << "\n";
180+
os << "transfer %%" << OpArgs[0] << "\n";
167181
break;
168182
case PartitionOpKind::Merge:
169183
os << "merge %%" << OpArgs[0] << " with %%" << OpArgs[1] << "\n";
@@ -200,8 +214,8 @@ static void horizontalUpdate(std::map<Element, Region> &map, Element key,
200214
class Partition {
201215
private:
202216
// Label each index with a non-negative (unsigned) label if it is associated
203-
// with a valid region, and with -1 if it is associated with a consumed region
204-
// in-order traversal relied upon.
217+
// with a valid region, and with -1 if it is associated with a transferred
218+
// region in-order traversal relied upon.
205219
std::map<Element, Region> labels;
206220

207221
// Track a label that is guaranteed to be strictly larger than all in use,
@@ -227,8 +241,8 @@ class Partition {
227241
};
228242

229243
for (auto &[i, label] : labels) {
230-
// correctness vacuous at consumed indices
231-
if (label.isConsumed())
244+
// correctness vacuous at transferred indices
245+
if (label.isTransferred())
232246
continue;
233247

234248
// this label should not exceed fresh_label
@@ -253,7 +267,7 @@ class Partition {
253267

254268
// linear time - For each region label that occurs, find the first index
255269
// at which it occurs and relabel all instances of it to that index.
256-
// This excludes the -1 label for consumed regions.
270+
// This excludes the -1 label for transferred regions.
257271
void canonicalize() {
258272
if (canonical)
259273
return;
@@ -263,8 +277,8 @@ class Partition {
263277

264278
// relies on in-order traversal of labels
265279
for (auto &[i, label] : labels) {
266-
// leave -1 (consumed region) as is
267-
if (label.isConsumed())
280+
// leave -1 (transferred region) as is
281+
if (label.isTransferred())
268282
continue;
269283

270284
if (!relabel.count(label)) {
@@ -309,7 +323,7 @@ class Partition {
309323
// so set to false to begin with
310324
Partition(bool canonical) : labels({}), canonical(canonical) {}
311325

312-
static Partition singleRegion(std::vector<Element> indices) {
326+
static Partition singleRegion(ArrayRef<Element> indices) {
313327
Partition p;
314328
if (!indices.empty()) {
315329
Region min_index =
@@ -335,8 +349,8 @@ class Partition {
335349

336350
bool isTracked(Element val) const { return labels.count(val); }
337351

338-
bool isConsumed(Element val) const {
339-
return isTracked(val) && labels.at(val).isConsumed();
352+
bool isTransferred(Element val) const {
353+
return isTracked(val) && labels.at(val).isTransferred();
340354
}
341355

342356
// quadratic time - Construct the partition corresponding to the join of the
@@ -361,9 +375,10 @@ class Partition {
361375
// of indices that are in the same region in snd are also in the same region
362376
// in fst - the desired property
363377
for (const auto [i, snd_label] : snd_reduced.labels) {
364-
if (snd_label.isConsumed())
365-
// if snd says that the region has been consumed, mark it consumed in fst
366-
horizontalUpdate(fst_reduced.labels, i, Region::consumed());
378+
if (snd_label.isTransferred())
379+
// if snd says that the region has been transferred, mark it transferred
380+
// in fst
381+
horizontalUpdate(fst_reduced.labels, i, Region::transferred());
367382
else
368383
fst_reduced.merge(i, Element(snd_label));
369384
}
@@ -381,29 +396,35 @@ class Partition {
381396

382397
// Apply the passed PartitionOp to this partition, performing its action.
383398
// A `handleFailure` closure can optionally be passed in that will be called
384-
// if a consumed region is required. The closure is given the PartitionOp that
385-
// failed, and the index of the SIL value that was required but consumed.
386-
// Additionally, a list of "nonconsumable" indices can be passed in along with
387-
// a handleConsumeNonConsumable closure. In the event that a region containing
388-
// one of the nonconsumable indices is consumed, the closure will be called
389-
// with the offending Consume.
399+
// if a transferred region is required. The closure is given the PartitionOp
400+
// that failed, and the index of the SIL value that was required but
401+
// transferred. Additionally, a list of "nonconsumable" indices can be passed
402+
// in along with a handleConsumeNonConsumable closure. In the event that a
403+
// region containing one of the nonconsumable indices is transferred, the
404+
// closure will be called with the offending Consume.
390405
void apply(
391406
PartitionOp op,
392407
llvm::function_ref<void(const PartitionOp &, Element)> handleFailure =
393408
[](const PartitionOp &, Element) {},
394-
395409
ArrayRef<Element> nonconsumables = {},
396-
397410
llvm::function_ref<void(const PartitionOp &, Element)>
398411
handleConsumeNonConsumable = [](const PartitionOp &, Element) {}) {
412+
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << "Applying: ";
413+
op.print(llvm::dbgs()));
414+
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << " Before: ";
415+
print(llvm::dbgs()));
416+
SWIFT_DEFER {
417+
REGIONBASEDISOLATION_VERBOSE_LOG(llvm::dbgs() << " After: ";
418+
print(llvm::dbgs()));
419+
};
399420
switch (op.OpKind) {
400421
case PartitionOpKind::Assign:
401422
assert(op.OpArgs.size() == 2 &&
402423
"Assign PartitionOp should be passed 2 arguments");
403424
assert(labels.count(op.OpArgs[1]) &&
404425
"Assign PartitionOp's source argument should be already tracked");
405426
// if assigning to a missing region, handle the failure
406-
if (isConsumed(op.OpArgs[1]))
427+
if (isTransferred(op.OpArgs[1]))
407428
handleFailure(op, op.OpArgs[1]);
408429

409430
labels.insert_or_assign(op.OpArgs[0], labels.at(op.OpArgs[1]));
@@ -429,25 +450,24 @@ class Partition {
429450
assert(labels.count(op.OpArgs[0]) &&
430451
"Consume PartitionOp's argument should already be tracked");
431452

432-
// check if any nonconsumables are consumed here, and handle the failure if so
453+
// check if any nonconsumables are transferred here, and handle the
454+
// failure if so
433455
for (Element nonconsumable : nonconsumables) {
434456
assert(labels.count(nonconsumable) &&
435457
"nonconsumables should be function args and self, and therefore"
436458
"always present in the label map because of initialization at "
437459
"entry");
438-
if (!isConsumed(nonconsumable) &&
460+
if (!isTransferred(nonconsumable) &&
439461
labels.at(nonconsumable) == labels.at(op.OpArgs[0])) {
440462
handleConsumeNonConsumable(op, nonconsumable);
441463
break;
442464
}
443465
}
444466

445-
// ensure region is consumed
446-
if (!isConsumed(op.OpArgs[0]))
447-
// mark region as consumed
448-
horizontalUpdate(labels, op.OpArgs[0], Region::consumed());
449-
450-
467+
// ensure region is transferred
468+
if (!isTransferred(op.OpArgs[0]))
469+
// mark region as transferred
470+
horizontalUpdate(labels, op.OpArgs[0], Region::transferred());
451471

452472
break;
453473
case PartitionOpKind::Merge:
@@ -456,10 +476,10 @@ class Partition {
456476
assert(labels.count(op.OpArgs[0]) && labels.count(op.OpArgs[1]) &&
457477
"Merge PartitionOp's arguments should already be tracked");
458478

459-
// if attempting to merge a consumed region, handle the failure
460-
if (isConsumed(op.OpArgs[0]))
479+
// if attempting to merge a transferred region, handle the failure
480+
if (isTransferred(op.OpArgs[0]))
461481
handleFailure(op, op.OpArgs[0]);
462-
if (isConsumed(op.OpArgs[1]))
482+
if (isTransferred(op.OpArgs[1]))
463483
handleFailure(op, op.OpArgs[1]);
464484

465485
merge(op.OpArgs[0], op.OpArgs[1]);
@@ -469,26 +489,26 @@ class Partition {
469489
"Require PartitionOp should be passed 1 argument");
470490
assert(labels.count(op.OpArgs[0]) &&
471491
"Require PartitionOp's argument should already be tracked");
472-
if (isConsumed(op.OpArgs[0]))
492+
if (isTransferred(op.OpArgs[0]))
473493
handleFailure(op, op.OpArgs[0]);
474494
}
475495

476496
assert(is_canonical_correct());
477497
}
478498

479-
// return a vector of the consumed values in this partition
480-
std::vector<Element> getConsumedVals() const {
499+
// return a vector of the transferred values in this partition
500+
std::vector<Element> getTransferredVals() const {
481501
// for effeciency, this could return an iterator not a vector
482-
std::vector<Element> consumedVals;
502+
std::vector<Element> transferredVals;
483503
for (auto [i, _] : labels)
484-
if (isConsumed(i))
485-
consumedVals.push_back(i);
486-
return consumedVals;
504+
if (isTransferred(i))
505+
transferredVals.push_back(i);
506+
return transferredVals;
487507
}
488508

489-
// return a vector of the non-consumed regions in this partition, each
509+
// return a vector of the non-transferred regions in this partition, each
490510
// represented as a vector of values
491-
std::vector<std::vector<Element>> getNonConsumedRegions() const {
511+
std::vector<std::vector<Element>> getNonTransferredRegions() const {
492512
// for effeciency, this could return an iterator not a vector
493513
std::map<Region, std::vector<Element>> buckets;
494514

@@ -523,12 +543,12 @@ class Partition {
523543

524544
os << "[";
525545
for (auto [label, indices] : buckets) {
526-
os << (label.isConsumed() ? "{" : "(");
546+
os << (label.isTransferred() ? "{" : "(");
527547
int j = 0;
528548
for (Element i : indices) {
529549
os << (j++ ? " " : "") << i;
530550
}
531-
os << (label.isConsumed() ? "}" : ")");
551+
os << (label.isTransferred() ? "}" : ")");
532552
}
533553
os << "]\n";
534554
}

0 commit comments

Comments
 (0)