Skip to content

[concurrency] SILGen: emit @asyncHandler functions. #34876

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 4 commits into from
Dec 1, 2020
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
22 changes: 17 additions & 5 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class ASTMangler : public Mangler {
public:
enum class SymbolKind {
Default,
AsyncHandlerBody,
DynamicThunk,
SwiftAsObjCThunk,
ObjCAsSwiftThunk,
Expand Down Expand Up @@ -323,16 +324,24 @@ class ASTMangler : public Mangler {

void appendAnyGenericType(const GenericTypeDecl *decl);

void appendFunction(AnyFunctionType *fn, bool isFunctionMangling = false,
const ValueDecl *forDecl = nullptr);
enum FunctionManglingKind {
NoFunctionMangling,
FunctionMangling,
AsyncHandlerBodyMangling
};

void appendFunction(AnyFunctionType *fn,
FunctionManglingKind functionMangling = NoFunctionMangling,
const ValueDecl *forDecl = nullptr);
void appendFunctionType(AnyFunctionType *fn, bool isAutoClosure = false,
const ValueDecl *forDecl = nullptr);
void appendClangType(AnyFunctionType *fn);
template <typename FnType>
void appendClangType(FnType *fn, llvm::raw_svector_ostream &os);

void appendFunctionSignature(AnyFunctionType *fn,
const ValueDecl *forDecl = nullptr);
const ValueDecl *forDecl,
FunctionManglingKind functionMangling);

void appendFunctionInputType(ArrayRef<AnyFunctionType::Param> params,
const ValueDecl *forDecl = nullptr);
Expand Down Expand Up @@ -383,7 +392,10 @@ class ASTMangler : public Mangler {
GenericSignature &genericSig,
GenericSignature &parentGenericSig);

void appendDeclType(const ValueDecl *decl, bool isFunctionMangling = false);


void appendDeclType(const ValueDecl *decl,
FunctionManglingKind functionMangling = NoFunctionMangling);

bool tryAppendStandardSubstitution(const GenericTypeDecl *type);

Expand All @@ -400,7 +412,7 @@ class ASTMangler : public Mangler {

void appendEntity(const ValueDecl *decl, StringRef EntityOp, bool isStatic);

void appendEntity(const ValueDecl *decl);
void appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody = false);

void appendProtocolConformance(const ProtocolConformance *conformance);
void appendProtocolConformanceRef(const RootProtocolConformance *conformance);
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/ExtInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,10 @@ class SILExtInfo {
return builder.withNoEscape(noEscape).build();
}

SILExtInfo withAsync(bool isAsync = true) const {
return builder.withAsync(isAsync).build();
}

bool isEqualTo(SILExtInfo other, bool useClangTypes) const {
return builder.isEqualTo(other.builder, useClangTypes);
}
Expand Down
1 change: 1 addition & 0 deletions include/swift/SIL/SILDeclRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ struct SILDeclRef {
enum class ManglingKind {
Default,
DynamicThunk,
AsyncHandlerBody
};

/// Produce a mangled form of this constant.
Expand Down
31 changes: 18 additions & 13 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ std::string ASTMangler::mangleClosureEntity(const AbstractClosureExpr *closure,

std::string ASTMangler::mangleEntity(const ValueDecl *decl, SymbolKind SKind) {
beginMangling();
appendEntity(decl);
appendEntity(decl, SKind == SymbolKind::AsyncHandlerBody);
appendSymbolKind(SKind);
return finalize();
}
Expand Down Expand Up @@ -657,7 +657,7 @@ std::string ASTMangler::mangleTypeAsUSR(Type Ty) {
Ty = getTypeForDWARFMangling(Ty);

if (auto *fnType = Ty->getAs<AnyFunctionType>()) {
appendFunction(fnType, false);
appendFunction(fnType);
} else {
appendType(Ty);
}
Expand Down Expand Up @@ -744,6 +744,7 @@ std::string ASTMangler::mangleOpaqueTypeDecl(const ValueDecl *decl) {
void ASTMangler::appendSymbolKind(SymbolKind SKind) {
switch (SKind) {
case SymbolKind::Default: return;
case SymbolKind::AsyncHandlerBody: return;
case SymbolKind::DynamicThunk: return appendOperator("TD");
case SymbolKind::SwiftAsObjCThunk: return appendOperator("To");
case SymbolKind::ObjCAsSwiftThunk: return appendOperator("TO");
Expand Down Expand Up @@ -2249,7 +2250,8 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
addSubstitution(cast<TypeAliasDecl>(decl));
}

void ASTMangler::appendFunction(AnyFunctionType *fn, bool isFunctionMangling,
void ASTMangler::appendFunction(AnyFunctionType *fn,
FunctionManglingKind functionMangling,
const ValueDecl *forDecl) {
// Append parameter labels right before the signature/type.
auto parameters = fn->getParams();
Expand All @@ -2269,8 +2271,8 @@ void ASTMangler::appendFunction(AnyFunctionType *fn, bool isFunctionMangling,
appendOperator("y");
}

if (isFunctionMangling) {
appendFunctionSignature(fn, forDecl);
if (functionMangling != NoFunctionMangling) {
appendFunctionSignature(fn, forDecl, functionMangling);
} else {
appendFunctionType(fn, /*autoclosure*/ false, forDecl);
}
Expand All @@ -2281,7 +2283,7 @@ void ASTMangler::appendFunctionType(AnyFunctionType *fn, bool isAutoClosure,
assert((DWARFMangling || fn->isCanonical()) &&
"expecting canonical types when not mangling for the debugger");

appendFunctionSignature(fn, forDecl);
appendFunctionSignature(fn, forDecl, NoFunctionMangling);

bool mangleClangType = fn->getASTContext().LangOpts.UseClangFunctionTypes &&
fn->hasNonDerivableClangType();
Expand Down Expand Up @@ -2359,10 +2361,11 @@ void ASTMangler::appendClangType(AnyFunctionType *fn) {
}

void ASTMangler::appendFunctionSignature(AnyFunctionType *fn,
const ValueDecl *forDecl) {
const ValueDecl *forDecl,
FunctionManglingKind functionMangling) {
appendFunctionResultType(fn->getResult(), forDecl);
appendFunctionInputType(fn->getParams(), forDecl);
if (fn->isAsync())
if (fn->isAsync() || functionMangling == AsyncHandlerBodyMangling)
appendOperator("Y");
if (fn->isThrowing())
appendOperator("K");
Expand Down Expand Up @@ -2780,22 +2783,23 @@ CanType ASTMangler::getDeclTypeForMangling(
return canTy;
}

void ASTMangler::appendDeclType(const ValueDecl *decl, bool isFunctionMangling) {
void ASTMangler::appendDeclType(const ValueDecl *decl,
FunctionManglingKind functionMangling) {
Mod = decl->getModuleContext();
GenericSignature genericSig;
GenericSignature parentGenericSig;
auto type = getDeclTypeForMangling(decl, genericSig, parentGenericSig);

if (AnyFunctionType *FuncTy = type->getAs<AnyFunctionType>()) {
appendFunction(FuncTy, isFunctionMangling, decl);
appendFunction(FuncTy, functionMangling, decl);
} else {
appendType(type, decl);
}

// Mangle the generic signature, if any.
if (genericSig && appendGenericSignature(genericSig, parentGenericSig)) {
// The 'F' function mangling doesn't need a 'u' for its generic signature.
if (!isFunctionMangling)
if (functionMangling == NoFunctionMangling)
appendOperator("u");
}
}
Expand Down Expand Up @@ -2870,7 +2874,7 @@ void ASTMangler::appendEntity(const ValueDecl *decl, StringRef EntityOp,
appendOperator("Z");
}

void ASTMangler::appendEntity(const ValueDecl *decl) {
void ASTMangler::appendEntity(const ValueDecl *decl, bool isAsyncHandlerBody) {
Copy link
Member

Choose a reason for hiding this comment

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

Should this bool be a FunctionManglingKind instead to make it clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought of that, but at the end decided to pass a "bool", because FunctionManglingKind has 3 cases.

assert(!isa<ConstructorDecl>(decl));
assert(!isa<DestructorDecl>(decl));

Expand All @@ -2891,7 +2895,8 @@ void ASTMangler::appendEntity(const ValueDecl *decl) {

appendContextOf(decl);
appendDeclName(decl);
appendDeclType(decl, /*isFunctionMangling*/ true);
appendDeclType(decl, isAsyncHandlerBody ? AsyncHandlerBodyMangling
: FunctionMangling);
appendOperator("F");
if (decl->isStatic())
appendOperator("Z");
Expand Down
3 changes: 3 additions & 0 deletions lib/SIL/IR/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,9 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
case SILDeclRef::ManglingKind::DynamicThunk:
SKind = ASTMangler::SymbolKind::DynamicThunk;
break;
case SILDeclRef::ManglingKind::AsyncHandlerBody:
SKind = ASTMangler::SymbolKind::AsyncHandlerBody;
break;
}

switch (kind) {
Expand Down
5 changes: 5 additions & 0 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ SILGenModule::getResumeUnsafeThrowingContinuationWithError() {
ResumeUnsafeThrowingContinuationWithError,
"_resumeUnsafeThrowingContinuationWithError");
}
FuncDecl *
SILGenModule::getRunAsyncHandler() {
return lookupConcurrencyIntrinsic(getASTContext(), RunAsyncHandler,
"_runAsyncHandler");
}

ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
if (NSErrorConformanceToError)
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
Optional<FuncDecl*> ResumeUnsafeContinuation;
Optional<FuncDecl*> ResumeUnsafeThrowingContinuation;
Optional<FuncDecl*> ResumeUnsafeThrowingContinuationWithError;
Optional<FuncDecl*> RunAsyncHandler;

public:
SILGenModule(SILModule &M, ModuleDecl *SM);
Expand Down Expand Up @@ -492,6 +493,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
FuncDecl *getResumeUnsafeThrowingContinuation();
/// Retrieve the _Concurrency._resumeUnsafeThrowingContinuationWithError intrinsic.
FuncDecl *getResumeUnsafeThrowingContinuationWithError();
/// Retrieve the _Concurrency._runAsyncHandler intrinsic.
FuncDecl *getRunAsyncHandler();

SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
KeyPathTypeKind typeKind);
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1439,7 +1439,7 @@ static ManagedValue emitBuiltinCreateAsyncTaskFuture(
// Form the metatype of the result type.
CanType futureResultType =
Type(
MetatypeType::get(GenericTypeParamType::get(0, 0, SGF.getASTContext())))
MetatypeType::get(GenericTypeParamType::get(0, 0, SGF.getASTContext()), MetatypeRepresentation::Thick))
.subst(subs)->getCanonicalType();
CanType anyTypeType = ExistentialMetatypeType::get(
ProtocolCompositionType::get(ctx, { }, false))->getCanonicalType();
Expand Down
62 changes: 57 additions & 5 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,11 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc());
prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd));

if (fd->isAsyncHandler()) {
// Async handlers are need to have their bodies emitted into a
// detached task.
// FIXME: Actually implement these properly.
B.createBuiltinTrap(fd->getTypecheckedBody());
if (fd->isAsyncHandler() &&
// If F.isAsync() we are emitting the asyncHandler body and not the
// original asyncHandler.
!F.isAsync()) {
emitAsyncHandler(fd);
} else {
emitStmt(fd->getTypecheckedBody());
}
Expand All @@ -531,6 +531,58 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
mergeCleanupBlocks();
}

/// An asyncHandler function is split into two functions:
/// 1. The asyncHandler body function: it contains the body of the function, but
/// is emitted as an async function.
/// 2. The original function: it just contains
/// _runAsyncHandler(operation: asyncHandlerBodyFunction)
void SILGenFunction::emitAsyncHandler(FuncDecl *fd) {

// 1. step: create the asyncHandler body function
//
auto origFnTy = F.getLoweredFunctionType();
assert(!F.isAsync() && "an asyncHandler function cannot be async");

// The body function type is the same as the original type, just with "async".
auto bodyFnTy = origFnTy->getWithExtInfo(origFnTy->getExtInfo().withAsync());

SILDeclRef constant(fd, SILDeclRef::Kind::Func);
std::string name = constant.mangle(SILDeclRef::ManglingKind::AsyncHandlerBody);
SILLocation loc = F.getLocation();
SILGenFunctionBuilder builder(*this);

SILFunction *bodyFn = builder.createFunction(
SILLinkage::Hidden, name, bodyFnTy, F.getGenericEnvironment(),
loc, F.isBare(), F.isTransparent(),
F.isSerialized(), IsNotDynamic, ProfileCounter(), IsNotThunk,
F.getClassSubclassScope(), F.getInlineStrategy(), F.getEffectsKind());
bodyFn->setDebugScope(new (getModule()) SILDebugScope(loc, bodyFn));

SILGenFunction(SGM, *bodyFn, fd).emitFunction(fd);

// 2. step: emit the original asyncHandler function
//
Scope scope(*this, loc);

// %bodyFnRef = partial_apply %bodyFn(%originalArg0, %originalArg1, ...)
//
SmallVector<ManagedValue, 4> managedArgs;
for (SILValue arg : F.getArguments()) {
ManagedValue argVal = ManagedValue(arg, CleanupHandle::invalid());
managedArgs.push_back(argVal.copy(*this, loc));
}
auto *bodyFnRef = B.createFunctionRef(loc, bodyFn);
ManagedValue bodyFnValue =
B.createPartialApply(loc, bodyFnRef, F.getForwardingSubstitutionMap(),
managedArgs, ParameterConvention::Direct_Guaranteed);

// apply %_runAsyncHandler(%bodyFnValue)
//
FuncDecl *asyncHandlerDecl = SGM.getRunAsyncHandler();
emitApplyOfLibraryIntrinsic(loc, asyncHandlerDecl, SubstitutionMap(),
{ bodyFnValue }, SGFContext());
}

void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
MagicFunctionName = SILGenModule::getMagicFunctionName(ace);

Expand Down
2 changes: 2 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction

/// Generates code for a FuncDecl.
void emitFunction(FuncDecl *fd);
/// Generate code for @asyncHandler functions.
void emitAsyncHandler(FuncDecl *fd);
/// Emits code for a ClosureExpr.
void emitClosure(AbstractClosureExpr *ce);
/// Generates code for a class destroying destructor. This
Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ extension Task {

return Handle<T>(task: task)
}

}

public func _runAsyncHandler(operation: @escaping () async -> ()) {
_ = Task.runDetached(operation: operation)
}

// ==== Voluntary Suspension -----------------------------------------------------
Expand Down
59 changes: 59 additions & 0 deletions test/SILGen/async_handler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck %s
// REQUIRES: concurrency

func take<T>(_ t: T) async {
print(t)
}

// CHECK-LABEL: sil [ossa] @$s4test13simpleHandleryySiF : $@convention(thin) (Int) -> () {
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test13simpleHandleryySiYF : $@convention(thin) @async (Int) -> ()
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0) : $@convention(thin) @async (Int) -> ()
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
// CHECK: } // end sil function '$s4test13simpleHandleryySiF'
@asyncHandler
public func simpleHandler(_ i: Int) {
await take(i)
}

// CHECK-LABEL: sil [ossa] @$s4test20nonTrivialArgHandleryySSF : $@convention(thin) (@guaranteed String) -> () {
// CHECK: [[COPY:%[0-9]+]] = copy_value %0 : $String
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test20nonTrivialArgHandleryySSYF : $@convention(thin) @async (@guaranteed String) -> ()
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]([[COPY]]) : $@convention(thin) @async (@guaranteed String) -> ()
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
// CHECK: } // end sil function '$s4test20nonTrivialArgHandleryySSF'
@asyncHandler
public func nonTrivialArgHandler(_ s: String) {
await take(s)
}

// CHECK-LABEL: sil [ossa] @$s4test14genericHandleryyxlF : $@convention(thin) <T> (@in_guaranteed T) -> () {
// CHECK: [[TMP:%[0-9]+]] = alloc_stack $T
// CHECK: copy_addr %0 to [initialization] [[TMP]] : $*T
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test14genericHandleryyxYlF : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]<T>([[TMP]]) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
// CHECK: } // end sil function '$s4test14genericHandleryyxlF'
@asyncHandler
public func genericHandler<T>(_ t: T) {
await take(t)
}

public struct Mystruct {
// CHECK-LABEL: sil [ossa] @$s4test8MystructV13memberHandleryySiF : $@convention(method) (Int, Mystruct) -> () {
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test8MystructV13memberHandleryySiYF : $@convention(method) @async (Int, Mystruct) -> ()
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0, %1) : $@convention(method) @async (Int, Mystruct) -> ()
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
// CHECK: } // end sil function '$s4test8MystructV13memberHandleryySiF'
@asyncHandler
public func memberHandler(_ i: Int) {
await take(i)
}
}