Skip to content

[Distributed] Generate SIL for DistributedActor.resolve #38938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/IRGen/GenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,

if (Builtin.ID == BuiltinValueKind::InitializeDistributedRemoteActor) {
auto actorMetatype = args.claimNext();
emitDistributedActorInitializeRemote(IGF, actorMetatype, out);
emitDistributedActorInitializeRemote(IGF, resultType, actorMetatype, out);
return;
}

Expand Down
14 changes: 11 additions & 3 deletions lib/IRGen/GenDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "GenDistributed.h"

#include "BitPatternBuilder.h"
#include "ClassTypeInfo.h"
#include "ExtraInhabitants.h"
#include "GenProto.h"
#include "GenType.h"
Expand All @@ -32,7 +33,12 @@ using namespace swift;
using namespace irgen;

llvm::Value *irgen::emitDistributedActorInitializeRemote(
IRGenFunction &IGF, llvm::Value *actorMetatype, Explosion &out) {
IRGenFunction &IGF, SILType selfType, llvm::Value *actorMetatype, Explosion &out) {
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
auto &classLayout = classTI.getClassLayout(IGF.IGM, selfType,
/*forBackwardDeployment=*/false);
llvm::Type *destType = classLayout.getType()->getPointerTo();

auto fn = IGF.IGM.getDistributedActorInitializeRemoteFn();
actorMetatype =
IGF.Builder.CreateBitCast(actorMetatype, IGF.IGM.TypeMetadataPtrTy);
Expand All @@ -41,9 +47,11 @@ llvm::Value *irgen::emitDistributedActorInitializeRemote(
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();

out.add(call);
auto result = IGF.Builder.CreateBitCast(call, destType);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh I see, very good to know..! Thanks a lot!


out.add(result);

return call;
return result;
}

void irgen::emitDistributedActorDestroy(IRGenFunction &IGF,
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenDistributed.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class IRGenFunction;
/// Emit the '_distributedActorRemoteInitialize' call.
llvm::Value *emitDistributedActorInitializeRemote(
IRGenFunction &IGF,
SILType selfType,
llvm::Value *actorMetatype,
Explosion &out);

Expand Down
216 changes: 196 additions & 20 deletions lib/SILGen/SILGenDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,34 @@ static void emitDistributedIfRemoteBranch(SILGenFunction &SGF,
B.createCondBranch(Loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB);
}

static AbstractFunctionDecl *lookupActorTransportResolveFunc(ASTContext &C) {
auto transportDecl = C.getActorTransportDecl();

for (auto decl : transportDecl->lookupDirect(DeclName(C.Id_resolve)))
if (auto funcDecl = dyn_cast<AbstractFunctionDecl>(decl))
return funcDecl;

llvm_unreachable("Missing ActorTransport.resolve function");
}

static VarDecl *lookupActorTransportProperty(ASTContext &C, ClassDecl *cd,
SILValue selfValue) {
auto transportVarDeclRefs = cd->lookupDirect(C.Id_actorTransport);
assert(transportVarDeclRefs.size() == 1);
return dyn_cast<VarDecl>(transportVarDeclRefs.front());
}

static EnumElementDecl *lookupEnumCase(ASTContext &C, EnumDecl *target,
Identifier identifier) {
auto elementDecls = target->lookupDirect(DeclName(identifier));
if (elementDecls.empty())
return nullptr;

auto *elementDecl = elementDecls.front();

return dyn_cast<EnumElementDecl>(elementDecl);
}

/******************************************************************************/
/****************** DISTRIBUTED ACTOR STORAGE INITIALIZATION ******************/
/******************************************************************************/
Expand Down Expand Up @@ -120,6 +148,7 @@ static void emitDistributedActorStore_transport(
SILValue actorSelf, AbstractFunctionDecl *func,
SILArgument *transportArg) {
auto &B = SGF.B;

auto &SGM = SGF.SGM;
SILGenFunctionBuilder builder(SGM);

Expand All @@ -135,6 +164,7 @@ static void emitDistributedActorStore_transport(
auto *var = dyn_cast<VarDecl>(vars.front());

// ----

auto fieldAddr = B.createRefElementAddr(
loc, actorSelf, var,
SGF.getLoweredType(var->getInterfaceType()));
Expand Down Expand Up @@ -166,6 +196,7 @@ emitDistributedActor_init_transportStore(
SILValue transportArgValue = getActorTransportArgument(C, F, ctor);

// ----

auto transportFieldAddr = B.createRefElementAddr(
loc, borrowedSelfArg.getValue(), var,
SGF.getLoweredType(var->getInterfaceType()));
Expand All @@ -187,6 +218,7 @@ static void emitDistributedActorStore_id(
SILValue actorSelf, AbstractFunctionDecl *func,
SILArgument *identityArg) {
auto &B = SGF.B;

auto &SGM = SGF.SGM;
SILGenFunctionBuilder builder(SGM);

Expand Down Expand Up @@ -237,7 +269,9 @@ static void emitDistributedActorStore_init_assignIdentity(

// --- Prepare the arguments
SILValue transportArgValue = getActorTransportArgument(C, F, ctor);

ProtocolDecl *distributedActorProto = C.getProtocol(KnownProtocolKind::DistributedActor);

assert(distributedActorProto);
assert(transportProto);

Expand Down Expand Up @@ -382,6 +416,7 @@ void SILGenFunction::initializeDistributedActorImplicitStorageInit(

void SILGenFunction::emitDistributedActorReady(
ConstructorDecl *ctor, ManagedValue selfArg) {

auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
auto &C = classDecl->getASTContext();
Expand Down Expand Up @@ -462,6 +497,86 @@ void SILGenFunction::emitDistributedActorReady(
/******************* DISTRIBUTED ACTOR RESOLVE FUNCTION ***********************/
/******************************************************************************/

/// Synthesize the distributed actor's identity (`id`) initialization:
///
/// \verbatim
/// transport.resolve(_, as:)
/// \endverbatim
static void createDistributedActorFactory_resolve(
SILGenFunction &SGF, ASTContext &C, FuncDecl *fd, SILValue identityValue,
SILValue transportValue, Type selfTy, SILValue selfMetatypeValue,
SILType resultTy, SILBasicBlock *normalBB, SILBasicBlock *errorBB) {
auto &B = SGF.B;
auto &SGM = SGF.SGM;
auto &F = SGF.F;
SILGenFunctionBuilder builder(SGM);

auto loc = SILLocation(fd);
loc.markAutoGenerated();

ProtocolDecl *distributedActorProto =
C.getProtocol(KnownProtocolKind::DistributedActor);
ProtocolDecl *transportProto =
C.getProtocol(KnownProtocolKind::ActorTransport);
assert(distributedActorProto);
assert(transportProto);

// // --- Open the transport existential
OpenedArchetypeType *Opened;
auto transportASTType = transportValue->getType().getASTType();
auto openedTransportType =
transportASTType->openAnyExistentialType(Opened)->getCanonicalType();
auto openedTransportSILType = F.getLoweredType(openedTransportType);
auto transportArchetypeValue =
B.createOpenExistentialAddr(loc, transportValue, openedTransportSILType,
OpenedExistentialAccess::Immutable);

// --- 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 = SGF.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 distributedActorConfRef =
module->lookupConformance(selfTy, distributedActorProto);
assert(!distributedActorConfRef.isInvalid() &&
"Missing conformance to `DistributedActor`");

auto resolveMethod =
cast<FuncDecl>(transportProto->getSingleRequirement(C.Id_resolve));
auto resolveRef = SILDeclRef(resolveMethod, SILDeclRef::Kind::Func);
auto constantInfo =
SGF.getConstantInfo(SGF.getTypeExpansionContext(), resolveRef);
auto resolveSILTy = constantInfo.getSILType();

auto resolveWitnessMethod =
B.createWitnessMethod(loc,
/*lookupTy*/ openedTransportType,
/*Conformance*/ transportConfRef,
/*member*/ resolveRef,
/*methodTy*/ resolveSILTy);

// // --- prepare conformance subs
auto genericSig = resolveMethod->getGenericSignature();

SubstitutionMap subs =
SubstitutionMap::get(genericSig, {openedTransportType, selfTy},
{transportConfRef, distributedActorConfRef});

// // ---- actually call transport.resolve(id, as: Self.self)

SmallVector<SILValue, 3> params;
params.push_back(identityValue);
params.push_back(selfMetatypeValue);
params.push_back(transportArchetypeValue); // self for the call, as last param

B.createTryApply(loc, resolveWitnessMethod, subs, params, normalBB, errorBB);
}

/// Function body of:
/// \verbatim
/// DistributedActor.resolve(
Expand All @@ -487,28 +602,66 @@ void SILGenFunction::emitDistributedActorFactory(FuncDecl *fd) {
assert(
transportArg->getType().getASTType()->isEqual(C.getActorTransportType()));

// --- Parameter: self
SILValue selfArgValue = F.getSelfArgument();
ManagedValue selfArg = ManagedValue::forUnmanaged(selfArgValue);

// type: SpecificDistributedActor.Type
auto selfArgType = F.mapTypeIntoContext(selfArg.getType().getASTType());
auto selfMetatype = getLoweredType(selfArgType);
SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype);

// type: SpecificDistributedActor
auto *selfTyDecl = fd->getParent()->getSelfNominalTypeDecl();
assert(selfTyDecl->isDistributedActor());
auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType());
auto returnTy = getLoweredType(selfTy);

SILValue selfArgValue = F.getSelfArgument();
ManagedValue selfArg = ManagedValue::forUnmanaged(selfArgValue);
// ==== Prepare all the basic blocks
auto returnBB = createBasicBlock();
auto resolvedBB = createBasicBlock();
auto makeProxyBB = createBasicBlock();
auto switchBB = createBasicBlock();
auto errorBB = createBasicBlock();

SILFunctionConventions fnConv = F.getConventions(); // TODO: no idea?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SILFunctionConventions fnConv = F.getConventions(); // TODO: no idea?
SILFunctionConventions fnConv = F.getConventions();

seems to be right I guess :)


// --- get the uninitialized allocation from the runtime system.
FullExpr scope(Cleanups, CleanupLocation(fd));

auto optionalReturnTy = SILType::getOptionalType(returnTy);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice optionals made things a bit easier :)


// ==== Case 'remote') Create the remote instance
// ==== Call `try transport.resolve(id, as: Self.self)`
{
// ==== Create 'remote' distributed actor instance
// --- Prepare param: Self.self
// type: SpecificDistributedActor
auto returnTy = getLoweredType(
F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType()));
createDistributedActorFactory_resolve(
*this, C, fd, identityArg, transportArg, selfTy, selfMetatypeValue,
optionalReturnTy, switchBB, errorBB);
}

// ==== switch resolved { ... }
{
B.emitBlock(switchBB);
auto resolve =
switchBB->createPhiArgument(optionalReturnTy, OwnershipKind::Owned);

B.createSwitchEnum(
loc, resolve, nullptr,
{{C.getOptionalSomeDecl(), resolvedBB},
{std::make_pair(C.getOptionalNoneDecl(), makeProxyBB)}});
}

// type: SpecificDistributedActor.Type
auto selfMetatype =
getLoweredType(F.mapTypeIntoContext(selfArg.getType().getASTType()));
SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype);
// ==== Case 'some') return the resolved instance
{
B.emitBlock(resolvedBB);

auto local = resolvedBB->createPhiArgument(returnTy, OwnershipKind::Owned);

B.createBranch(loc, returnBB, {local});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh nice, not too hard then 👍


// --- get the uninitialized allocation from the runtime system.
FullExpr scope(Cleanups, CleanupLocation(fd));
// ==== Case 'none') Create the remote instance
{
B.emitBlock(makeProxyBB);
// ==== Create 'remote' distributed actor instance

// --- Call: _distributedActorRemoteInitialize(Self.self)
auto builtinName = C.getIdentifier(
Expand All @@ -528,8 +681,32 @@ void SILGenFunction::emitDistributedActorFactory(FuncDecl *fd) {
emitDistributedActorStore_transport(
C, *this, /*actorSelf*/remote, fd, transportArg);

// ==== Return the fully initialized remote instance
B.createReturn(loc, remote);
// ==== Branch to return the fully initialized remote instance
B.createBranch(loc, returnBB, {remote});
}

// --- Emit return logic
// return <remote>
{
B.emitBlock(returnBB);

auto local = returnBB->createPhiArgument(returnTy, OwnershipKind::Owned);

Cleanups.emitCleanupsForReturn(CleanupLocation(loc), NotForUnwind);
B.createReturn(loc, local);
}

// --- Emit rethrow logic
// throw error
{
B.emitBlock(errorBB);

auto error = errorBB->createPhiArgument(
fnConv.getSILErrorType(F.getTypeExpansionContext()),
OwnershipKind::Owned);

Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind);
B.createThrow(loc, error);
}
}

Expand Down Expand Up @@ -563,9 +740,8 @@ void SILGenFunction::emitDistributedActor_resignAddress(
getLoweredType(idVarDeclRef->getType()));

// ==== locate: self.actorTransport
auto transportVarDeclRefs = cd->lookupDirect(ctx.Id_actorTransport);
assert(transportVarDeclRefs.size() == 1);
auto *transportVarDeclRef = dyn_cast<VarDecl>(transportVarDeclRefs.front());
auto transportVarDeclRef = lookupActorTransportProperty(ctx, cd, selfValue);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

auto transportRef =
B.createRefElementAddr(Loc, selfValue, transportVarDeclRef,
getLoweredType(transportVarDeclRef->getType()));
Expand Down
7 changes: 4 additions & 3 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,17 +514,18 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
emitProfilerIncrement(fd->getTypecheckedBody());
emitProlog(captureInfo, fd->getParameters(), fd->getImplicitSelfDecl(), fd,
fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc());
prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd));

if (fd->isDistributedActorFactory()) {
// Synthesize the factory function body
emitDistributedActorFactory(fd);
} else {
prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, so we emit the cleanups ourselves ok 👍

// Emit the actual function body as usual
emitStmt(fd->getTypecheckedBody());
}

emitEpilog(fd);
emitEpilog(fd);
}

mergeCleanupBlocks();
}
Expand Down
8 changes: 1 addition & 7 deletions stdlib/public/Distributed/ActorTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public protocol ActorTransport: Sendable {
///
/// Detecting liveness of such remote actors shall be offered / by transport libraries
/// by other means, such as "watching an actor for termination" or similar.
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> ActorResolved<Act> // TODO(distributed): make just optional
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? // TODO(distributed): make just optional
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? // TODO(distributed): make just optional
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act?

where Act: DistributedActor

// ==== ---------------------------------------------------------------------
Expand All @@ -73,9 +73,3 @@ public protocol ActorTransport: Sendable {
func resignIdentity(_ id: AnyActorIdentity)

}

@available(SwiftStdlib 5.5, *)
public enum ActorResolved<Act: DistributedActor> {
case resolved(Act)
case makeProxy
}
Loading