Skip to content

Commit af8115f

Browse files
committed
Hop to the current isolation properly in delegating async actor initializers.
This requires two major changes. The first is that we need to teach SILGen that the isolation of an initializer is essentially dynamic (as far as SILGen is concerned) --- that it needs to emit code in order to get the isolation reference. To make this work, I needed to refactor how we store the expected executor of a function so that it's not always a constant value; instead, we'll need to emit code that DI will lower properly. Fortunately, I can largely build on top of the work that Doug previously did to support #isolation in these functions. The SIL we emit here around delegating initializer calls is not ideal --- the breadcrumb hop ends up jumping to the generic executor, and then DI actually emits the hop to the actor. This is a little silly, but it's hard to eliminate without special-casing the self-rebinding, which honestly we should consider rather than the weirdly global handling of that in SILGen today. The optimizer should eliminate this hop pretty reliably, at least. The second is that we need to teach DI to handle the pattern of code we get in delegating initializers, where the builtin actually has to be passed the self var rather than a class reference. This is because we don't *have* a class reference that's consistently correct in these cases. This ended up being a fairly straightforward generalization. I also taught the hop_to_executor optimizer to skip over the initialization of the default-actor header; there are a lot of simple cases where we still do emit the prologue generic-executor hop, but at least the most trivial case is handled. To do this better, we'd need to teach this bit of the optimizer that the properties of self can be stored to in an initializer prior to the object having escaped, and we don't have that information easily at hand, I think. Fixes rdar://87485045.
1 parent 96143db commit af8115f

18 files changed

+524
-226
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ ERROR(exclusivity_access_required_unknown_decl_moveonly,none,
111111
NOTE(exclusivity_conflicting_access,none,
112112
"conflicting access is here", ())
113113

114+
ERROR(unsupported_sil_builtin,none,
115+
"builtin '%0' is not supported by SIL generation", (StringRef))
116+
114117
ERROR(unsupported_c_function_pointer_conversion,none,
115118
"C function pointer signature %0 is not compatible with expected type %1",
116119
(Type, Type))

include/swift/SIL/AddressWalker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) && {
276276
case BuiltinValueKind::GetEnumTag:
277277
case BuiltinValueKind::InjectEnumTag:
278278
case BuiltinValueKind::AddressOfRawLayout:
279+
case BuiltinValueKind::FlowSensitiveSelfIsolation:
280+
case BuiltinValueKind::FlowSensitiveDistributedSelfIsolation:
279281
callVisitUse(op);
280282
continue;
281283
default:

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -914,14 +914,33 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, EndAsyncLetLifetime)
914914
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroup)
915915
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, CreateTaskGroupWithFlags)
916916
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, DestroyTaskGroup)
917-
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveSelfIsolation)
918-
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, FlowSensitiveDistributedSelfIsolation)
919917

920918
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, COWBufferForReading)
921919

922920
// This should actually never be seen in SIL
923921
BUILTIN_OPERAND_OWNERSHIP(GuaranteedForwarding, ExtractFunctionIsolation)
924922

923+
static OperandOwnership
924+
visitAnyFlowSensitiveSelfIsolation(BuiltinInst *bi) {
925+
// In potentially-delegating initializers, the operand will be the
926+
// address of the box instead of the actor instance.
927+
auto operand = bi->getOperand(0);
928+
if (operand->getType().isAddress())
929+
return OperandOwnership::TrivialUse;
930+
return OperandOwnership::InstantaneousUse;
931+
}
932+
933+
OperandOwnership
934+
OperandOwnershipBuiltinClassifier
935+
::visitFlowSensitiveSelfIsolation(BuiltinInst *bi, StringRef attr) {
936+
return visitAnyFlowSensitiveSelfIsolation(bi);
937+
}
938+
OperandOwnership
939+
OperandOwnershipBuiltinClassifier
940+
::visitFlowSensitiveDistributedSelfIsolation(BuiltinInst *bi, StringRef attr) {
941+
return visitAnyFlowSensitiveSelfIsolation(bi);
942+
}
943+
925944
OperandOwnership
926945
OperandOwnershipBuiltinClassifier
927946
::visitStartAsyncLetWithLocalBuffer(BuiltinInst *bi, StringRef attr) {

lib/SILGen/SILGenApply.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5709,8 +5709,10 @@ RValue SILGenFunction::emitApply(
57095709
}
57105710

57115711
breadcrumb = emitHopToTargetExecutor(loc, executor);
5712-
} else if (ExpectedExecutor &&
5713-
(substFnType->isAsync() || calleeTypeInfo.foreign.async)) {
5712+
} else if ((substFnType->isAsync() || calleeTypeInfo.foreign.async) &&
5713+
ExpectedExecutor.isNecessary()) {
5714+
assert(F.isAsync());
5715+
57145716
// Otherwise, if we're in an actor method ourselves, and we're calling into
57155717
// any sort of async function, we'll want to make sure to hop back to our
57165718
// own executor afterward, since the callee could have made arbitrary hops

lib/SILGen/SILGenBridging.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2090,6 +2090,11 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
20902090
if (nativeFnTy->isAsync()) {
20912091
foreignAsync = fd->getForeignAsyncConvention();
20922092
assert(foreignAsync && "couldn't find foreign async convention?!");
2093+
2094+
// We might switch to to the callee's actor as part of making the call,
2095+
// but we don't need to switch back afterwards because we're going to
2096+
// immediately return.
2097+
ExpectedExecutor.setUnnecessary();
20932098
}
20942099
std::optional<ForeignErrorConvention> foreignError;
20952100
if (nativeFnTy->hasErrorResult()) {

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,8 +1493,16 @@ static ManagedValue emitBuiltinEndAsyncLet(
14931493
static ManagedValue emitBuiltinGetCurrentExecutor(
14941494
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
14951495
PreparedArguments &&preparedArgs, SGFContext C) {
1496-
return ManagedValue::forObjectRValueWithoutOwnership(
1497-
SGF.emitGetCurrentExecutor(loc));
1496+
1497+
// We don't support this builtin anymore in SILGen.
1498+
// TODO: just remove it?
1499+
SGF.SGM.diagnose(loc, diag::unsupported_sil_builtin,
1500+
getBuiltinName(BuiltinValueKind::GetCurrentExecutor));
1501+
1502+
auto &ctx = SGF.getASTContext();
1503+
auto executorType = SILType::getPrimitiveObjectType(ctx.TheExecutorType);
1504+
auto optionalExecutorType = SILType::getOptionalType(executorType);
1505+
return SGF.emitUndef(optionalExecutorType);
14981506
}
14991507

15001508
// Emit SIL for sizeof/strideof/alignof.

0 commit comments

Comments
 (0)