Skip to content

Commit 3bb3969

Browse files
authored
[coro] Lower llvm.coro.await.suspend.handle to resume with tail call (#89751)
The C++ standard requires that symmetric transfer from one coroutine to another is performed via a tail call. Failure to do so is a miscompile and often breaks programs by quickly overflowing the stack. Until now, the coro split pass tried to ensure this in the `addMustTailToCoroResumes()` function by searching for `llvm.coro.resume` calls to lower as tail calls if the conditions were right: the right function arguments, attributes, calling convention etc., and if a `ret void` was sure to be reached after traversal with some ad-hoc constant folding following the call. This was brittle, as the kind of implicit variants required for a tail call to happen could easily be broken by other passes (e.g. if some instruction got in between the `resume` and `ret`), see for example 9d1cb18 and 284da04. Also the logic seemed backwards: instead of searching for possible tail call candidates and doing them if the circumstances are right, it seems better to start with the intention of making the tail calls we need, and forcing the circumstances to be right. Now that we have the `llvm.coro.await.suspend.handle` intrinsic (since f786881) which corresponds exactly to symmetric transfer, change the lowering of that to also include the `resume` part, always lowered as a tail call.
1 parent b59760d commit 3bb3969

22 files changed

+168
-478
lines changed

clang/lib/CodeGen/CGCoroutine.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,11 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
278278

279279
llvm::Function *AwaitSuspendIntrinsic = CGF.CGM.getIntrinsic(AwaitSuspendIID);
280280

281-
const auto AwaitSuspendCanThrow = StmtCanThrow(S.getSuspendExpr());
281+
// SuspendHandle might throw since it also resumes the returned handle.
282+
const bool AwaitSuspendCanThrow =
283+
SuspendReturnType ==
284+
CoroutineSuspendExpr::SuspendReturnType::SuspendHandle ||
285+
StmtCanThrow(S.getSuspendExpr());
282286

283287
llvm::CallBase *SuspendRet = nullptr;
284288
// FIXME: add call attributes?
@@ -307,10 +311,7 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
307311
break;
308312
}
309313
case CoroutineSuspendExpr::SuspendReturnType::SuspendHandle: {
310-
assert(SuspendRet->getType()->isPointerTy());
311-
312-
auto ResumeIntrinsic = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_resume);
313-
Builder.CreateCall(ResumeIntrinsic, SuspendRet);
314+
assert(SuspendRet->getType()->isVoidTy());
314315
break;
315316
}
316317
}

clang/test/CodeGenCoroutines/coro-await.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,8 @@ extern "C" void TestTailcall() {
370370
// ---------------------------
371371
// Call coro.await.suspend
372372
// ---------------------------
373-
// CHECK-NEXT: %[[RESUMED:.+]] = call ptr @llvm.coro.await.suspend.handle(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @TestTailcall.__await_suspend_wrapper__await)
374-
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[RESUMED]])
373+
// Note: The call must not be nounwind since the resumed function could throw.
374+
// CHECK-NEXT: call void @llvm.coro.await.suspend.handle(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @TestTailcall.__await_suspend_wrapper__await){{$}}
375375
// CHECK-NEXT: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
376376
// CHECK-NEXT: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [
377377
// CHECK-NEXT: i8 0, label %[[READY_BB]]

clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp

Lines changed: 0 additions & 54 deletions
This file was deleted.

clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ Task bar() {
8989
// CHECK: br i1 %{{.+}}, label %[[CASE1_AWAIT_READY:.+]], label %[[CASE1_AWAIT_SUSPEND:.+]]
9090
// CHECK: [[CASE1_AWAIT_SUSPEND]]:
9191
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(ptr null)
92-
// CHECK-NEXT: %[[HANDLE1_PTR:.+]] = call ptr @llvm.coro.await.suspend.handle
93-
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[HANDLE1_PTR]])
92+
// CHECK-NEXT: call void @llvm.coro.await.suspend.handle
9493
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
9594
// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
9695
// CHECK-NEXT: i8 0, label %[[CASE1_AWAIT_READY]]
@@ -104,8 +103,7 @@ Task bar() {
104103
// CHECK: br i1 %{{.+}}, label %[[CASE2_AWAIT_READY:.+]], label %[[CASE2_AWAIT_SUSPEND:.+]]
105104
// CHECK: [[CASE2_AWAIT_SUSPEND]]:
106105
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(ptr null)
107-
// CHECK-NEXT: %[[HANDLE2_PTR:.+]] = call ptr @llvm.coro.await.suspend.handle
108-
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[HANDLE2_PTR]])
106+
// CHECK-NEXT: call void @llvm.coro.await.suspend.handle
109107
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
110108
// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
111109
// CHECK-NEXT: i8 0, label %[[CASE2_AWAIT_READY]]

llvm/docs/Coroutines.rst

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ Example:
19221922
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19231923
::
19241924

1925-
declare ptr @llvm.coro.await.suspend.handle(
1925+
declare void @llvm.coro.await.suspend.handle(
19261926
ptr <awaiter>,
19271927
ptr <handle>,
19281928
ptr <await_suspend_function>)
@@ -1967,7 +1967,9 @@ The intrinsic must be used between corresponding `coro.save`_ and
19671967
`await_suspend_function` call during `CoroSplit`_ pass.
19681968

19691969
`await_suspend_function` must return a pointer to a valid
1970-
coroutine frame, which is immediately resumed
1970+
coroutine frame. The intrinsic will be lowered to a tail call resuming the
1971+
returned coroutine frame. It will be marked `musttail` on targets that support
1972+
that. Instructions following the intrinsic will become unreachable.
19711973

19721974
Example:
19731975
""""""""
@@ -1977,11 +1979,10 @@ Example:
19771979
; before lowering
19781980
await.suspend:
19791981
%save = call token @llvm.coro.save(ptr %hdl)
1980-
%next = call ptr @llvm.coro.await.suspend.handle(
1981-
ptr %awaiter,
1982-
ptr %hdl,
1983-
ptr @await_suspend_function)
1984-
call void @llvm.coro.resume(%next)
1982+
call void @llvm.coro.await.suspend.handle(
1983+
ptr %awaiter,
1984+
ptr %hdl,
1985+
ptr @await_suspend_function)
19851986
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
19861987
...
19871988
@@ -1992,8 +1993,8 @@ Example:
19921993
%next = call ptr @await_suspend_function(
19931994
ptr %awaiter,
19941995
ptr %hdl)
1995-
call void @llvm.coro.resume(%next)
1996-
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
1996+
musttail call void @llvm.coro.resume(%next)
1997+
ret void
19971998
...
19981999
19992000
; wrapper function example

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1717,7 +1717,7 @@ def int_coro_await_suspend_bool : Intrinsic<[llvm_i1_ty],
17171717
[llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
17181718
[Throws]>;
17191719

1720-
def int_coro_await_suspend_handle : Intrinsic<[llvm_ptr_ty],
1720+
def int_coro_await_suspend_handle : Intrinsic<[],
17211721
[llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty],
17221722
[Throws]>;
17231723

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct LowererBase {
4747
ConstantPointerNull *const NullPtr;
4848

4949
LowererBase(Module &M);
50-
Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
50+
CallInst *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
5151
};
5252

5353
enum class ABI {
@@ -85,6 +85,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
8585
SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
8686
SmallVector<CallInst*, 2> SwiftErrorOps;
8787
SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
88+
SmallVector<CallInst *, 2> SymmetricTransfers;
8889

8990
// Field indexes for special fields in the switch lowering.
9091
struct SwitchFieldIndex {

0 commit comments

Comments
 (0)