Skip to content

Commit b2c85a8

Browse files
committed
[region-isolation] Rather than tracking task isolated values via a separate non-transferrable array... just track it by using ValueIsolationRegionInfo on a value.
In a subsequent commit, this is going to let me begin handling parameters with actor regions in a nice way (and standardize all of the errors). This is meant to be a refactoring commit that uses the current tests in tree to make sure I did it correctly, so no tests need to be updated.
1 parent f2355ea commit b2c85a8

File tree

4 files changed

+158
-117
lines changed

4 files changed

+158
-117
lines changed

include/swift/SILOptimizer/Analysis/RegionAnalysis.h

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -139,33 +139,34 @@ class ValueIsolationRegionInfo {
139139
enum Kind {
140140
Unknown,
141141
Disconnected,
142+
Task,
142143
Actor,
143144
};
144145

145146
private:
146147
Kind kind;
147-
148-
/// When this is set it corresponds to a specific ActorIsolation from the AST
149-
/// that we found.
150-
///
151-
/// NOTE: actorInstanceType and actorIsolation should never be both set!
152-
std::optional<ActorIsolation> actorIsolation;
153-
154-
/// When this is set it corresponds to a specific Actor type that we
155-
/// identified as being actor isolated from a SIL level type. We do not have
156-
/// the ActorIsolation information, so this is artificial.
157-
///
158-
/// NOTE: actorInstanceType and actorIsolation should never be both set!
159-
NominalTypeDecl *actorInstanceType = nullptr;
148+
// clang-format off
149+
std::variant<
150+
// Used for actor isolated when we have ActorIsolation info from the AST.
151+
std::optional<ActorIsolation>,
152+
// Used for actor isolation when we infer the actor at the SIL level.
153+
NominalTypeDecl *,
154+
// The task isolated parameter when we find a task isolated value.
155+
SILValue
156+
> data;
157+
// clang-format on
160158

161159
ValueIsolationRegionInfo(Kind kind,
162-
std::optional<ActorIsolation> actorIsolation,
163-
NominalTypeDecl *actorInstanceType = nullptr)
164-
: kind(kind), actorIsolation(actorIsolation),
165-
actorInstanceType(actorInstanceType) {}
160+
std::optional<ActorIsolation> actorIsolation)
161+
: kind(kind), data(actorIsolation) {}
162+
ValueIsolationRegionInfo(Kind kind, NominalTypeDecl *decl)
163+
: kind(kind), data(decl) {}
164+
165+
ValueIsolationRegionInfo(Kind kind, SILValue value)
166+
: kind(kind), data(value) {}
166167

167168
public:
168-
ValueIsolationRegionInfo() : kind(Kind::Unknown), actorIsolation() {}
169+
ValueIsolationRegionInfo() : kind(Kind::Unknown), data() {}
169170

170171
operator bool() const { return kind != Kind::Unknown; }
171172

@@ -175,6 +176,7 @@ class ValueIsolationRegionInfo {
175176

176177
bool isDisconnected() const { return kind == Kind::Disconnected; }
177178
bool isActorIsolated() const { return kind == Kind::Actor; }
179+
bool isTaskIsolated() const { return kind == Kind::Task; }
178180

179181
void print(llvm::raw_ostream &os) const {
180182
switch (Kind(*this)) {
@@ -187,6 +189,9 @@ class ValueIsolationRegionInfo {
187189
case Actor:
188190
os << "actor";
189191
return;
192+
case Task:
193+
os << "task";
194+
return;
190195
}
191196
}
192197

@@ -196,14 +201,24 @@ class ValueIsolationRegionInfo {
196201
}
197202

198203
std::optional<ActorIsolation> getActorIsolation() const {
199-
assert(!actorInstanceType &&
200-
"Should never be set if getActorIsolation is called");
201-
return actorIsolation;
204+
assert(kind == Actor);
205+
assert(std::holds_alternative<NominalTypeDecl *>(data) &&
206+
"Doesn't have an actor isolation?!");
207+
return std::get<std::optional<ActorIsolation>>(data);
202208
}
203209

204210
NominalTypeDecl *getActorInstance() const {
205-
assert(!actorIsolation.has_value());
206-
return actorInstanceType;
211+
assert(kind == Actor);
212+
assert(std::holds_alternative<NominalTypeDecl *>(data) &&
213+
"Doesn't have an actor instance?!");
214+
return std::get<NominalTypeDecl *>(data);
215+
}
216+
217+
SILValue getTaskIsolatedValue() const {
218+
assert(kind == Task);
219+
assert(std::holds_alternative<SILValue>(data) &&
220+
"Doesn't have a task isolated value");
221+
return std::get<SILValue>(data);
207222
}
208223

209224
[[nodiscard]] ValueIsolationRegionInfo
@@ -240,9 +255,13 @@ class ValueIsolationRegionInfo {
240255
if (actorIsolation.isActorIsolated())
241256
return getActorIsolated(actorIsolation);
242257
if (nomDecl->isActor())
243-
return {Kind::Actor, {}, nomDecl};
258+
return {Kind::Actor, nomDecl};
244259
return {};
245260
}
261+
262+
static ValueIsolationRegionInfo getTaskIsolated(SILValue value) {
263+
return {Kind::Task, value};
264+
}
246265
};
247266

248267
} // namespace regionanalysisimpl
@@ -432,11 +451,6 @@ class RegionAnalysisValueMap {
432451
equivalenceClassValuesToState;
433452
llvm::DenseMap<unsigned, RepresentativeValue> stateIndexToEquivalenceClass;
434453

435-
/// A list of values that can never be transferred.
436-
///
437-
/// This only includes function arguments.
438-
std::vector<TrackableValueID> neverTransferredValueIDs;
439-
440454
SILFunction *fn;
441455

442456
public:
@@ -451,12 +465,7 @@ class RegionAnalysisValueMap {
451465
SILValue maybeGetRepresentative(Element trackableValueID) const;
452466

453467
ValueIsolationRegionInfo getIsolationRegion(Element trackableValueID) const;
454-
455-
ArrayRef<Element> getNonTransferrableElements() const {
456-
return neverTransferredValueIDs;
457-
}
458-
459-
void sortUniqueNeverTransferredValues();
468+
ValueIsolationRegionInfo getIsolationRegion(SILValue trackableValueID) const;
460469

461470
void print(llvm::raw_ostream &os) const;
462471
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
@@ -473,9 +482,6 @@ class RegionAnalysisValueMap {
473482
ValueIsolationRegionInfo isolation) const;
474483
bool mergeIsolationRegionInfo(SILValue value,
475484
ValueIsolationRegionInfo isolation);
476-
void addNeverTransferredValueID(TrackableValueID valueID) {
477-
neverTransferredValueIDs.push_back(valueID);
478-
}
479485
bool valueHasID(SILValue value, bool dumpIfHasNoID = false);
480486
TrackableValueID lookupValueID(SILValue value);
481487
};

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,21 +1040,26 @@ struct PartitionOpEvaluator {
10401040
return asImpl().handleTransferNonTransferrable(op, elt);
10411041
}
10421042

1043+
/// Just call our CRTP subclass.
1044+
void handleTransferNonTransferrable(const PartitionOp &op, Element elt,
1045+
Element otherElement) const {
1046+
return asImpl().handleTransferNonTransferrable(op, elt, otherElement);
1047+
}
1048+
10431049
/// Call isActorDerived on our CRTP subclass.
10441050
bool isActorDerived(Element elt) const {
10451051
return asImpl().isActorDerived(elt);
10461052
}
10471053

1054+
bool isTaskIsolatedDerived(Element elt) const {
1055+
return asImpl().isTaskIsolatedDerived(elt);
1056+
}
1057+
10481058
/// Call isClosureCaptured on our CRTP subclass.
10491059
bool isClosureCaptured(Element elt, Operand *op) const {
10501060
return asImpl().isClosureCaptured(elt, op);
10511061
}
10521062

1053-
/// Call getNonTransferrableElements() on our CRTP subclass.
1054-
ArrayRef<Element> getNonTransferrableElements() const {
1055-
return asImpl().getNonTransferrableElements();
1056-
}
1057-
10581063
/// Apply \p op to the partition op.
10591064
void apply(const PartitionOp &op) const {
10601065
if (shouldEmitVerboseLogging()) {
@@ -1098,23 +1103,8 @@ struct PartitionOpEvaluator {
10981103
assert(p.isTrackingElement(op.getOpArgs()[0]) &&
10991104
"Transfer PartitionOp's argument should already be tracked");
11001105

1101-
// check if any nontransferrables are transferred here, and handle the
1102-
// failure if so
1103-
for (Element nonTransferrable : getNonTransferrableElements()) {
1104-
assert(
1105-
p.isTrackingElement(nonTransferrable) &&
1106-
"nontransferrables should be function args and self, and therefore"
1107-
"always present in the label map because of initialization at "
1108-
"entry");
1109-
if (!p.isTransferred(nonTransferrable) &&
1110-
p.areElementsInSameRegion(nonTransferrable, op.getOpArgs()[0])) {
1111-
return handleTransferNonTransferrable(op, nonTransferrable);
1112-
}
1113-
}
1114-
1115-
// If this value is actor derived or if any elements in its region are
1116-
// actor derived, we need to treat as nontransferrable.
1117-
if (isActorDerived(op.getOpArgs()[0]))
1106+
if (isActorDerived(op.getOpArgs()[0]) ||
1107+
isTaskIsolatedDerived(op.getOpArgs()[0]))
11181108
return handleTransferNonTransferrable(op, op.getOpArgs()[0]);
11191109

11201110
// NOTE: We purposely do not check here if a transferred value is already
@@ -1131,8 +1121,10 @@ struct PartitionOpEvaluator {
11311121

11321122
Region elementRegion = p.getRegion(op.getOpArgs()[0]);
11331123
for (const auto &pair : p.range()) {
1134-
if (pair.second == elementRegion && isActorDerived(pair.first))
1135-
return handleTransferNonTransferrable(op, op.getOpArgs()[0]);
1124+
if (pair.second == elementRegion &&
1125+
(isActorDerived(pair.first) || isTaskIsolatedDerived(pair.first)))
1126+
return handleTransferNonTransferrable(op, op.getOpArgs()[0],
1127+
pair.first);
11361128
isClosureCapturedElt |= isClosureCaptured(pair.first, op.getSourceOp());
11371129
}
11381130

@@ -1229,22 +1221,26 @@ struct PartitionOpEvaluatorBaseImpl : PartitionOpEvaluator<Subclass> {
12291221
void handleFailure(const PartitionOp &op, Element elt,
12301222
TransferringOperand transferringOp) const {}
12311223

1232-
/// A list of elements that cannot be transferred. Whenever we transfer, we
1233-
/// check this list to see if we are transferring the element and then call
1234-
/// transferNonTransferrableCallback. This should consist only of function
1235-
/// arguments.
1236-
ArrayRef<Element> getNonTransferrableElements() const { return {}; }
1237-
12381224
/// This is called if we detect a never transferred element that was passed to
12391225
/// a transfer instruction.
12401226
void handleTransferNonTransferrable(const PartitionOp &op,
12411227
Element elt) const {}
12421228

1229+
/// This is called if we detect a never transferred element that was passed to
1230+
/// a transfer instruction but the actual element that could not be
1231+
/// transferred is a different element in its region.
1232+
void handleTransferNonTransferrable(const PartitionOp &op, Element elt,
1233+
Element otherEltInRegion) const {}
1234+
12431235
/// This is used to determine if an element is actor derived. If we determine
12441236
/// that a region containing such an element is transferred, we emit an error
12451237
/// since actor regions cannot be transferred.
12461238
bool isActorDerived(Element elt) const { return false; }
12471239

1240+
/// This is used to determine if an element is in the same region as a task
1241+
/// isolated value.
1242+
bool isTaskIsolatedDerived(Element elt) const { return false; }
1243+
12481244
/// Check if the representative value of \p elt is closure captured at \p
12491245
/// op.
12501246
///

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ getGlobalActorInitIsolation(SILFunction *fn) {
544544
/// in the body of our function.
545545
static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
546546
// Indirect out parameters cannot be an input transferring parameter.
547-
if (arg->getArgumentConvention().isIndirectOutParameter())
547+
if (arg->isIndirectResult() || arg->isIndirectErrorResult())
548548
return false;
549549

550550
// If we have a function argument that is closure captured by a Sendable
@@ -563,6 +563,10 @@ static bool isTransferrableFunctionArgument(SILFunctionArgument *arg) {
563563
// 2. If we have an async-let based Sendable closure, we want to allow
564564
// for the argument to be transferred in the async let's statement and
565565
// not emit an error.
566+
//
567+
// TODO: Once the async let refactoring change this will no longer be needed
568+
// since closure captures will have transferring parameters and be
569+
// non-Sendable.
566570
if (arg->isClosureCapture() &&
567571
arg->getFunction()->getLoweredFunctionType()->isSendable())
568572
return true;
@@ -1414,7 +1418,8 @@ class PartitionOpTranslator {
14141418
// Otherwise, it is one of our merged parameters. Add it to the never
14151419
// transfer list and to the region join list.
14161420
LLVM_DEBUG(llvm::dbgs() << " %%" << state->getID() << ": " << *arg);
1417-
valueMap.addNeverTransferredValueID(state->getID());
1421+
valueMap.mergeIsolationRegionInfo(
1422+
arg, ValueIsolationRegionInfo::getTaskIsolated(arg));
14181423
nonSendableJoinedIndices.push_back(state->getID());
14191424
}
14201425
}
@@ -1466,10 +1471,6 @@ class PartitionOpTranslator {
14661471
return valueMap.mergeIsolationRegionInfo(value, isolationRegion);
14671472
}
14681473

1469-
void addNeverTransferredValueID(TrackableValueID valueID) {
1470-
valueMap.addNeverTransferredValueID(valueID);
1471-
}
1472-
14731474
bool valueHasID(SILValue value, bool dumpIfHasNoID = false) {
14741475
return valueMap.valueHasID(value, dumpIfHasNoID);
14751476
}
@@ -1478,18 +1479,6 @@ class PartitionOpTranslator {
14781479
return valueMap.lookupValueID(value);
14791480
}
14801481

1481-
void sortUniqueNeverTransferredValues() {
1482-
valueMap.sortUniqueNeverTransferredValues();
1483-
}
1484-
1485-
/// Get the vector of IDs that cannot be legally transferred at any point in
1486-
/// this function.
1487-
ArrayRef<TrackableValueID> getNeverTransferredValues() const {
1488-
return valueMap.getNonTransferrableElements();
1489-
}
1490-
1491-
// ===========================================================================
1492-
14931482
public:
14941483
/// Return the partition consisting of all function arguments.
14951484
///
@@ -3098,10 +3087,6 @@ void RegionAnalysisFunctionInfo::runDataflow() {
30983087
}
30993088
}
31003089
}
3101-
3102-
// Now that we have finished processing, sort/unique our non transferred
3103-
// array.
3104-
translator->getValueMap().sortUniqueNeverTransferredValues();
31053090
}
31063091

31073092
//===----------------------------------------------------------------------===//
@@ -3137,6 +3122,14 @@ RegionAnalysisValueMap::getIsolationRegion(Element trackableValueID) const {
31373122
return iter->getValueState().getIsolationRegionInfo();
31383123
}
31393124

3125+
ValueIsolationRegionInfo
3126+
RegionAnalysisValueMap::getIsolationRegion(SILValue value) const {
3127+
auto iter = equivalenceClassValuesToState.find(RepresentativeValue(value));
3128+
if (iter == equivalenceClassValuesToState.end())
3129+
return {};
3130+
return iter->getSecond().getIsolationRegionInfo();
3131+
}
3132+
31403133
/// If \p isAddressCapturedByPartialApply is set to true, then this value is
31413134
/// an address that is captured by a partial_apply and we want to treat it as
31423135
/// may alias.
@@ -3213,9 +3206,11 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
32133206
}
32143207
}
32153208

3216-
// Otherwise refer to the oracle.
3217-
if (!isNonSendableType(value->getType(), fn))
3209+
// Otherwise refer to the oracle. If we have a Sendable value, just return.
3210+
if (!isNonSendableType(value->getType(), fn)) {
32183211
iter.first->getSecond().addFlag(TrackableValueFlag::isSendable);
3212+
return {iter.first->first, iter.first->second};
3213+
}
32193214

32203215
// Check if our base is a ref_element_addr from an actor. In such a case,
32213216
// mark this value as actor derived.
@@ -3285,8 +3280,15 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
32853280
}
32863281
}
32873282

3288-
// If our access storage is from a class, then see if we have an actor. In
3289-
// such a case, we need to add this id to the neverTransferred set.
3283+
// See if we have a non-transferring argument from a function. In such a case,
3284+
// mark the value as task isolated.
3285+
if (auto *fArg =
3286+
dyn_cast<SILFunctionArgument>(iter.first->first.getValue())) {
3287+
if (!isTransferrableFunctionArgument(fArg)) {
3288+
iter.first->getSecond().mergeIsolationRegionInfo(
3289+
ValueIsolationRegionInfo::getTaskIsolated(fArg));
3290+
}
3291+
}
32903292

32913293
return {iter.first->first, iter.first->second};
32923294
}
@@ -3348,10 +3350,6 @@ TrackableValueID RegionAnalysisValueMap::lookupValueID(SILValue value) {
33483350
return state.getID();
33493351
}
33503352

3351-
void RegionAnalysisValueMap::sortUniqueNeverTransferredValues() {
3352-
sortUnique(neverTransferredValueIDs);
3353-
}
3354-
33553353
void RegionAnalysisValueMap::print(llvm::raw_ostream &os) const {
33563354
#ifndef NDEBUG
33573355
// Since this is just used for debug output, be inefficient to make nicer

0 commit comments

Comments
 (0)