Skip to content

Commit e54fa6c

Browse files
committed
inject actorReady calls for async dist actor ctors
Immediately after the hop_to_executor in an async, distributed actor init, we need to notify the transport that the actor is ready. This patch does not yet account for more complex cases. In particular, we will need a mechanism to prevent multiple calls to actorReady, which can happen if a loop appears in the init: distributed actor Dactor { var x: Int init(tport: ActorTransport, count: Int) async { var i = count repeat { self.x = count // hop is injected here i -= 1 } while i > 0 } }
1 parent 51b7697 commit e54fa6c

File tree

4 files changed

+59
-70
lines changed

4 files changed

+59
-70
lines changed

include/swift/SILOptimizer/Utils/DistributedActor.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,17 @@ class SILFunction;
2424
class SILLocation;
2525
class SILValue;
2626

27-
/// Perform a load of the given distributed actor's transport.
28-
/// \param actorSelf the value representing `self` for the distributed actor
29-
/// instance. \returns the transport value
30-
SILValue loadActorTransport(SILBuilder &B, SILLocation loc,
31-
ClassDecl *actorDecl, SILValue actorSelf);
27+
/// Finds the first `ActorTransport`-compatible parameter of the given function.
28+
/// \returns nullptr if the function does not have such a parameter.
29+
SILArgument *findFirstActorTransportArg(SILFunction &F);
3230

3331
/// Emits code that notifies the distributed actor's transport that the
3432
/// actor is ready for execution.
3533
/// \param B the builder to use when emitting the code.
3634
/// \param actor the distributed actor instance to pass to the transport as
3735
/// being "ready" \param transport a value representing the ActorTransport
38-
void emitActorReadyCall(SILBuilder &B, SILLocation loc, ClassDecl *actorDecl,
39-
SILValue actor, SILValue transport);
36+
void emitActorReadyCall(SILBuilder &B, SILLocation loc, SILValue actor,
37+
SILValue transport);
4038

4139
} // namespace swift
4240

lib/SILGen/SILGenDistributed.cpp

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -100,39 +100,6 @@ static void emitDistributedIfRemoteBranch(SILGenFunction &SGF,
100100

101101
// MARK: local instance initialization
102102

103-
/// Finds the first `ActorTransport`-compatible parameter of the given function.
104-
/// Crashes if the given function does not have such a parameter.
105-
static SILArgument *findFirstActorTransportArg(SILFunction &F) {
106-
auto *module = F.getModule().getSwiftModule();
107-
auto &C = F.getASTContext();
108-
109-
auto *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
110-
Type transportTy = transportProto->getDeclaredInterfaceType();
111-
112-
for (auto arg : F.getArguments()) {
113-
// TODO(distributed): also be able to locate a generic transport
114-
Type argTy = arg->getType().getASTType();
115-
auto argDecl = arg->getDecl();
116-
117-
auto conformsToTransport =
118-
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
119-
120-
// Is it a protocol that conforms to ActorTransport?
121-
if (argTy->isEqual(transportTy) || conformsToTransport) {
122-
return arg;
123-
}
124-
125-
// Is it some specific ActorTransport?
126-
auto result = module->lookupConformance(argTy, transportProto);
127-
if (!result.isInvalid()) {
128-
return arg;
129-
}
130-
}
131-
132-
// did not find argument of ActorTransport type!
133-
llvm_unreachable("Missing required ActorTransport argument!");
134-
}
135-
136103
/// For the initialization of a local distributed actor instance, emits code to initialize the instance's
137104
/// stored property corresponding to the transport.
138105
static void emitTransportInit(SILGenFunction &SGF,
@@ -200,8 +167,7 @@ static void emitIdentityInit(SILGenFunction &SGF, ConstructorDecl *ctor,
200167
assert(distributedActorProto);
201168
assert(transportProto);
202169

203-
SILValue transportValue =
204-
loadActorTransport(B, loc, classDecl, borrowedSelfArg.getValue());
170+
SILValue transportValue = findFirstActorTransportArg(F);
205171

206172
// --- Open the transport existential, if needed.
207173
auto transportASTType = transportValue->getType().getASTType();
@@ -296,15 +262,12 @@ void SILGenFunction::emitDistributedActorReady(
296262
// Only designated initializers get the lifecycle handling injected
297263
assert(ctor->isDesignatedInit());
298264

299-
auto *dc = ctor->getDeclContext();
300-
auto classDecl = dc->getSelfClassDecl();
301-
302265
SILValue transport = findFirstActorTransportArg(F);
303266

304267
FullExpr scope(Cleanups, CleanupLocation(loc));
305268
auto borrowedSelf = actorSelf.borrow(*this, loc);
306269

307-
emitActorReadyCall(B, loc, classDecl, borrowedSelf.getValue(), transport);
270+
emitActorReadyCall(B, loc, borrowedSelf.getValue(), transport);
308271
}
309272

310273
// MARK: remote instance initialization

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/SILOptimizer/PassManager/Passes.h"
2828
#include "swift/SILOptimizer/PassManager/Transforms.h"
2929
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
30+
#include "swift/SILOptimizer/Utils/DistributedActor.h"
3031
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3132
#include "llvm/ADT/STLExtras.h"
3233
#include "llvm/ADT/SmallBitVector.h"
@@ -797,6 +798,16 @@ void LifetimeChecker::diagnoseInitError(const DIMemoryUse &Use,
797798
diagnose(Module, TheMemory.getLoc(), diag::variable_defined_here, isLet);
798799
}
799800

801+
/// Determines whether the given function is a constructor that belogs to a
802+
/// distributed actor declaration.
803+
static bool isDistributedActorCtor(SILFunction &F) {
804+
auto *context = F.getDeclContext();
805+
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(context->getAsDecl()))
806+
if (auto *cls = dyn_cast<ClassDecl>(ctor->getDeclContext()->getAsDecl()))
807+
return cls->isDistributedActor();
808+
return false;
809+
}
810+
800811
/// Injects a hop_to_executor instruction after the specified insertion point.
801812
static void injectHopToExecutorAfter(SILLocation loc,
802813
SILBasicBlock::iterator insertPt,
@@ -820,6 +831,13 @@ static void injectHopToExecutorAfter(SILLocation loc,
820831

821832
b.createHopToExecutor(loc.asAutoGenerated(), actor, /*mandatory=*/false);
822833

834+
// Distributed actors also need to notify their transport immediately
835+
// after performing the hop.
836+
if (isDistributedActorCtor(b.getFunction())) {
837+
auto transport = findFirstActorTransportArg(b.getFunction());
838+
emitActorReadyCall(b, loc.asAutoGenerated(), actor, transport);
839+
}
840+
823841
if (needsBorrow)
824842
b.createEndBorrow(loc, actor);
825843
});

lib/SILOptimizer/Utils/DistributedActor.cpp

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,46 @@
1818

1919
namespace swift {
2020

21-
// MARK: utilities
22-
23-
SILValue loadActorTransport(SILBuilder &B, SILLocation loc,
24-
ClassDecl *actorDecl, SILValue actorSelf) {
25-
assert(actorDecl->isDistributedActor());
21+
SILArgument *findFirstActorTransportArg(SILFunction &F) {
22+
auto *module = F.getModule().getSwiftModule();
23+
auto &C = F.getASTContext();
24+
25+
auto *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
26+
Type transportTy = transportProto->getDeclaredInterfaceType();
27+
28+
for (auto arg : F.getArguments()) {
29+
// TODO(distributed): also be able to locate a generic transport
30+
Type argTy = arg->getType().getASTType();
31+
auto argDecl = arg->getDecl();
32+
33+
auto conformsToTransport =
34+
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
35+
36+
// Is it a protocol that conforms to ActorTransport?
37+
if (argTy->isEqual(transportTy) || conformsToTransport) {
38+
return arg;
39+
}
40+
41+
// Is it some specific ActorTransport?
42+
auto result = module->lookupConformance(argTy, transportProto);
43+
if (!result.isInvalid()) {
44+
return arg;
45+
}
46+
}
2647

27-
// get the VarDecl corresponding to the transport
28-
auto &C = actorDecl->getASTContext();
29-
auto refs = actorDecl->lookupDirect(C.Id_actorTransport);
30-
assert(refs.size() == 1);
31-
VarDecl *prop = dyn_cast<VarDecl>(refs.front());
48+
#ifndef NDEBUG
49+
llvm_unreachable("Missing required ActorTransport argument!");
50+
#endif
3251

33-
// form a reference and load it
34-
auto &F = B.getFunction();
35-
auto fieldAddr = B.createRefElementAddr(
36-
loc, actorSelf, prop, F.getLoweredType(prop->getInterfaceType()));
37-
return B.createLoad(loc, fieldAddr, LoadOwnershipQualifier::Copy);
52+
return nullptr;
3853
}
3954

40-
// MARK: exposed interface
41-
42-
void emitActorReadyCall(SILBuilder &B, SILLocation loc, ClassDecl *actorDecl,
43-
SILValue actor, SILValue transport) {
55+
void emitActorReadyCall(SILBuilder &B, SILLocation loc, SILValue actor,
56+
SILValue transport) {
4457

4558
auto &F = B.getFunction();
4659
auto &M = B.getModule();
47-
auto &C = actorDecl->getASTContext();
60+
auto &C = F.getASTContext();
4861

4962
ProtocolDecl *actorProto = C.getProtocol(KnownProtocolKind::DistributedActor);
5063
ProtocolDecl *transProto = C.getProtocol(KnownProtocolKind::ActorTransport);
@@ -69,10 +82,7 @@ void emitActorReadyCall(SILBuilder &B, SILLocation loc, ClassDecl *actorDecl,
6982
assert(!transportConfRef.isInvalid() &&
7083
"Missing conformance to `ActorTransport`");
7184

72-
auto *selfTyDecl = actorDecl->getSelfNominalTypeDecl();
73-
auto selfTy = F.mapTypeIntoContext(
74-
selfTyDecl->getDeclaredInterfaceType()); // TODO: thats just self var devl
75-
// getType
85+
Type selfTy = F.mapTypeIntoContext(actor->getType().getASTType());
7686

7787
// Note: it does not matter on what module we perform the lookup,
7888
// it is currently ignored. So the Stdlib module is good enough.

0 commit comments

Comments
 (0)