decouple the emission of actorReady from SILGen

We need to be able to inject a call to a distributed actor's
transport.actorReady, passing the actor instance to it,
during definite initialization. This means that its dependence
on SILGenFunction must be broken, hence this refactoring as
a SILOptimizer utility.
This commit is contained in:
Kavon Farvardin
2021-10-19 15:11:18 -07:00
parent b7d5e0aff7
commit 51b769795a
6 changed files with 230 additions and 187 deletions

View File

@@ -0,0 +1,43 @@
//===---- DistributedActor.h - SIL utils for distributed actors -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SILOPTIMIZER_UTILS_DISTRIBUTED_ACTOR_H
#define SWIFT_SILOPTIMIZER_UTILS_DISTRIBUTED_ACTOR_H
namespace swift {
class ASTContext;
class ConstructorDecl;
class ClassDecl;
class SILBuilder;
class SILArgument;
class SILFunction;
class SILLocation;
class SILValue;
/// Perform a load of the given distributed actor's transport.
/// \param actorSelf the value representing `self` for the distributed actor
/// instance. \returns the transport value
SILValue loadActorTransport(SILBuilder &B, SILLocation loc,
ClassDecl *actorDecl, SILValue actorSelf);
/// Emits code that notifies the distributed actor's transport that the
/// actor is ready for execution.
/// \param B the builder to use when emitting the code.
/// \param actor the distributed actor instance to pass to the transport as
/// being "ready" \param transport a value representing the ActorTransport
void emitActorReadyCall(SILBuilder &B, SILLocation loc, ClassDecl *actorDecl,
SILValue actor, SILValue transport);
} // namespace swift
#endif

View File

@@ -785,7 +785,7 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
// Distributed actor initializers implicitly initialize their transport and id
if (isDesignatedDistActorInit) {
initializeDistributedActorImplicitStorageInit(ctor, selfArg);
emitDistActorImplicitPropertyInits(ctor, selfArg);
}
// Prepare the end of initializer location.

View File

@@ -28,11 +28,12 @@
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SIL/TypeLowering.h"
#include "swift/SILOptimizer/Utils/DistributedActor.h"
using namespace swift;
using namespace Lowering;
// MARK: Local utility functions
// MARK: utility functions
/// Obtain a nominal type's member by name, as a VarDecl.
/// \returns nullptr if the name lookup doesn't resolve to exactly one member,
@@ -44,6 +45,21 @@ static VarDecl* lookupProperty(NominalTypeDecl *ty, DeclName name) {
return dyn_cast<VarDecl>(refs.front());
}
/// Perform an initializing store to the given property using the value
/// \param actorSelf the value representing `self` for the actor instance.
/// \param prop the property to be initialized.
/// \param value the value to use when initializing the property.
static void initializeProperty(SILGenFunction &SGF, SILLocation loc,
SILValue actorSelf,
VarDecl* prop, SILValue value) {
auto fieldAddr = SGF.B.createRefElementAddr(loc, actorSelf, prop,
SGF.getLoweredType(prop->getInterfaceType()));
SGF.B.createCopyAddr(loc,
/*src*/value,
/*dest*/fieldAddr,
IsNotTake, IsInitialization);
}
/******************************************************************************/
/******************* COMMON (DISTRIBUTED) SIL PATTERNS ************************/
/******************************************************************************/
@@ -82,20 +98,15 @@ static void emitDistributedIfRemoteBranch(SILGenFunction &SGF,
B.createCondBranch(Loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB);
}
/******************************************************************************/
/****************** DISTRIBUTED ACTOR STORAGE INITIALIZATION ******************/
/******************************************************************************/
// MARK: local instance initialization
/// Get the `ActorTransport` parameter of the constructor.
/// Sema should have guaranteed that there is exactly one of them for any
/// designated initializer of a distributed actor.
static SILArgument*
getActorTransportArgument(ASTContext& C, SILFunction& F, ConstructorDecl *ctor) {
auto *DC = cast<DeclContext>(ctor);
auto module = DC->getParentModule();
/// Finds the first `ActorTransport`-compatible parameter of the given function.
/// Crashes if the given function does not have such a parameter.
static SILArgument *findFirstActorTransportArg(SILFunction &F) {
auto *module = F.getModule().getSwiftModule();
auto &C = F.getASTContext();
auto *transportProto =
C.getProtocol(KnownProtocolKind::ActorTransport);
auto *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
Type transportTy = transportProto->getDeclaredInterfaceType();
for (auto arg : F.getArguments()) {
@@ -103,8 +114,8 @@ getActorTransportArgument(ASTContext& C, SILFunction& F, ConstructorDecl *ctor)
Type argTy = arg->getType().getASTType();
auto argDecl = arg->getDecl();
auto conformsToTransport = module->lookupConformance(
argDecl->getInterfaceType(), transportProto);
auto conformsToTransport =
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
// Is it a protocol that conforms to ActorTransport?
if (argTy->isEqual(transportTy) || conformsToTransport) {
@@ -122,42 +133,21 @@ getActorTransportArgument(ASTContext& C, SILFunction& F, ConstructorDecl *ctor)
llvm_unreachable("Missing required ActorTransport argument!");
}
/// Perform an initializing store to the given property using the value
/// \param actorSelf the value representing `self` for the actor instance.
/// \param prop the property to be initialized.
/// \param value the value to use when initializing the property.
static void initializeProperty(SILGenFunction &SGF, SILLocation loc,
SILValue actorSelf,
VarDecl* prop, SILArgument *value) {
auto fieldAddr = SGF.B.createRefElementAddr(loc, actorSelf, prop,
SGF.getLoweredType(prop->getInterfaceType()));
SGF.B.createCopyAddr(loc,
/*src*/value,
/*dest*/fieldAddr,
IsNotTake, IsInitialization);
}
// TODO(distributed): remove this store impl and reuse Store_transport
static void
emitDistributedActor_init_transportStore(
SILGenFunction &SGF,
ManagedValue borrowedSelfArg, VarDecl *selfDecl,
ConstructorDecl *ctor, VarDecl *var) {
auto &C = selfDecl->getASTContext();
auto &B = SGF.B;
auto &F = SGF.F;
/// For the initialization of a local distributed actor instance, emits code to initialize the instance's
/// stored property corresponding to the transport.
static void emitTransportInit(SILGenFunction &SGF,
ConstructorDecl *ctor,
SILLocation loc,
ManagedValue actorSelf) {
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
auto &C = ctor->getASTContext();
auto loc = SILLocation(ctor);
loc.markAutoGenerated();
// ==== Prepare assignment: get the self.transport address
SILValue transportArg = getActorTransportArgument(C, F, ctor);
auto fieldAddr = B.createRefElementAddr(
loc, borrowedSelfArg.getValue(), var,
SGF.getLoweredType(var->getInterfaceType()));
// Sema has already guaranteed that there is exactly one ActorTransport
// argument to the constructor, so we grab the first one from the params.
SILValue transportArg = findFirstActorTransportArg(SGF.F);
VarDecl *var = lookupProperty(classDecl, C.Id_actorTransport);
assert(var);
// If the argument is not existential, it will be a concrete type
// that can be erased to that existential.
@@ -186,45 +176,35 @@ emitDistributedActor_init_transportStore(
transportValue = mv.getValue();
}
// ==== Store the transport
B.createCopyAddr(loc,
/*src*/transportValue,
/*dest*/fieldAddr,
IsNotTake, IsInitialization);
initializeProperty(SGF, loc, actorSelf.getValue(), var, transportValue);
}
/// Synthesize the distributed actor's identity (`id`) initialization:
/// Emits the distributed actor's identity (`id`) initialization.
///
/// Specifically, it performs:
/// \verbatim
/// self.id = transport.assignIdentity(Self.self)
/// \endverbatim
static void emitDistributedActorStore_init_assignIdentity(
SILGenFunction &SGF,
ManagedValue borrowedSelfArg, VarDecl *selfVarDecl,
ConstructorDecl *ctor, VarDecl *var) {
auto &C = selfVarDecl->getASTContext();
static void emitIdentityInit(SILGenFunction &SGF, ConstructorDecl *ctor,
SILLocation loc, ManagedValue borrowedSelfArg) {
auto &C = ctor->getASTContext();
auto &B = SGF.B;
auto &F = SGF.F;
auto &SGM = SGF.SGM;
SILGenFunctionBuilder builder(SGM);
auto loc = SILLocation(ctor);
loc.markAutoGenerated();
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
// ==== prepare the transport.assignIdentity(_:) function
ProtocolDecl *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
// --- Prepare the arguments
SILValue transportArgValue = getActorTransportArgument(C, F, ctor);
ProtocolDecl *distributedActorProto = C.getProtocol(KnownProtocolKind::DistributedActor);
assert(distributedActorProto);
assert(transportProto);
SILValue transportValue =
loadActorTransport(B, loc, classDecl, borrowedSelfArg.getValue());
// --- Open the transport existential, if needed.
SILValue transportValue = transportArgValue;
auto transportASTType = transportArgValue->getType().getASTType();
auto transportASTType = transportValue->getType().getASTType();
if (transportASTType->isAnyExistentialType()) {
OpenedArchetypeType *Opened;
transportASTType =
@@ -282,6 +262,8 @@ static void emitDistributedActorStore_init_assignIdentity(
{transportASTType, selfTy},
{transportConfRef, distributedActorConfRef});
VarDecl *var = lookupProperty(classDecl, C.Id_id);
// --- create a temporary storage for the result of the call
// it will be deallocated automatically as we exit this scope
auto resultTy = SGF.getLoweredType(var->getInterfaceType());
@@ -292,133 +274,40 @@ static void emitDistributedActorStore_init_assignIdentity(
loc, assignWitnessMethod, subs,
{ temp, selfMetatypeValue, transportValue});
// ==== Assign the identity to stored property
// TODO(distributed): reuse emitDistributedActorStore_id here, pass the SILValue
// --- Prepare address of self.id
auto idFieldAddr = B.createRefElementAddr(
loc, borrowedSelfArg.getValue(), var,
SGF.getLoweredType(var->getInterfaceType()));
// --- assign to the property
B.createCopyAddr(loc, /*src*/temp, /*dest*/idFieldAddr,
IsTake, IsInitialization);
initializeProperty(SGF, loc, borrowedSelfArg.getValue(), var, temp);
}
/******************************************************************************/
/******************* DISTRIBUTED ACTOR LOCAL INIT *****************************/
/******************************************************************************/
void SILGenFunction::initializeDistributedActorImplicitStorageInit(
void SILGenFunction::emitDistActorImplicitPropertyInits(
ConstructorDecl *ctor, ManagedValue selfArg) {
VarDecl *selfVarDecl = ctor->getImplicitSelfDecl();
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
auto &C = classDecl->getASTContext();
// Only designated initializers should perform this initialization.
assert(ctor->isDesignatedInit());
// Only designated initializers get the lifecycle handling injected
if (!ctor->isDesignatedInit())
return;
auto loc = SILLocation(ctor);
loc.markAutoGenerated();
SILLocation prologueLoc = RegularLocation(ctor);
prologueLoc.markAsPrologue(); // TODO: no idea if this is necessary or makes sense
// ==== Initialize Properties.
auto borrowedSelfArg = selfArg.borrow(*this, prologueLoc);
VarDecl *transportVar = lookupProperty(classDecl, C.Id_actorTransport);
emitDistributedActor_init_transportStore(*this, borrowedSelfArg, selfVarDecl,
ctor, transportVar);
VarDecl *identityVar = lookupProperty(classDecl, C.Id_id);
emitDistributedActorStore_init_assignIdentity(*this, borrowedSelfArg,
selfVarDecl,
ctor, identityVar);
selfArg = selfArg.borrow(*this, loc);
emitTransportInit(*this, ctor, loc, selfArg);
emitIdentityInit(*this, ctor, loc, selfArg);
}
void SILGenFunction::emitDistributedActorReady(
SILLocation loc, ConstructorDecl *ctor, ManagedValue actorSelf) {
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
auto &C = classDecl->getASTContext();
// Only designated initializers get the lifecycle handling injected
assert(ctor->isDesignatedInit());
// setup scope for clean-ups
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
SILValue transport = findFirstActorTransportArg(F);
FullExpr scope(Cleanups, CleanupLocation(loc));
auto borrowedSelf = actorSelf.borrow(*this, loc);
// === Prepare the arguments
SILValue transportArgValue = getActorTransportArgument(C, F, ctor);
actorSelf = actorSelf.borrow(*this, loc);
ProtocolDecl *distributedActorProto = C.getProtocol(KnownProtocolKind::DistributedActor);
ProtocolDecl *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
assert(distributedActorProto);
assert(transportProto);
// --- Open the transport existential
SILValue transportValue = transportArgValue;
auto transportASTType = transportValue->getType().getASTType();
if (transportASTType->isAnyExistentialType()) {
OpenedArchetypeType *Opened;
transportASTType =
transportASTType->openAnyExistentialType(Opened)->getCanonicalType();
transportValue = B.createOpenExistentialAddr(
loc, transportValue, F.getLoweredType(transportASTType),
OpenedExistentialAccess::Immutable);
emitActorReadyCall(B, loc, classDecl, borrowedSelf.getValue(), transport);
}
// === Make the transport.actorReady call
// --- prepare the witness_method
// Note: it does not matter on what module we perform the lookup,
// it is currently ignored. So the Stdlib module is good enough.
auto *module = getModule().getSwiftModule();
// the conformance here is just an abstract thing so we can simplify
auto transportConfRef = ProtocolConformanceRef(transportProto);
assert(!transportConfRef.isInvalid() && "Missing conformance to `ActorTransport`");
auto *selfTyDecl = ctor->getParent()->getSelfNominalTypeDecl();
auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType()); // TODO: thats just self var devl getType
auto distributedActorConfRef = module->lookupConformance(
selfTy,
distributedActorProto);
assert(!distributedActorConfRef.isInvalid() && "Missing conformance to `DistributedActor`");
// === Prepare the actorReady function
auto actorReadyMethod =
cast<FuncDecl>(transportProto->getSingleRequirement(C.Id_actorReady));
auto actorReadyRef = SILDeclRef(actorReadyMethod, SILDeclRef::Kind::Func);
auto actorReadySILTy =
getConstantInfo(getTypeExpansionContext(), actorReadyRef)
.getSILType();
auto readyWitnessMethod = B.createWitnessMethod(
loc,
/*lookupTy*/transportASTType,
/*Conformance*/transportConfRef,
/*member*/actorReadyRef,
/*methodTy*/actorReadySILTy);
// --- prepare conformance subs
auto genericSig = actorReadyMethod->getGenericSignature();
SubstitutionMap subs =
SubstitutionMap::get(genericSig,
{transportASTType, selfTy},
{transportConfRef, distributedActorConfRef});
// ---- actually call transport.actorReady(self)
B.createApply(
loc, readyWitnessMethod, subs,
{ actorSelf.getValue(), transportValue});
}
/******************************************************************************/
/******************* DISTRIBUTED ACTOR RESOLVE FUNCTION ***********************/
/******************************************************************************/
// MARK: remote instance initialization
/// Synthesize the distributed actor's identity (`id`) initialization:
///

View File

@@ -2006,8 +2006,9 @@ public:
// Distributed Actors
//===---------------------------------------------------------------------===//
/// Initialize the distributed actors transport and id.
void initializeDistributedActorImplicitStorageInit(
/// Initializes the implicit stored properties of a distributed actor that correspond to
/// its transport and identity.
void emitDistActorImplicitPropertyInits(
ConstructorDecl *ctor, ManagedValue selfArg);
/// Given a function representing a distributed actor factory, emits the

View File

@@ -10,6 +10,7 @@ target_sources(swiftSILOptimizer PRIVATE
ConstExpr.cpp
Devirtualize.cpp
DifferentiationMangler.cpp
DistributedActor.cpp
Existential.cpp
GenericCloner.cpp
Generics.cpp

View File

@@ -0,0 +1,109 @@
//===-- DistributedActor.cpp - SIL utils for distributed actors -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/SILOptimizer/Utils/DistributedActor.h"
#include "swift/AST/Decl.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILLocation.h"
namespace swift {
// MARK: utilities
SILValue loadActorTransport(SILBuilder &B, SILLocation loc,
ClassDecl *actorDecl, SILValue actorSelf) {
assert(actorDecl->isDistributedActor());
// get the VarDecl corresponding to the transport
auto &C = actorDecl->getASTContext();
auto refs = actorDecl->lookupDirect(C.Id_actorTransport);
assert(refs.size() == 1);
VarDecl *prop = dyn_cast<VarDecl>(refs.front());
// form a reference and load it
auto &F = B.getFunction();
auto fieldAddr = B.createRefElementAddr(
loc, actorSelf, prop, F.getLoweredType(prop->getInterfaceType()));
return B.createLoad(loc, fieldAddr, LoadOwnershipQualifier::Copy);
}
// MARK: exposed interface
void emitActorReadyCall(SILBuilder &B, SILLocation loc, ClassDecl *actorDecl,
SILValue actor, SILValue transport) {
auto &F = B.getFunction();
auto &M = B.getModule();
auto &C = actorDecl->getASTContext();
ProtocolDecl *actorProto = C.getProtocol(KnownProtocolKind::DistributedActor);
ProtocolDecl *transProto = C.getProtocol(KnownProtocolKind::ActorTransport);
assert(actorProto);
assert(transProto);
// Open the transport existential
auto transportASTType = transport->getType().getASTType();
if (transportASTType->isAnyExistentialType()) {
OpenedArchetypeType *Opened;
transportASTType =
transportASTType->openAnyExistentialType(Opened)->getCanonicalType();
transport = B.createOpenExistentialAddr(loc, transport,
F.getLoweredType(transportASTType),
OpenedExistentialAccess::Immutable);
}
// Make the transport.actorReady call
// the conformance here is just an abstract thing so we can simplify
auto transportConfRef = ProtocolConformanceRef(transProto);
assert(!transportConfRef.isInvalid() &&
"Missing conformance to `ActorTransport`");
auto *selfTyDecl = actorDecl->getSelfNominalTypeDecl();
auto selfTy = F.mapTypeIntoContext(
selfTyDecl->getDeclaredInterfaceType()); // TODO: thats just self var devl
// getType
// Note: it does not matter on what module we perform the lookup,
// it is currently ignored. So the Stdlib module is good enough.
auto *module = M.getSwiftModule();
auto distributedActorConfRef = module->lookupConformance(selfTy, actorProto);
assert(!distributedActorConfRef.isInvalid() &&
"Missing conformance to `DistributedActor`");
// Prepare the actorReady function
auto actorReadyMethod =
cast<FuncDecl>(transProto->getSingleRequirement(C.Id_actorReady));
auto actorReadyRef = SILDeclRef(actorReadyMethod, SILDeclRef::Kind::Func);
auto actorReadySILTy =
M.Types.getConstantInfo(B.getTypeExpansionContext(), actorReadyRef)
.getSILType();
auto readyWitnessMethod =
B.createWitnessMethod(loc,
/*lookupTy*/ transportASTType,
/*Conformance*/ transportConfRef,
/*member*/ actorReadyRef,
/*methodTy*/ actorReadySILTy);
// prepare conformance substitutions
auto genericSig = actorReadyMethod->getGenericSignature();
SubstitutionMap subs =
SubstitutionMap::get(genericSig, {transportASTType, selfTy},
{transportConfRef, distributedActorConfRef});
B.createApply(loc, readyWitnessMethod, subs, {actor, transport});
}
} // namespace swift