Skip to content

Commit d8b2631

Browse files
committed
[region-isolation] Make fields of global actor guarded types that are non-Sendable be considered as actor isolated.
rdar://123488540
1 parent f63ac2e commit d8b2631

File tree

4 files changed

+238
-62
lines changed

4 files changed

+238
-62
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 117 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,27 @@ static bool isIsolationBoundaryCrossingApply(SILInstruction *inst) {
6969

7070
namespace {
7171

72+
struct UnderlyingTrackedValueInfo {
73+
SILValue value;
74+
75+
/// Only used for addresses.
76+
bool isActorIsolated;
77+
78+
explicit UnderlyingTrackedValueInfo(SILValue value)
79+
: value(value), isActorIsolated(false) {}
80+
81+
UnderlyingTrackedValueInfo(SILValue value, bool isActorIsolated)
82+
: value(value), isActorIsolated(isActorIsolated) {}
83+
};
84+
7285
struct UseDefChainVisitor
7386
: public AccessUseDefChainVisitor<UseDefChainVisitor, SILValue> {
7487
bool isMerge = false;
7588

89+
/// The actor isolation that we found while walking from use->def. Always set
90+
/// to the first one encountered.
91+
std::optional<ActorIsolation> actorIsolation;
92+
7693
SILValue visitAll(SILValue sourceAddr) {
7794
SILValue result = visit(sourceAddr);
7895
if (!result)
@@ -142,9 +159,21 @@ struct UseDefChainVisitor
142159
// Index is always a merge.
143160
isMerge = true;
144161
break;
145-
case ProjectionKind::Enum:
146-
// Enum is never a merge since it always has a single field.
162+
case ProjectionKind::Enum: {
163+
// Enum is never a merge since it always has a single tuple field... but
164+
// it can be actor isolated.
165+
if (!bool(actorIsolation)) {
166+
auto *uedi = cast<UncheckedTakeEnumDataAddrInst>(inst);
167+
auto i = getActorIsolation(uedi->getEnumDecl());
168+
// If our operand decl is actor isolated, then we want to stop looking
169+
// through since it is Sendable.
170+
if (i.isActorIsolated()) {
171+
actorIsolation = i;
172+
return SILValue();
173+
}
174+
}
147175
break;
176+
}
148177
case ProjectionKind::Tuple: {
149178
// These are merges if we have multiple fields.
150179
auto *tti = cast<TupleElementAddrInst>(inst);
@@ -163,6 +192,17 @@ struct UseDefChainVisitor
163192
case ProjectionKind::Struct:
164193
auto *sea = cast<StructElementAddrInst>(inst);
165194

195+
// See if our type is actor isolated.
196+
if (!bool(actorIsolation)) {
197+
auto i = getActorIsolation(sea->getStructDecl());
198+
// If our parent type is actor isolated then we do not want to keep on
199+
// walking up from use->def since the value is considered Sendable.
200+
if (i.isActorIsolated()) {
201+
actorIsolation = i;
202+
return SILValue();
203+
}
204+
}
205+
166206
// See if our result type is a sendable type. In such a case, we do not
167207
// want to look through the struct_element_addr since we do not want to
168208
// identify the sendable type with the non-sendable operand. These we
@@ -248,8 +288,6 @@ static bool isLookThroughIfResultNonSendable(SILInstruction *inst) {
248288
switch (inst->getKind()) {
249289
default:
250290
return false;
251-
case SILInstructionKind::TupleElementAddrInst:
252-
case SILInstructionKind::StructElementAddrInst:
253291
case SILInstructionKind::RawPointerToRefInst:
254292
return true;
255293
}
@@ -264,13 +302,16 @@ static bool isLookThroughIfOperandNonSendable(SILInstruction *inst) {
264302
}
265303
}
266304

267-
static bool isLookThroughIfOperandAndResultSendable(SILInstruction *inst) {
305+
static bool isLookThroughIfOperandAndResultNonSendable(SILInstruction *inst) {
268306
switch (inst->getKind()) {
269307
default:
270308
return false;
271309
case SILInstructionKind::UncheckedTrivialBitCastInst:
272310
case SILInstructionKind::UncheckedBitwiseCastInst:
273311
case SILInstructionKind::UncheckedValueCastInst:
312+
case SILInstructionKind::StructElementAddrInst:
313+
case SILInstructionKind::TupleElementAddrInst:
314+
case SILInstructionKind::UncheckedTakeEnumDataAddrInst:
274315
return true;
275316
}
276317
}
@@ -293,7 +334,7 @@ static SILValue getUnderlyingTrackedObjectValue(SILValue value) {
293334

294335
// If we have a cast and our operand and result are non-Sendable, treat it
295336
// as a look through.
296-
if (isLookThroughIfOperandAndResultSendable(svi)) {
337+
if (isLookThroughIfOperandAndResultNonSendable(svi)) {
297338
if (isNonSendableType(svi->getType(), fn) &&
298339
isNonSendableType(svi->getOperand(0)->getType(), fn)) {
299340
temp = svi->getOperand(0);
@@ -329,21 +370,21 @@ static SILValue getUnderlyingTrackedObjectValue(SILValue value) {
329370
}
330371
}
331372

332-
static SILValue getUnderlyingTrackedValue(SILValue value) {
373+
static UnderlyingTrackedValueInfo getUnderlyingTrackedValue(SILValue value) {
333374
if (!value->getType().isAddress()) {
334-
return getUnderlyingTrackedObjectValue(value);
375+
return UnderlyingTrackedValueInfo(getUnderlyingTrackedObjectValue(value));
335376
}
336377

337378
UseDefChainVisitor visitor;
338379
SILValue base = visitor.visitAll(value);
339380
assert(base);
340381
if (base->getType().isObject())
341-
return getUnderlyingObject(base);
342-
return base;
382+
return {getUnderlyingObject(base), visitor.actorIsolation.has_value()};
383+
return {base, visitor.actorIsolation.has_value()};
343384
}
344385

345386
SILValue RegionAnalysisFunctionInfo::getUnderlyingTrackedValue(SILValue value) {
346-
return ::getUnderlyingTrackedValue(value);
387+
return ::getUnderlyingTrackedValue(value).value;
347388
}
348389

349390
namespace {
@@ -650,7 +691,7 @@ struct PartialApplyReachabilityDataflow {
650691

651692
private:
652693
SILValue getRootValue(SILValue value) const {
653-
return getUnderlyingTrackedValue(value);
694+
return getUnderlyingTrackedValue(value).value;
654695
}
655696

656697
unsigned getBitForValue(SILValue value) const {
@@ -2150,7 +2191,7 @@ class PartitionOpTranslator {
21502191
assert((isStaticallyLookThroughInst(inst) ||
21512192
isLookThroughIfResultNonSendable(inst) ||
21522193
isLookThroughIfOperandNonSendable(inst) ||
2153-
isLookThroughIfOperandAndResultSendable(inst)) &&
2194+
isLookThroughIfOperandAndResultNonSendable(inst)) &&
21542195
"Out of sync... should return true for one of these categories!");
21552196
return translateSILLookThrough(inst->getResults(), inst->getOperand(0));
21562197

@@ -2379,7 +2420,6 @@ CONSTANT_TRANSLATION(EndInitLetRefInst, LookThrough)
23792420
CONSTANT_TRANSLATION(InitEnumDataAddrInst, LookThrough)
23802421
CONSTANT_TRANSLATION(OpenExistentialAddrInst, LookThrough)
23812422
CONSTANT_TRANSLATION(UncheckedRefCastInst, LookThrough)
2382-
CONSTANT_TRANSLATION(UncheckedTakeEnumDataAddrInst, LookThrough)
23832423
CONSTANT_TRANSLATION(UpcastInst, LookThrough)
23842424
CONSTANT_TRANSLATION(MoveValueInst, LookThrough)
23852425
CONSTANT_TRANSLATION(MarkUnresolvedNonCopyableValueInst, LookThrough)
@@ -2598,39 +2638,6 @@ CONSTANT_TRANSLATION(VectorInst, Asserting)
25982638

25992639
#undef CONSTANT_TRANSLATION
26002640

2601-
#ifdef LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE
2602-
#error "LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE already defined?!"
2603-
#endif
2604-
2605-
// If our result is non-Sendable, treat this as a lookthrough.
2606-
// Otherwise, we are extracting a sendable field from a non-Sendable base
2607-
// type. We need to track this as an assignment so that if we transferred
2608-
//
2609-
// the value we emit an error. Since we do not track uses of Sendable
2610-
// values this is the best place to emit the error since we do not look
2611-
// further to find the actual use site.
2612-
//
2613-
// TODO: We could do a better job here and attempt to find the actual
2614-
// use
2615-
// of the Sendable addr. That would require adding more logic though.
2616-
#define LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE(INST) \
2617-
TranslationSemantics PartitionOpTranslator::visit##INST(INST *inst) { \
2618-
assert(isLookThroughIfResultNonSendable(inst) && "Out of sync?!"); \
2619-
if (isNonSendableType(inst->getType())) { \
2620-
return TranslationSemantics::LookThrough; \
2621-
} \
2622-
return TranslationSemantics::Require; \
2623-
}
2624-
2625-
LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE(TupleElementAddrInst)
2626-
LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE(StructElementAddrInst)
2627-
2628-
#undef LOOKTHROUGH_IF_NONSENDABLE_RESULT_REQUIRE_OTHERWISE
2629-
2630-
#ifdef IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE
2631-
#error IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE already defined
2632-
#endif
2633-
26342641
#define IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE(INST) \
26352642
TranslationSemantics PartitionOpTranslator::visit##INST(INST *inst) { \
26362643
if (!isNonSendableType(inst->getType())) { \
@@ -2644,14 +2651,14 @@ IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE(StructExtractInst)
26442651

26452652
#undef IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE
26462653

2647-
#ifdef CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT
2648-
#error "CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT already defined"
2654+
#ifdef LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND
2655+
#error "LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND already defined"
26492656
#endif
26502657

2651-
#define CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT(INST) \
2658+
#define LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(INST) \
26522659
\
26532660
TranslationSemantics PartitionOpTranslator::visit##INST(INST *cast) { \
2654-
assert(isLookThroughIfOperandAndResultSendable(cast) && "Out of sync"); \
2661+
assert(isLookThroughIfOperandAndResultNonSendable(cast) && "Out of sync"); \
26552662
bool isOperandNonSendable = \
26562663
isNonSendableType(cast->getOperand()->getType()); \
26572664
bool isResultNonSendable = isNonSendableType(cast->getType()); \
@@ -2670,11 +2677,14 @@ IGNORE_IF_SENDABLE_RESULT_ASSIGN_OTHERWISE(StructExtractInst)
26702677
return TranslationSemantics::Ignored; \
26712678
}
26722679

2673-
CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT(UncheckedTrivialBitCastInst)
2674-
CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT(UncheckedBitwiseCastInst)
2675-
CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT(UncheckedValueCastInst)
2680+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTrivialBitCastInst)
2681+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedBitwiseCastInst)
2682+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedValueCastInst)
2683+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(TupleElementAddrInst)
2684+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(StructElementAddrInst)
2685+
LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTakeEnumDataAddrInst)
26762686

2677-
#undef CAST_WITH_MAYBE_SENDABLE_NONSENDABLE_OP_AND_RESULT
2687+
#undef LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND
26782688

26792689
//===---
26802690
// Custom Handling
@@ -3146,7 +3156,8 @@ bool RegionAnalysisValueMap::isActorDerived(Element trackableValueID) const {
31463156
/// may alias.
31473157
TrackableValue RegionAnalysisValueMap::getTrackableValue(
31483158
SILValue value, bool isAddressCapturedByPartialApply) const {
3149-
value = getUnderlyingTrackedValue(value);
3159+
auto info = getUnderlyingTrackedValue(value);
3160+
value = info.value;
31503161

31513162
auto *self = const_cast<RegionAnalysisValueMap *>(this);
31523163
auto iter = self->equivalenceClassValuesToState.try_emplace(
@@ -3163,6 +3174,12 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
31633174

31643175
// First for addresses.
31653176
if (value->getType().isAddress()) {
3177+
// If we were able to find this was actor isolated from finding our
3178+
// underlying object, use that. It is never wrong.
3179+
if (info.isActorIsolated) {
3180+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3181+
}
3182+
31663183
auto storage = AccessStorageWithBase::compute(value);
31673184
if (storage.storage) {
31683185
// Check if we have a uniquely identified address that was not captured
@@ -3214,10 +3231,50 @@ TrackableValue RegionAnalysisValueMap::getTrackableValue(
32143231
// mark this value as actor derived.
32153232
if (isa<LoadInst, LoadBorrowInst>(iter.first->first.getValue())) {
32163233
auto *svi = cast<SingleValueInstruction>(iter.first->first.getValue());
3234+
3235+
// See if we can use get underlying tracked value to find if it is actor
3236+
// isolated.
3237+
//
3238+
// TODO: Instead of using AccessStorageBase, just use our own visitor
3239+
// everywhere. Just haven't done it due to possible perturbations.
3240+
auto parentAddrInfo = getUnderlyingTrackedValue(svi);
3241+
if (parentAddrInfo.isActorIsolated)
3242+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3243+
32173244
auto storage = AccessStorageWithBase::compute(svi->getOperand(0));
3218-
if (storage.storage && isa<RefElementAddrInst>(storage.base)) {
3219-
if (storage.storage.getRoot()->getType().isActor()) {
3220-
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3245+
if (storage.storage) {
3246+
if (isa<RefElementAddrInst>(storage.base)) {
3247+
if (storage.storage.getRoot()->getType().isActor()) {
3248+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3249+
}
3250+
}
3251+
}
3252+
}
3253+
3254+
// See if we have a struct_extract from a global actor isolated type.
3255+
if (auto *sei = dyn_cast<StructExtractInst>(iter.first->first.getValue())) {
3256+
if (getActorIsolation(sei->getStructDecl()).isActorIsolated()) {
3257+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3258+
}
3259+
}
3260+
3261+
// See if we have an unchecked_enum_data from a global actor isolated type.
3262+
if (auto *uedi =
3263+
dyn_cast<UncheckedEnumDataInst>(iter.first->first.getValue())) {
3264+
if (getActorIsolation(uedi->getEnumDecl()).isActorIsolated()) {
3265+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3266+
}
3267+
}
3268+
3269+
// Handle a switch_enum from a global actor isolated type.
3270+
if (auto *arg = dyn_cast<SILPhiArgument>(iter.first->first.getValue())) {
3271+
if (auto *singleTerm = arg->getSingleTerminator()) {
3272+
if (auto *sei = dyn_cast<SwitchEnumInst>(singleTerm)) {
3273+
auto enumDecl =
3274+
sei->getOperand()->getType().getEnumOrBoundGenericEnum();
3275+
if (getActorIsolation(enumDecl).isActorIsolated()) {
3276+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
3277+
}
32213278
}
32223279
}
32233280
}
@@ -3266,7 +3323,7 @@ TrackableValue RegionAnalysisValueMap::getActorIntroducingRepresentative(
32663323
}
32673324

32683325
bool RegionAnalysisValueMap::markValueAsActorDerived(SILValue value) {
3269-
value = getUnderlyingTrackedValue(value);
3326+
value = getUnderlyingTrackedValue(value).value;
32703327
auto iter = equivalenceClassValuesToState.find(value);
32713328
if (iter == equivalenceClassValuesToState.end())
32723329
return false;

0 commit comments

Comments
 (0)