Skip to content

Commit 0c25480

Browse files
committed
[region-isolation] Allow for unapplied isolated parameter ownership.
Given a function or a partial_apply with an isolated parameter, we do not know immediately what the actual isolation is of the function or partial_apply since we do not know which instance will be applied to the function or partial_apply. In this commit, I introduce a new bit into SILIsolationInfo that tracks this information upon construction and allows for it to merge with ownership that has the appropriate type and a specific instance. Since the values that created the two isolations, will be in the same region this should ensure that the value is only ever in a flow sensitive manner in a region with only one actor instance (since regions with isolations with differing actor instances are illegal).
1 parent 549566e commit 0c25480

File tree

3 files changed

+135
-55
lines changed

3 files changed

+135
-55
lines changed

include/swift/SILOptimizer/Utils/SILIsolationInfo.h

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ class ActorInstance {
114114
}
115115
};
116116

117+
/// The isolation info inferred for a specific SILValue. Use
118+
/// SILIsolationInfo::get() to compute these. It is intended to be a
119+
/// conservatively correct model that we expand over time with more pattern
120+
/// matching.
117121
class SILIsolationInfo {
118122
public:
119123
/// The lattice is:
@@ -128,6 +132,20 @@ class SILIsolationInfo {
128132
Actor,
129133
};
130134

135+
enum class Flag : uint8_t {
136+
None,
137+
138+
/// If set, this means that the element that we derived this from was marked
139+
/// with nonisolated(unsafe).
140+
UnsafeNonIsolated = 0x1,
141+
142+
/// If set, this means that this actor isolation is from an isolated
143+
/// parameter and should be allowed to merge into a self parameter.
144+
UnappliedIsolatedAnyParameter = 0x2,
145+
};
146+
147+
using Options = OptionSet<Flag>;
148+
131149
private:
132150
/// The actor isolation if this value has one. The default unspecified case
133151
/// otherwise.
@@ -142,13 +160,13 @@ class SILIsolationInfo {
142160
ActorInstance actorInstance;
143161

144162
unsigned kind : 8;
145-
unsigned unsafeNonIsolated : 1;
163+
unsigned options : 8;
146164

147165
SILIsolationInfo(SILValue isolatedValue, SILValue actorInstance,
148-
ActorIsolation actorIsolation, bool isUnsafeNonIsolated)
166+
ActorIsolation actorIsolation, Options options = Options())
149167
: actorIsolation(actorIsolation), isolatedValue(isolatedValue),
150168
actorInstance(ActorInstance::getForValue(actorInstance)), kind(Actor),
151-
unsafeNonIsolated(isUnsafeNonIsolated) {
169+
options(options.toRaw()) {
152170
assert((!actorInstance ||
153171
(actorIsolation.getKind() == ActorIsolation::ActorInstance &&
154172
actorInstance->getType()
@@ -159,24 +177,22 @@ class SILIsolationInfo {
159177
}
160178

161179
SILIsolationInfo(SILValue isolatedValue, ActorInstance actorInstance,
162-
ActorIsolation actorIsolation, bool isUnsafeNonIsolated)
180+
ActorIsolation actorIsolation, Options options = Options())
163181
: actorIsolation(actorIsolation), isolatedValue(isolatedValue),
164-
actorInstance(actorInstance), kind(Actor),
165-
unsafeNonIsolated(isUnsafeNonIsolated) {
182+
actorInstance(actorInstance), kind(Actor), options(options.toRaw()) {
166183
assert(actorInstance);
167184
assert(actorIsolation.getKind() == ActorIsolation::ActorInstance);
168185
}
169186

170187
SILIsolationInfo(Kind kind, SILValue isolatedValue)
171-
: actorIsolation(), isolatedValue(isolatedValue), kind(kind),
172-
unsafeNonIsolated(false) {}
188+
: actorIsolation(), isolatedValue(isolatedValue), kind(kind), options(0) {
189+
}
173190

174-
SILIsolationInfo(Kind kind, bool isUnsafeNonIsolated)
175-
: actorIsolation(), kind(kind), unsafeNonIsolated(isUnsafeNonIsolated) {}
191+
SILIsolationInfo(Kind kind, Options options = Options())
192+
: actorIsolation(), kind(kind), options(options.toRaw()) {}
176193

177194
public:
178-
SILIsolationInfo()
179-
: actorIsolation(), kind(Kind::Unknown), unsafeNonIsolated(false) {}
195+
SILIsolationInfo() : actorIsolation(), kind(Kind::Unknown), options(0) {}
180196

181197
operator bool() const { return kind != Kind::Unknown; }
182198

@@ -188,12 +204,43 @@ class SILIsolationInfo {
188204
bool isActorIsolated() const { return kind == Kind::Actor; }
189205
bool isTaskIsolated() const { return kind == Kind::Task; }
190206

191-
bool isUnsafeNonIsolated() const { return unsafeNonIsolated; }
207+
Options getOptions() const { return Options(options); }
208+
209+
void setOptions(Options newOptions) { options = newOptions.toRaw(); }
210+
211+
bool isUnsafeNonIsolated() const {
212+
return getOptions().contains(Flag::UnsafeNonIsolated);
213+
}
192214

193215
SILIsolationInfo withUnsafeNonIsolated(bool newValue = true) const {
194216
assert(*this && "Cannot be unknown");
195217
auto self = *this;
196-
self.unsafeNonIsolated = newValue;
218+
if (newValue) {
219+
self.options = (self.getOptions() | Flag::UnsafeNonIsolated).toRaw();
220+
} else {
221+
self.options =
222+
self.getOptions().toRaw() & ~Options(Flag::UnsafeNonIsolated).toRaw();
223+
}
224+
return self;
225+
}
226+
227+
/// Returns true if this actor isolation is derived from an unapplied
228+
/// isolation parameter. When merging, we allow for this to be merged with a
229+
/// more specific isolation kind.
230+
bool isUnappliedIsolatedAnyParameter() const {
231+
return getOptions().contains(Flag::UnappliedIsolatedAnyParameter);
232+
}
233+
234+
SILIsolationInfo withUnappliedIsolatedParameter(bool newValue = true) const {
235+
assert(*this && "Cannot be unknown");
236+
auto self = *this;
237+
if (newValue) {
238+
self.options =
239+
(self.getOptions() | Flag::UnappliedIsolatedAnyParameter).toRaw();
240+
} else {
241+
self.options = self.getOptions().toRaw() &
242+
~Options(Flag::UnappliedIsolatedAnyParameter).toRaw();
243+
}
197244
return self;
198245
}
199246

@@ -243,7 +290,8 @@ class SILIsolationInfo {
243290
}
244291

245292
static SILIsolationInfo getDisconnected(bool isUnsafeNonIsolated) {
246-
return {Kind::Disconnected, isUnsafeNonIsolated};
293+
return {Kind::Disconnected,
294+
isUnsafeNonIsolated ? Flag::UnsafeNonIsolated : Flag::None};
247295
}
248296

249297
/// Create an actor isolation for a value that we know is actor isolated to a
@@ -261,7 +309,7 @@ class SILIsolationInfo {
261309
getFlowSensitiveActorIsolated(SILValue isolatedValue,
262310
ActorIsolation actorIsolation) {
263311
return {isolatedValue, SILValue(), actorIsolation,
264-
false /*nonisolated(unsafe)*/};
312+
Flag::UnappliedIsolatedAnyParameter};
265313
}
266314

267315
/// Only use this as a fallback if we cannot find better information.
@@ -270,8 +318,7 @@ class SILIsolationInfo {
270318
if (crossing.getCalleeIsolation().isActorIsolated()) {
271319
// SIL level, just let it through
272320
return SILIsolationInfo(SILValue(), SILValue(),
273-
crossing.getCalleeIsolation(),
274-
false /*nonisolated(unsafe)*/);
321+
crossing.getCalleeIsolation());
275322
}
276323

277324
return {};
@@ -287,8 +334,7 @@ class SILIsolationInfo {
287334
return {};
288335
}
289336
return {isolatedValue, actorInstance,
290-
ActorIsolation::forActorInstanceSelf(typeDecl),
291-
false /*nonisolated(unsafe)*/};
337+
ActorIsolation::forActorInstanceSelf(typeDecl)};
292338
}
293339

294340
static SILIsolationInfo getActorInstanceIsolated(SILValue isolatedValue,
@@ -301,8 +347,7 @@ class SILIsolationInfo {
301347
return {};
302348
}
303349
return {isolatedValue, actorInstance,
304-
ActorIsolation::forActorInstanceSelf(typeDecl),
305-
false /*nonisolated(unsafe)*/};
350+
ActorIsolation::forActorInstanceSelf(typeDecl)};
306351
}
307352

308353
/// A special actor instance isolated for partial apply cases where we do not
@@ -318,14 +363,13 @@ class SILIsolationInfo {
318363
}
319364
return {isolatedValue, SILValue(),
320365
ActorIsolation::forActorInstanceSelf(typeDecl),
321-
false /*nonisolated(unsafe)*/};
366+
Flag::UnappliedIsolatedAnyParameter};
322367
}
323368

324369
static SILIsolationInfo getGlobalActorIsolated(SILValue value,
325370
Type globalActorType) {
326371
return {value, SILValue() /*no actor instance*/,
327-
ActorIsolation::forGlobalActor(globalActorType),
328-
false /*nonisolated(unsafe)*/};
372+
ActorIsolation::forGlobalActor(globalActorType)};
329373
}
330374

331375
static SILIsolationInfo getGlobalActorIsolated(SILValue value,
@@ -395,7 +439,7 @@ class SILDynamicMergedIsolationInfo {
395439

396440
public:
397441
SILDynamicMergedIsolationInfo() : innerInfo() {}
398-
explicit SILDynamicMergedIsolationInfo(SILIsolationInfo innerInfo)
442+
SILDynamicMergedIsolationInfo(SILIsolationInfo innerInfo)
399443
: innerInfo(innerInfo) {}
400444

401445
/// Returns nullptr only if both this isolation info and \p other are actor

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,8 +1635,9 @@ class PartitionOpTranslator {
16351635
if (originalMergedInfo)
16361636
originalMergedInfo->printForDiagnostics(llvm::dbgs());
16371637
else llvm::dbgs() << "nil";
1638-
llvm::dbgs() << "\nValue: "
1638+
llvm::dbgs() << "\nValue Rep: "
16391639
<< value->getRepresentative().getValue();
1640+
llvm::dbgs() << "Original Src: " << src;
16401641
llvm::dbgs() << "Value Info: ";
16411642
value->getIsolationRegionInfo().printForDiagnostics(llvm::dbgs());
16421643
llvm::dbgs() << "\n");

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -875,15 +875,38 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
875875
}
876876

877877
void SILIsolationInfo::print(llvm::raw_ostream &os) const {
878+
auto printOptions = [&] {
879+
auto opts = getOptions();
880+
if (!opts)
881+
return;
882+
883+
os << ": ";
884+
885+
std::array<std::pair<Flag, StringLiteral>, 2> data = {
886+
std::make_pair(Flag::UnsafeNonIsolated,
887+
StringLiteral("nonisolated(unsafe)")),
888+
std::make_pair(Flag::UnappliedIsolatedAnyParameter,
889+
StringLiteral("unapplied_isolated_parameter")),
890+
};
891+
892+
llvm::interleave(
893+
data, os,
894+
[&](const std::pair<Flag, StringLiteral> &value) {
895+
opts -= value.first;
896+
os << value.second;
897+
},
898+
", ");
899+
900+
assert(!opts && "Unhandled flag?!");
901+
};
902+
878903
switch (Kind(*this)) {
879904
case Unknown:
880905
os << "unknown";
881906
return;
882907
case Disconnected:
883908
os << "disconnected";
884-
if (unsafeNonIsolated) {
885-
os << ": nonisolated(unsafe)";
886-
}
909+
printOptions();
887910
return;
888911
case Actor:
889912
if (ActorInstance instance = getActorInstance()) {
@@ -892,9 +915,7 @@ void SILIsolationInfo::print(llvm::raw_ostream &os) const {
892915
SILValue value = instance.getValue();
893916
if (auto name = VariableNameInferrer::inferName(value)) {
894917
os << "'" << *name << "'-isolated";
895-
if (unsafeNonIsolated) {
896-
os << ": nonisolated(unsafe)";
897-
}
918+
printOptions();
898919
os << "\n";
899920
os << "instance: " << *value;
900921

@@ -904,9 +925,7 @@ void SILIsolationInfo::print(llvm::raw_ostream &os) const {
904925
}
905926
case ActorInstance::Kind::ActorAccessorInit:
906927
os << "'self'-isolated";
907-
if (unsafeNonIsolated) {
908-
os << ": nonisolated(unsafe)";
909-
}
928+
printOptions();
910929
os << '\n';
911930
os << "instance: actor accessor init\n";
912931
return;
@@ -916,23 +935,17 @@ void SILIsolationInfo::print(llvm::raw_ostream &os) const {
916935
if (getActorIsolation().getKind() == ActorIsolation::ActorInstance) {
917936
if (auto *vd = getActorIsolation().getActorInstance()) {
918937
os << "'" << vd->getBaseIdentifier() << "'-isolated";
919-
if (unsafeNonIsolated) {
920-
os << ": nonisolated(unsafe)";
921-
}
938+
printOptions();
922939
return;
923940
}
924941
}
925942

926943
getActorIsolation().printForDiagnostics(os);
927-
if (unsafeNonIsolated) {
928-
os << ": nonisolated(unsafe)";
929-
}
944+
printOptions();
930945
return;
931946
case Task:
932947
os << "task-isolated";
933-
if (unsafeNonIsolated) {
934-
os << ": nonisolated(unsafe)";
935-
}
948+
printOptions();
936949
os << '\n';
937950
os << "instance: " << *getIsolatedValue();
938951
return;
@@ -959,8 +972,13 @@ bool SILIsolationInfo::hasSameIsolation(const SILIsolationInfo &other) const {
959972
ActorInstance actor1 = getActorInstance();
960973
ActorInstance actor2 = other.getActorInstance();
961974

962-
// If either are non-null, and the actor instance doesn't match, return
963-
// false.
975+
// If either have an actor instance, and the actor instance doesn't match,
976+
// return false.
977+
//
978+
// This ensures that cases like comparing two global actor isolated things
979+
// do not hit this path.
980+
//
981+
// It also catches cases where we have a missing actor instance.
964982
if ((actor1 || actor2) && actor1 != actor2)
965983
return false;
966984

@@ -1054,7 +1072,7 @@ void SILIsolationInfo::printForOneLineLogging(llvm::raw_ostream &os) const {
10541072
return;
10551073
case Disconnected:
10561074
os << "disconnected";
1057-
if (unsafeNonIsolated) {
1075+
if (getOptions().contains(Flag::UnsafeNonIsolated)) {
10581076
os << ": nonisolated(unsafe)";
10591077
}
10601078
return;
@@ -1143,14 +1161,31 @@ std::optional<SILDynamicMergedIsolationInfo>
11431161
SILDynamicMergedIsolationInfo::merge(SILIsolationInfo other) const {
11441162
// If we are greater than the other kind, then we are further along the
11451163
// lattice. We ignore the change.
1146-
if (unsigned(other.getKind()) < unsigned(innerInfo.getKind()))
1164+
if (unsigned(innerInfo.getKind() > unsigned(other.getKind())))
11471165
return {*this};
11481166

1149-
// If we are both actor isolated and our isolations are not
1150-
// compatible... return None.
1151-
if (other.isActorIsolated() && innerInfo.isActorIsolated() &&
1152-
!innerInfo.hasSameIsolation(other))
1167+
// If we are both actor isolated...
1168+
if (innerInfo.isActorIsolated() && other.isActorIsolated()) {
1169+
// If both innerInfo and other have the same isolation, we are obviously
1170+
// done. Just return innerInfo since we could return either.
1171+
if (innerInfo.hasSameIsolation(other))
1172+
return {innerInfo};
1173+
1174+
// Ok, there is some difference in between innerInfo and other. Lets see if
1175+
// they are both actor instance isolated and if either are unapplied
1176+
// isolated any parameter. In such a case, take the one that is further
1177+
// along.
1178+
if (innerInfo.getActorIsolation().isActorInstanceIsolated() &&
1179+
other.getActorIsolation().isActorInstanceIsolated()) {
1180+
if (innerInfo.isUnappliedIsolatedAnyParameter())
1181+
return other;
1182+
if (other.isUnappliedIsolatedAnyParameter())
1183+
return innerInfo;
1184+
}
1185+
1186+
// Otherwise, they do not match... so return None to signal merge failure.
11531187
return {};
1188+
}
11541189

11551190
// If we are both disconnected and other has the unsafeNonIsolated bit set,
11561191
// drop that bit and return that.
@@ -1159,11 +1194,11 @@ SILDynamicMergedIsolationInfo::merge(SILIsolationInfo other) const {
11591194
// merging. These bits should not propagate through merging and should instead
11601195
// always be associated with non-merged infos.
11611196
if (other.isDisconnected() && other.isUnsafeNonIsolated()) {
1162-
return SILDynamicMergedIsolationInfo(other.withUnsafeNonIsolated(false));
1197+
return {other.withUnsafeNonIsolated(false)};
11631198
}
11641199

11651200
// Otherwise, just return other.
1166-
return SILDynamicMergedIsolationInfo(other);
1201+
return {other};
11671202
}
11681203

11691204
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)