Skip to content

Commit af683dc

Browse files
committed
basic implementation of performing an init of the id after an assign of the actorSystem.
The resignID call within the initializer moved into DI, because an assignment to the actorSystem, and thus initialization of the id, is no longer guaranteed to happen. Thus, while we previously could model the resignation as a clean-up emitted on all unwind paths in an initializer, now it's conditional, based on whether the id was initialized. This is exactly what DI is designed to do, so we inject the resignation call just before we call the identity's deinit.
1 parent 13cbe0d commit af683dc

25 files changed

+290
-117
lines changed

include/swift/SILOptimizer/Utils/DistributedActor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ class SILLocation;
3232
class SILType;
3333
class SILValue;
3434

35+
/// Creates a reference to the distributed actor's \p actorSystem
36+
/// stored property.
37+
SILValue refDistributedActorSystem(SILBuilder &B,
38+
SILLocation loc,
39+
ClassDecl *actorDecl,
40+
SILValue actorInstance);
41+
3542
/// Emit a call to a witness of the DistributedActorSystem protocol.
3643
///
3744
/// \param methodName The name of the method on the DistributedActorSystem protocol.
@@ -55,6 +62,11 @@ void emitDistributedActorSystemWitnessCall(
5562
void emitActorReadyCall(SILBuilder &B, SILLocation loc, SILValue actor,
5663
SILValue actorSystem);
5764

65+
/// Emits code to notify the \p actorSystem that the given identity is resigned.
66+
void emitResignIdentityCall(SILBuilder &B, SILLocation loc, ClassDecl* actDecl,
67+
SILValue actor, SILValue identityRef);
68+
69+
5870
} // namespace swift
5971

6072
#endif
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- InitializeDistActorIdentity.h - dist actor ID init for SILGen ----===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "Cleanup.h"
14+
15+
namespace swift {
16+
17+
class AllocStackInst;
18+
19+
namespace Lowering {
20+
21+
/// A clean-up designed to emit an initialization of a distributed actor's
22+
/// identity upon successful initialization of the actor's system.
23+
struct InitializeDistActorIdentity : Cleanup {
24+
private:
25+
ConstructorDecl *ctor;
26+
ManagedValue actorSelf;
27+
VarDecl *systemVar;
28+
public:
29+
InitializeDistActorIdentity(ConstructorDecl *ctor, ManagedValue actorSelf);
30+
31+
void emit(SILGenFunction &SGF, CleanupLocation loc,
32+
ForUnwind_t forUnwind) override;
33+
34+
void dump(SILGenFunction &) const override;
35+
36+
VarDecl* getSystemVar() const { return systemVar; }
37+
};
38+
39+
} // end Lowering namespace
40+
} // end swift namespace

lib/SILGen/SILGenDistributed.cpp

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -137,24 +137,24 @@ static SILArgument *findFirstDistributedActorSystemArg(SILFunction &F) {
137137
auto *module = F.getModule().getSwiftModule();
138138
auto &C = F.getASTContext();
139139

140-
auto *transportProto = C.getProtocol(KnownProtocolKind::DistributedActorSystem);
141-
Type transportTy = transportProto->getDeclaredInterfaceType();
140+
auto *DAS = C.getDistributedActorSystemDecl();
141+
Type systemTy = DAS->getDeclaredInterfaceType();
142142

143143
for (auto arg : F.getArguments()) {
144-
// TODO(distributed): also be able to locate a generic transport
144+
// TODO(distributed): also be able to locate a generic system
145145
Type argTy = arg->getType().getASTType();
146146
auto argDecl = arg->getDecl();
147147

148-
auto conformsToTransport =
149-
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
148+
auto conformsToSystem =
149+
module->lookupConformance(argDecl->getInterfaceType(), DAS);
150150

151151
// Is it a protocol that conforms to DistributedActorSystem?
152-
if (argTy->isEqual(transportTy) || conformsToTransport) {
152+
if (argTy->isEqual(systemTy) || conformsToSystem) {
153153
return arg;
154154
}
155155

156156
// Is it some specific DistributedActorSystem?
157-
auto result = module->lookupConformance(argTy, transportProto);
157+
auto result = module->lookupConformance(argTy, DAS);
158158
if (!result.isInvalid()) {
159159
return arg;
160160
}
@@ -172,7 +172,8 @@ static SILArgument *findFirstDistributedActorSystemArg(SILFunction &F) {
172172
static void emitActorSystemInit(SILGenFunction &SGF,
173173
ConstructorDecl *ctor,
174174
SILLocation loc,
175-
ManagedValue actorSelf) {
175+
ManagedValue actorSelf,
176+
SILValue systemValue) {
176177
assert(ctor->isImplicit() && "unexpected explicit dist actor init");
177178
assert(ctor->isDesignatedInit());
178179

@@ -183,11 +184,10 @@ static void emitActorSystemInit(SILGenFunction &SGF,
183184
// By construction, automatically generated distributed actor ctors have
184185
// exactly one ActorSystem-conforming argument to the constructor,
185186
// so we grab the first one from the params.
186-
SILValue systemArg = findFirstDistributedActorSystemArg(SGF.F);
187187
VarDecl *var = lookupProperty(classDecl, C.Id_actorSystem);
188188
assert(var);
189189

190-
initializeProperty(SGF, loc, actorSelf.getValue(), var, systemArg);
190+
initializeProperty(SGF, loc, actorSelf.getValue(), var, systemValue);
191191
}
192192

193193
/// Emits the distributed actor's identity (`id`) initialization.
@@ -196,72 +196,98 @@ static void emitActorSystemInit(SILGenFunction &SGF,
196196
/// \verbatim
197197
/// self.id = system.assignID(Self.self)
198198
/// \endverbatim
199-
static void emitIDInit(SILGenFunction &SGF, ConstructorDecl *ctor,
200-
SILLocation loc, ManagedValue borrowedSelfArg) {
201-
assert(ctor->isImplicit() && "unexpected explicit dist actor init");
199+
void SILGenFunction::emitDistActorIdentityInit(ConstructorDecl *ctor,
200+
SILLocation loc,
201+
SILValue borrowedSelfArg,
202+
SILValue actorSystem) {
202203
assert(ctor->isDesignatedInit());
203204

204205
auto &C = ctor->getASTContext();
205-
auto &B = SGF.B;
206-
auto &F = SGF.F;
207206

208207
auto *dc = ctor->getDeclContext();
209208
auto classDecl = dc->getSelfClassDecl();
210209

210+
assert(classDecl->isDistributedActor());
211+
211212
// --- prepare `Self.self` metatype
212213
auto *selfTyDecl = ctor->getParent()->getSelfNominalTypeDecl();
213214
auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType());
214-
auto selfMetatype = SGF.getLoweredType(MetatypeType::get(selfTy));
215+
auto selfMetatype = getLoweredType(MetatypeType::get(selfTy));
215216
SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype);
216217

217-
// since we're doing this only for the implicitly defined ctors, grab from arg
218-
SILValue actorSystem = findFirstDistributedActorSystemArg(SGF.F);
219-
220218
// --- create a temporary storage for the result of the call
221219
// it will be deallocated automatically as we exit this scope
222220
VarDecl *var = lookupProperty(classDecl, C.Id_id);
223-
auto resultTy = SGF.getLoweredType(
224-
F.mapTypeIntoContext(var->getInterfaceType()));
225-
auto temp = SGF.emitTemporaryAllocation(loc, resultTy);
221+
auto resultTy = getLoweredType(F.mapTypeIntoContext(var->getInterfaceType()));
222+
auto temp = emitTemporaryAllocation(loc, resultTy);
226223

227224
// --- emit the call itself.
228225
emitDistributedActorSystemWitnessCall(
229226
B, loc, C.Id_assignID,
230-
actorSystem, SGF.getLoweredType(selfTy),
227+
actorSystem, getLoweredType(selfTy),
231228
{ temp, selfMetatypeValue });
232229

233230
// --- initialize the property.
234-
initializeProperty(SGF, loc, borrowedSelfArg.getValue(), var, temp);
231+
initializeProperty(*this, loc, borrowedSelfArg, var, temp);
235232
}
236233

237-
namespace {
238-
/// Cleanup to resign the identity of a distributed actor if an abnormal exit happens.
239-
class ResignIdentity : public Cleanup {
240-
ClassDecl *actorDecl;
241-
SILValue self;
242-
public:
243-
ResignIdentity(ClassDecl *actorDecl, SILValue self)
244-
: actorDecl(actorDecl), self(self) {
245-
assert(actorDecl->isDistributedActor());
246-
}
234+
InitializeDistActorIdentity::InitializeDistActorIdentity(ConstructorDecl *ctor,
235+
ManagedValue actorSelf)
236+
: ctor(ctor),
237+
actorSelf(actorSelf) {
238+
systemVar = ctor->getDeclContext()
239+
->getSelfClassDecl()
240+
->getDistributedActorSystemProperty();
241+
assert(systemVar);
242+
}
247243

248-
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override {
249-
if (forUnwind == IsForUnwind) {
250-
l.markAutoGenerated();
251-
SGF.emitDistributedActorSystemResignIDCall(l, actorDecl,
252-
ManagedValue::forUnmanaged(self));
253-
}
244+
void InitializeDistActorIdentity::emit(SILGenFunction &SGF, CleanupLocation loc,
245+
ForUnwind_t forUnwind) {
246+
247+
// If we're unwinding, that must mean we're in the case where the
248+
// evaluating the expression being assigned to the actorSystem has
249+
// thrown an error. In that case, we cannot initialize the identity,
250+
// since there is no actorSystem.
251+
if (forUnwind == IsForUnwind)
252+
return;
253+
254+
255+
// Save the current clean-up depth
256+
auto baseDepth = SGF.getCleanupsDepth();
257+
{
258+
loc.markAutoGenerated();
259+
auto borrowedSelf = actorSelf.borrow(SGF, loc);
260+
261+
// load the actorSystem value
262+
Type formalType = SGF.F.mapTypeIntoContext(systemVar->getInterfaceType());
263+
SILType loweredType = SGF.getLoweredType(formalType).getAddressType();
264+
auto ref =
265+
SGF.B.createRefElementAddr(loc, borrowedSelf, systemVar, loweredType);
266+
267+
SGFContext ctx;
268+
auto systemVal =
269+
SGF.emitLoad(loc, ref.getValue(),
270+
SGF.getTypeLowering(loweredType), ctx, IsNotTake);
271+
272+
// Important that we mark the location as auto-generated, since the id
273+
// is a @_compilerInitialized field.
274+
SGF.emitDistActorIdentityInit(ctor, loc,
275+
borrowedSelf.getValue(), systemVal.getValue());
254276
}
255277

256-
void dump(SILGenFunction &SGF) const override {
278+
// Emit any active clean-ups we just pushed.
279+
while (SGF.getTopCleanup() != baseDepth)
280+
SGF.Cleanups.popAndEmitCleanup(SGF.getTopCleanup(), loc, forUnwind);
281+
282+
}
283+
284+
void InitializeDistActorIdentity::dump(SILGenFunction &) const {
257285
#ifndef NDEBUG
258-
llvm::errs() << "ResignIdentity "
259-
<< "State:" << getState() << " "
260-
<< "Self: " << self << "\n";
286+
llvm::errs() << "InitializeDistActorIdentity\n"
287+
<< "State: " << getState()
288+
<< "\n";
261289
#endif
262-
}
263-
};
264-
} // end anonymous namespace
290+
}
265291

266292
void SILGenFunction::emitDistributedActorImplicitPropertyInits(
267293
ConstructorDecl *ctor, ManagedValue selfArg) {
@@ -273,18 +299,18 @@ void SILGenFunction::emitDistributedActorImplicitPropertyInits(
273299

274300
selfArg = selfArg.borrow(*this, loc);
275301

276-
// register a clean-up to resign the identity upon abnormal exit
277-
// we do this regardless of initializer kind, since it's easy to do in SILGen.
278-
auto *actorDecl = cast<ClassDecl>(ctor->getParent()->getAsDecl());
279-
Cleanups.pushCleanup<ResignIdentity>(actorDecl, selfArg.getValue());
280-
281-
// Users must initialize the actorSystem property explicitly in their ctors
282-
if (!ctor->isImplicit())
302+
// implicit ctors initialize the system and identity from
303+
// its ActorSystem parameter.
304+
if (ctor->isImplicit()) {
305+
SILValue actorSystem = findFirstDistributedActorSystemArg(F);
306+
emitActorSystemInit(*this, ctor, loc, selfArg, actorSystem);
307+
emitDistActorIdentityInit(ctor, loc, selfArg.getValue(), actorSystem);
283308
return;
309+
}
284310

285-
// implicit ctors initialize these from the ActorSystem parameter.
286-
emitActorSystemInit(*this, ctor, loc, selfArg);
287-
emitIDInit(*this, ctor, loc, selfArg);
311+
// for explicit ctors, store (but do not push) a clean-up that will
312+
// initialize the identity in whichever scope it's pushed to.
313+
DistActorCtorContext = InitializeDistActorIdentity(ctor, selfArg);
288314
}
289315

290316
void SILGenFunction::emitDistributedActorReady(
@@ -505,7 +531,7 @@ SILGenFunction::emitConditionalResignIdentityCall(SILLocation loc,
505531
ManagedValue actorSelf,
506532
SILBasicBlock *continueBB) {
507533
assert(actorDecl->isDistributedActor() &&
508-
"only distributed actors have transport lifecycle hooks in deinit");
534+
"only distributed actors have actorSystem lifecycle hooks in deinit");
509535

510536
auto selfTy = actorDecl->getDeclaredInterfaceType();
511537

lib/SILGen/SILGenFunction.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "FormalEvaluation.h"
1717
#include "Initialization.h"
18+
#include "InitializeDistActorIdentity.h"
1819
#include "JumpDest.h"
1920
#include "RValue.h"
2021
#include "SGFContext.h"
@@ -399,6 +400,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
399400
AsyncLetChildTask>
400401
AsyncLetChildTasks;
401402

403+
/// Indicates whether this function is a distributed actor's designated
404+
/// initializer, providing the needed clean-up to emit an identity
405+
/// assignment after initializing the actorSystem property.
406+
Optional<InitializeDistActorIdentity> DistActorCtorContext;
407+
402408
/// When rebinding 'self' during an initializer delegation, we have to be
403409
/// careful to preserve the object at 1 retain count during the delegation
404410
/// because of assumptions in framework code. This enum tracks the state of
@@ -2071,6 +2077,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
20712077
void emitDistributedActorImplicitPropertyInits(
20722078
ConstructorDecl *ctor, ManagedValue selfArg);
20732079

2080+
/// Initializes just the implicit identity property of a distributed actor.
2081+
/// \param selfVal a value corresponding to the actor's self
2082+
/// \param actorSystemVal a value corresponding to the actorSystem, to be used
2083+
/// to invoke its \p assignIdentity method.
2084+
void emitDistActorIdentityInit(ConstructorDecl *ctor,
2085+
SILLocation loc,
2086+
SILValue selfVal,
2087+
SILValue actorSystemVal);
2088+
2089+
void registerDistActorIdentityInit();
2090+
20742091
/// Given a function representing a distributed actor factory, emits the
20752092
/// corresponding SIL function for it.
20762093
void emitDistributedActorFactory(

lib/SILGen/SILGenLValue.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,8 @@ namespace {
749749

750750
virtual bool isLoadingPure() const override { return true; }
751751

752+
VarDecl *getField() const { return Field; }
753+
752754
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
753755
ManagedValue base) && override {
754756
assert(base.getType().hasReferenceSemantics() &&
@@ -4505,6 +4507,18 @@ void SILGenFunction::emitAssignToLValue(SILLocation loc, RValue &&src,
45054507
emitAssignToLValue(loc, ArgumentSource(loc, std::move(src)), std::move(dest));
45064508
}
45074509

4510+
namespace {
4511+
4512+
} // end anonymous namespace
4513+
4514+
/// Checks if the last component of the LValue refers to the given var decl.
4515+
static bool referencesVar(PathComponent const& comp, VarDecl *var) {
4516+
if (comp.getKind() != PathComponent::RefElementKind)
4517+
return false;
4518+
4519+
return static_cast<RefElementComponent const&>(comp).getField() == var;
4520+
}
4521+
45084522
void SILGenFunction::emitAssignToLValue(SILLocation loc,
45094523
ArgumentSource &&src,
45104524
LValue &&dest) {
@@ -4514,6 +4528,12 @@ void SILGenFunction::emitAssignToLValue(SILLocation loc,
45144528
// LValue. For example: (x[i], x[j]) = a, b
45154529
ArgumentScope argScope(*this, loc);
45164530

4531+
// If this is an assignment to a distributed actor's actorSystem, push
4532+
// a clean-up to also initialize its id.
4533+
if (LLVM_UNLIKELY(DistActorCtorContext) &&
4534+
referencesVar(**(dest.end()-1), DistActorCtorContext->getSystemVar()))
4535+
Cleanups.pushCleanup<InitializeDistActorIdentity>(*DistActorCtorContext);
4536+
45174537
// If the last component is a getter/setter component, use a special
45184538
// generation pattern that allows us to peephole the emission of the RHS.
45194539
if (trySetterPeephole(*this, loc, std::move(src), std::move(dest))) {
@@ -4605,6 +4625,13 @@ void SILGenFunction::emitAssignLValueToLValue(SILLocation loc, LValue &&src,
46054625
return;
46064626
}
46074627

4628+
// If this is an assignment to a distributed actor's actorSystem, push
4629+
// a clean-up to also initialize its id.
4630+
// FIXME: in what situations does this lvalue to lvalue assign happen?
4631+
if (LLVM_UNLIKELY(DistActorCtorContext) &&
4632+
referencesVar(**(dest.end()-1), DistActorCtorContext->getSystemVar()))
4633+
Cleanups.pushCleanup<InitializeDistActorIdentity>(*DistActorCtorContext);
4634+
46084635
auto &rvalueTL = getTypeLowering(src.getTypeOfRValue());
46094636

46104637
auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue();

0 commit comments

Comments
 (0)