Skip to content

Commit e760ec2

Browse files
committed
[coro] Add support for polymorphic return typed coro.suspend.async
This allows for suspend point specific resume function types. Return values from a suspend point can therefore be modelled as arguments to the resume function. Allowing for directly passed return types. Differential Revision: https://reviews.llvm.org/D96136
1 parent c0d7a8b commit e760ec2

File tree

5 files changed

+94
-16
lines changed

5 files changed

+94
-16
lines changed

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,9 +1216,8 @@ def int_coro_async_context_dealloc : Intrinsic<[],
12161216
def int_coro_async_resume : Intrinsic<[llvm_ptr_ty],
12171217
[],
12181218
[]>;
1219-
def int_coro_suspend_async : Intrinsic<[llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
1220-
[llvm_ptr_ty, llvm_ptr_ty, llvm_vararg_ty],
1221-
[]>;
1219+
def int_coro_suspend_async
1220+
: Intrinsic<[llvm_any_ty], [llvm_ptr_ty, llvm_ptr_ty, llvm_vararg_ty], []>;
12221221
def int_coro_prepare_async : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty],
12231222
[IntrNoMem]>;
12241223
def int_coro_begin : Intrinsic<[llvm_ptr_ty], [llvm_token_ty, llvm_ptr_ty],

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
146146
struct AsyncLoweringStorage {
147147
FunctionType *AsyncFuncTy;
148148
Value *Context;
149+
CallingConv::ID AsyncCC;
149150
unsigned ContextArgNo;
150151
uint64_t ContextHeaderSize;
151152
uint64_t ContextAlignment;
@@ -208,7 +209,8 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
208209
case coro::ABI::RetconOnce:
209210
return RetconLowering.ResumePrototype->getFunctionType();
210211
case coro::ABI::Async:
211-
return AsyncLowering.AsyncFuncTy;
212+
// Not used. The function type depends on the active suspend.
213+
return nullptr;
212214
}
213215

214216
llvm_unreachable("Unknown coro::ABI enum");
@@ -245,7 +247,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
245247
case coro::ABI::RetconOnce:
246248
return RetconLowering.ResumePrototype->getCallingConv();
247249
case coro::ABI::Async:
248-
return CallingConv::Swift;
250+
return AsyncLowering.AsyncCC;
249251
}
250252
llvm_unreachable("Unknown coro::ABI enum");
251253
}

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,23 @@ void CoroCloner::handleFinalSuspend() {
454454
}
455455
}
456456

457+
static FunctionType *
458+
getFunctionTypeFromAsyncSuspend(AnyCoroSuspendInst *Suspend) {
459+
auto *AsyncSuspend = cast<CoroSuspendAsyncInst>(Suspend);
460+
auto *StructTy = cast<StructType>(AsyncSuspend->getType());
461+
auto &Context = Suspend->getParent()->getParent()->getContext();
462+
auto *VoidTy = Type::getVoidTy(Context);
463+
return FunctionType::get(VoidTy, StructTy->elements(), false);
464+
}
465+
457466
static Function *createCloneDeclaration(Function &OrigF, coro::Shape &Shape,
458467
const Twine &Suffix,
459-
Module::iterator InsertBefore) {
468+
Module::iterator InsertBefore,
469+
AnyCoroSuspendInst *ActiveSuspend) {
460470
Module *M = OrigF.getParent();
461-
auto *FnTy = Shape.getResumeFunctionType();
471+
auto *FnTy = (Shape.ABI != coro::ABI::Async)
472+
? Shape.getResumeFunctionType()
473+
: getFunctionTypeFromAsyncSuspend(ActiveSuspend);
462474

463475
Function *NewF =
464476
Function::Create(FnTy, GlobalValue::LinkageTypes::InternalLinkage,
@@ -805,7 +817,7 @@ void CoroCloner::create() {
805817
// Create the new function if we don't already have one.
806818
if (!NewF) {
807819
NewF = createCloneDeclaration(OrigF, Shape, Suffix,
808-
OrigF.getParent()->end());
820+
OrigF.getParent()->end(), ActiveSuspend);
809821
}
810822

811823
// Replace all args with undefs. The buildCoroutineFrame algorithm already
@@ -1528,8 +1540,8 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
15281540
auto *Suspend = cast<CoroSuspendAsyncInst>(Shape.CoroSuspends[Idx]);
15291541

15301542
// Create the clone declaration.
1531-
auto *Continuation =
1532-
createCloneDeclaration(F, Shape, ".resume." + Twine(Idx), NextF);
1543+
auto *Continuation = createCloneDeclaration(
1544+
F, Shape, ".resume." + Twine(Idx), NextF, Suspend);
15331545
Clones.push_back(Continuation);
15341546

15351547
// Insert a branch to a new return block immediately before the suspend
@@ -1629,7 +1641,7 @@ static void splitRetconCoroutine(Function &F, coro::Shape &Shape,
16291641

16301642
// Create the clone declaration.
16311643
auto Continuation =
1632-
createCloneDeclaration(F, Shape, ".resume." + Twine(i), NextF);
1644+
createCloneDeclaration(F, Shape, ".resume." + Twine(i), NextF, nullptr);
16331645
Clones.push_back(Continuation);
16341646

16351647
// Insert a branch to the unified return block immediately before

llvm/lib/Transforms/Coroutines/Coroutines.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,7 @@ void coro::Shape::buildFrom(Function &F) {
399399
this->AsyncLowering.ContextAlignment =
400400
AsyncId->getStorageAlignment().value();
401401
this->AsyncLowering.AsyncFuncPointer = AsyncId->getAsyncFunctionPointer();
402-
auto &Context = F.getContext();
403-
auto *Int8PtrTy = Type::getInt8PtrTy(Context);
404-
auto *VoidTy = Type::getVoidTy(Context);
405-
this->AsyncLowering.AsyncFuncTy =
406-
FunctionType::get(VoidTy, {Int8PtrTy, Int8PtrTy, Int8PtrTy}, false);
402+
this->AsyncLowering.AsyncCC = F.getCallingConv();
407403
break;
408404
};
409405
case Intrinsic::coro_id_retcon:

llvm/test/Transforms/Coroutines/coro-async.ll

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,75 @@ is_not_equal:
377377
; CHECK: musttail call swiftcc void @asyncReturn(
378378
; CHECK: ret void
379379

380+
@polymorphic_suspend_return_fp = constant <{ i32, i32 }>
381+
<{ i32 trunc ( ; Relative pointer to async function
382+
i64 sub (
383+
i64 ptrtoint (void (i8*, %async.task*, %async.actor*)* @polymorphic_suspend_return to i64),
384+
i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32 }>, <{ i32, i32 }>* @polymorphic_suspend_return_fp, i32 0, i32 1) to i64)
385+
)
386+
to i32),
387+
i32 64 ; Initial async context size without space for frame
388+
}>
389+
390+
define swiftcc void @polymorphic_suspend_return(i8* %async.ctxt, %async.task* %task, %async.actor* %actor) {
391+
entry:
392+
%tmp = alloca { i64, i64 }, align 8
393+
%proj.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp, i64 0, i32 0
394+
%proj.2 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp, i64 0, i32 1
395+
396+
%id = call token @llvm.coro.id.async(i32 128, i32 16, i32 0,
397+
i8* bitcast (<{i32, i32}>* @polymorphic_suspend_return_fp to i8*))
398+
%hdl = call i8* @llvm.coro.begin(token %id, i8* null)
399+
store i64 0, i64* %proj.1, align 8
400+
store i64 1, i64* %proj.2, align 8
401+
call void @some_may_write(i64* %proj.1)
402+
403+
; Begin lowering: apply %my_other_async_function(%args...)
404+
405+
; setup callee context
406+
%arg0 = bitcast %async.task* %task to i8*
407+
%arg1 = bitcast <{ i32, i32}>* @my_other_async_function_fp to i8*
408+
%callee_context = call i8* @llvm.coro.async.context.alloc(i8* %arg0, i8* %arg1)
409+
%callee_context.0 = bitcast i8* %callee_context to %async.ctxt*
410+
; store arguments ...
411+
; ... (omitted)
412+
413+
; store the return continuation
414+
%callee_context.return_to_caller.addr = getelementptr inbounds %async.ctxt, %async.ctxt* %callee_context.0, i32 0, i32 1
415+
%return_to_caller.addr = bitcast void(i8*, %async.task*, %async.actor*)** %callee_context.return_to_caller.addr to i8**
416+
%resume.func_ptr = call i8* @llvm.coro.async.resume()
417+
store i8* %resume.func_ptr, i8** %return_to_caller.addr
418+
419+
; store caller context into callee context
420+
%callee_context.caller_context.addr = getelementptr inbounds %async.ctxt, %async.ctxt* %callee_context.0, i32 0, i32 0
421+
store i8* %async.ctxt, i8** %callee_context.caller_context.addr
422+
%resume_proj_fun = bitcast i8*(i8*)* @resume_context_projection to i8*
423+
%callee = bitcast void(i8*, %async.task*, %async.actor*)* @asyncSuspend to i8*
424+
%res = call {i8*, i8*, i8*, i8*} (i8*, i8*, ...) @llvm.coro.suspend.async.sl_p0i8p0i8p0i8p0i8s(
425+
i8* %resume.func_ptr,
426+
i8* %resume_proj_fun,
427+
void (i8*, i8*, %async.task*, %async.actor*)* @my_async_function.my_other_async_function_fp.apply,
428+
i8* %callee, i8* %callee_context, %async.task* %task, %async.actor *%actor)
429+
430+
call void @llvm.coro.async.context.dealloc(i8* %callee_context)
431+
%continuation_task_arg = extractvalue {i8*, i8*, i8*, i8*} %res, 3
432+
%task.2 = bitcast i8* %continuation_task_arg to %async.task*
433+
%val = load i64, i64* %proj.1
434+
call void @some_user(i64 %val)
435+
%val.2 = load i64, i64* %proj.2
436+
call void @some_user(i64 %val.2)
437+
438+
tail call swiftcc void @asyncReturn(i8* %async.ctxt, %async.task* %task.2, %async.actor* %actor)
439+
call i1 (i8*, i1, ...) @llvm.coro.end.async(i8* %hdl, i1 0)
440+
unreachable
441+
}
442+
443+
; CHECK-LABEL: define swiftcc void @polymorphic_suspend_return(i8* %async.ctxt, %async.task* %task, %async.actor* %actor)
444+
; CHECK-LABEL: define internal swiftcc void @polymorphic_suspend_return.resume.0(i8* {{.*}}%0, i8* {{.*}}%1, i8* {{.*}}%2, i8* {{.*}}%3)
445+
; CHECK: bitcast i8* %3 to %async.task*
446+
; CHECK: }
447+
448+
declare { i8*, i8*, i8*, i8* } @llvm.coro.suspend.async.sl_p0i8p0i8p0i8p0i8s(i8*, i8*, ...)
380449
declare i8* @llvm.coro.prepare.async(i8*)
381450
declare token @llvm.coro.id.async(i32, i32, i32, i8*)
382451
declare i8* @llvm.coro.begin(token, i8*)

0 commit comments

Comments
 (0)