Skip to content

Commit 8e03bd3

Browse files
committed
[concurrency] SILGen: emit @asyncHandler functions.
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) rdar://problem/71247879
1 parent 326e578 commit 8e03bd3

File tree

6 files changed

+130
-5
lines changed

6 files changed

+130
-5
lines changed

include/swift/AST/ExtInfo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,10 @@ class SILExtInfo {
828828
return builder.withNoEscape(noEscape).build();
829829
}
830830

831+
SILExtInfo withAsync(bool isAsync = true) const {
832+
return builder.withAsync(isAsync).build();
833+
}
834+
831835
bool isEqualTo(SILExtInfo other, bool useClangTypes) const {
832836
return builder.isEqualTo(other.builder, useClangTypes);
833837
}

lib/SILGen/SILGen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ SILGenModule::getResumeUnsafeThrowingContinuationWithError() {
393393
ResumeUnsafeThrowingContinuationWithError,
394394
"_resumeUnsafeThrowingContinuationWithError");
395395
}
396+
FuncDecl *
397+
SILGenModule::getRunAsyncHandler() {
398+
return lookupConcurrencyIntrinsic(getASTContext(), RunAsyncHandler,
399+
"_runAsyncHandler");
400+
}
396401

397402
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
398403
if (NSErrorConformanceToError)

lib/SILGen/SILGen.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
126126
Optional<FuncDecl*> ResumeUnsafeContinuation;
127127
Optional<FuncDecl*> ResumeUnsafeThrowingContinuation;
128128
Optional<FuncDecl*> ResumeUnsafeThrowingContinuationWithError;
129+
Optional<FuncDecl*> RunAsyncHandler;
129130

130131
public:
131132
SILGenModule(SILModule &M, ModuleDecl *SM);
@@ -492,6 +493,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
492493
FuncDecl *getResumeUnsafeThrowingContinuation();
493494
/// Retrieve the _Concurrency._resumeUnsafeThrowingContinuationWithError intrinsic.
494495
FuncDecl *getResumeUnsafeThrowingContinuationWithError();
496+
/// Retrieve the _Concurrency._runAsyncHandler intrinsic.
497+
FuncDecl *getRunAsyncHandler();
495498

496499
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
497500
KeyPathTypeKind typeKind);

lib/SILGen/SILGenFunction.cpp

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,11 +517,11 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
517517
fd->getResultInterfaceType(), fd->hasThrows(), fd->getThrowsLoc());
518518
prepareEpilog(true, fd->hasThrows(), CleanupLocation(fd));
519519

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

534+
/// An asyncHandler function is split into two functions:
535+
/// 1. The asyncHandler body function: it contains the body of the function, but
536+
/// is emitted as an async function.
537+
/// 2. The original function: it just contains
538+
/// _runAsyncHandler(operation: asyncHandlerBodyFunction)
539+
void SILGenFunction::emitAsyncHandler(FuncDecl *fd) {
540+
541+
// 1. step: create the asyncHandler body function
542+
//
543+
auto origFnTy = F.getLoweredFunctionType();
544+
assert(!F.isAsync() && "an asyncHandler function cannot be async");
545+
546+
// The body function type is the same as the original type, just with "async".
547+
auto bodyFnTy = origFnTy->getWithExtInfo(origFnTy->getExtInfo().withAsync());
548+
549+
SILDeclRef constant(fd, SILDeclRef::Kind::Func);
550+
std::string name = constant.mangle(SILDeclRef::ManglingKind::AsyncHandlerBody);
551+
SILLocation loc = F.getLocation();
552+
SILGenFunctionBuilder builder(*this);
553+
554+
SILFunction *bodyFn = builder.createFunction(
555+
SILLinkage::Hidden, name, bodyFnTy, F.getGenericEnvironment(),
556+
loc, F.isBare(), F.isTransparent(),
557+
F.isSerialized(), IsNotDynamic, ProfileCounter(), IsNotThunk,
558+
F.getClassSubclassScope(), F.getInlineStrategy(), F.getEffectsKind());
559+
bodyFn->setDebugScope(new (getModule()) SILDebugScope(loc, bodyFn));
560+
561+
SILGenFunction(SGM, *bodyFn, fd).emitFunction(fd);
562+
563+
// 2. step: emit the original asyncHandler function
564+
//
565+
Scope scope(*this, loc);
566+
567+
// %bodyFnRef = partial_apply %bodyFn(%originalArg0, %originalArg1, ...)
568+
//
569+
SmallVector<ManagedValue, 4> managedArgs;
570+
for (SILValue arg : F.getArguments()) {
571+
ManagedValue argVal = ManagedValue(arg, CleanupHandle::invalid());
572+
managedArgs.push_back(argVal.copy(*this, loc));
573+
}
574+
auto *bodyFnRef = B.createFunctionRef(loc, bodyFn);
575+
ManagedValue bodyFnValue =
576+
B.createPartialApply(loc, bodyFnRef, F.getForwardingSubstitutionMap(),
577+
managedArgs, ParameterConvention::Direct_Guaranteed);
578+
579+
// apply %_runAsyncHandler(%bodyFnValue)
580+
//
581+
FuncDecl *asyncHandlerDecl = SGM.getRunAsyncHandler();
582+
emitApplyOfLibraryIntrinsic(loc, asyncHandlerDecl, SubstitutionMap(),
583+
{ bodyFnValue }, SGFContext());
584+
}
585+
534586
void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
535587
MagicFunctionName = SILGenModule::getMagicFunctionName(ace);
536588

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
623623

624624
/// Generates code for a FuncDecl.
625625
void emitFunction(FuncDecl *fd);
626+
/// Generate code for @asyncHandler functions.
627+
void emitAsyncHandler(FuncDecl *fd);
626628
/// Emits code for a ClosureExpr.
627629
void emitClosure(AbstractClosureExpr *ce);
628630
/// Generates code for a class destroying destructor. This

test/SILGen/async_handler.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
func take<T>(_ t: T) async {
5+
print(t)
6+
}
7+
8+
// CHECK-LABEL: sil [ossa] @$s4test13simpleHandleryySiF : $@convention(thin) (Int) -> () {
9+
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test13simpleHandleryySiYF : $@convention(thin) @async (Int) -> ()
10+
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0) : $@convention(thin) @async (Int) -> ()
11+
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
12+
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
13+
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
14+
// CHECK: } // end sil function '$s4test13simpleHandleryySiF'
15+
@asyncHandler
16+
public func simpleHandler(_ i: Int) {
17+
await take(i)
18+
}
19+
20+
// CHECK-LABEL: sil [ossa] @$s4test20nonTrivialArgHandleryySSF : $@convention(thin) (@guaranteed String) -> () {
21+
// CHECK: [[COPY:%[0-9]+]] = copy_value %0 : $String
22+
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test20nonTrivialArgHandleryySSYF : $@convention(thin) @async (@guaranteed String) -> ()
23+
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]([[COPY]]) : $@convention(thin) @async (@guaranteed String) -> ()
24+
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
25+
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
26+
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
27+
// CHECK: } // end sil function '$s4test20nonTrivialArgHandleryySSF'
28+
@asyncHandler
29+
public func nonTrivialArgHandler(_ s: String) {
30+
await take(s)
31+
}
32+
33+
// CHECK-LABEL: sil [ossa] @$s4test14genericHandleryyxlF : $@convention(thin) <T> (@in_guaranteed T) -> () {
34+
// CHECK: [[TMP:%[0-9]+]] = alloc_stack $T
35+
// CHECK: copy_addr %0 to [initialization] [[TMP]] : $*T
36+
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test14genericHandleryyxYlF : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
37+
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]]<T>([[TMP]]) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> ()
38+
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
39+
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
40+
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
41+
// CHECK: } // end sil function '$s4test14genericHandleryyxlF'
42+
@asyncHandler
43+
public func genericHandler<T>(_ t: T) {
44+
await take(t)
45+
}
46+
47+
public struct Mystruct {
48+
// CHECK-LABEL: sil [ossa] @$s4test8MystructV13memberHandleryySiF : $@convention(method) (Int, Mystruct) -> () {
49+
// CHECK: [[BODYFN:%[0-9]+]] = function_ref @$s4test8MystructV13memberHandleryySiYF : $@convention(method) @async (Int, Mystruct) -> ()
50+
// CHECK: [[FN:%[0-9]+]] = partial_apply [callee_guaranteed] [[BODYFN]](%0, %1) : $@convention(method) @async (Int, Mystruct) -> ()
51+
// CHECK: [[INTRINSIC:%[0-9]+]] = function_ref @$s12_Concurrency16_runAsyncHandler9operationyyyYc_tF : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
52+
// CHECK: {{.*}} = apply [[INTRINSIC]]([[FN]]) : $@convention(thin) (@guaranteed @async @callee_guaranteed () -> ()) -> ()
53+
// CHECK: destroy_value [[FN]] : $@async @callee_guaranteed () -> ()
54+
// CHECK: } // end sil function '$s4test8MystructV13memberHandleryySiF'
55+
@asyncHandler
56+
public func memberHandler(_ i: Int) {
57+
await take(i)
58+
}
59+
}

0 commit comments

Comments
 (0)