[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-02-04 Thread via cfe-commits


@@ -1744,6 +1744,273 @@ a call to ``llvm.coro.suspend.retcon`` after resuming 
abnormally.
 In a yield-once coroutine, it is undefined behavior if the coroutine
 executes a call to ``llvm.coro.suspend.retcon`` after resuming in any way.
 
+.. _coro.await.suspend:
+
+'llvm.coro.await.suspend' Intrinsic
+^^
+::
+
+  declare void @llvm.coro.await.suspend(
+ptr ,
+ptr ,
+ptr )
+
+Overview:
+"
+
+The '``llvm.coro.await.suspend``' intrinsic hides C++ `await-suspend`
+block code from optimizations on presplit coroutine body 
+to avoid miscompilations. This version of intrinsic corresponds to 
+'``void awaiter.await_suspend(...)``' variant.
+
+Arguments:
+""
+
+The first argument is a pointer to `awaiter` object.
+
+The second argument is a pointer to the current coroutine's frame.
+
+The third argument is a pointer to the helper function encapsulating
+`await-suspend` logic. Its signature must be
+
+.. code-block:: llvm
+
+declare void @await_suspend_function(ptr %awaiter, ptr %hdl)
+
+Semantics:
+""
+
+The intrinsic must be used between corresponding `coro.save`_ and 
+`coro.suspend`_ calls. It is lowered to an inlined 
+`await_suspend_function` call during `CoroSplit`_ pass.
+
+Example:
+
+
+.. code-block:: llvm
+
+  ; before lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+call void @llvm.coro.await.suspend(
+ptr %awaiter,
+ptr %hdl,
+ptr @await_suspend_function)
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+...
+
+  ; after lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+; the call to await_suspend_function is inlined
+call void @await_suspend_function(
+ptr %awaiter,
+ptr %hdl)
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)   
+...
+
+  ; helper function example
+  define void @await_suspend_function(ptr %awaiter, ptr %hdl)
+entry:
+  %hdl.tmp = alloca %"struct.std::coroutine_handle"
+  %hdl.result.tmp = alloca %"struct.std::coroutine_handle"
+  %hdl.promise.tmp = alloca %"struct.std::coroutine_handle.0"
+  %hdl.promise = call ptr 
@"std::corouine_handle::from_address"(ptr %hdl)
+  %hdl.promise.tmp.dive = getelementptr inbounds 
%"struct.std::coroutine_handle.0",
+ptr %hdl.promise.tmp, i32 0, i32 0
+  %hdl.promise.tmp.dive2 = getelementptr inbounds 
%"struct.std::coroutine_handle",
+ptr %hdl.promise.tmp.dive, i32 0, i32 0
+  store ptr %hdl.promise, ptr %hdl.promise.tmp.dive2
+  call void @llvm.memcpy.p0.p0.i64(ptr %hdl.tmp, ptr %hdl.promise.tmp, i64 
8, i1 false)
+  %hdl.tmp.dive = getelementptr inbounds %"struct.std::coroutine_handle",
+ptr %hdl.tmp, i32 0, i32 0
+  %hdl.arg = load ptr, ptr %hdl.tmp.dive
+  call void @"Awaiter::await_suspend"(ptr %awaiter, ptr %hdl.arg)
+  ret void
+
+.. _coro.await.suspend.bool:
+
+'llvm.coro.await.suspend.bool' Intrinsic
+^^
+::
+
+  declare i1 @llvm.coro.await.suspend.bool(
+ptr ,
+ptr ,
+ptr )
+
+Overview:
+"
+
+The '``llvm.coro.await.suspend.bool``' intrinsic hides C++ `await-suspend`
+block code from optimizations on presplit coroutine body 
+to avoid miscompilations. This version of intrinsic corresponds to 
+'``bool awaiter.await_suspend(...)``' variant.
+
+Arguments:
+""
+
+The first argument is a pointer to `awaiter` object.
+
+The second argument is a pointer to the current coroutine's frame.
+
+The third argument is a pointer to the helper function encapsulating
+`await-suspend` logic. Its signature must be
+
+.. code-block:: llvm
+
+declare i1 @await_suspend_function(ptr %awaiter, ptr %hdl)
+
+Semantics:
+""
+
+The intrinsic must be used between corresponding `coro.save`_ and 
+`coro.suspend`_ calls. It is lowered to an inlined 
+`await_suspend_function` call during `CoroSplit`_ pass.
+
+If `await_suspend_function` call returns `true`, the current coroutine is
+immediately resumed.
+
+Example:
+
+
+.. code-block:: llvm
+
+  ; before lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+%resume = call i1 @llvm.coro.await.suspend(
+ptr %awaiter,
+ptr %hdl,
+ptr @await_suspend_function)
+br i1 %resume, %await.suspend.bool, %await.ready
+  await.suspend.bool:
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+...
+  await.ready:
+call void @"Awaiter::await_ready"(ptr %awaiter)
+...
+
+  ; after lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+; the call to await_suspend_function is inlined
+%resume = call i1 @await_suspend_function(
+ptr %awaiter,
+ptr %hdl)
+ 

[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-02-04 Thread via cfe-commits


@@ -79,6 +79,73 @@ using namespace llvm;
 
 namespace {
 
+// Created on demand if the coro-early pass has work to do.
+class Lowerer : public coro::LowererBase {
+  IRBuilder<> Builder;
+  void lowerAwaitSuspend(CoroAwaitSuspendInst *CB);
+
+public:
+  Lowerer(Module ) : LowererBase(M), Builder(Context) {}
+
+  void lowerAwaitSuspends(Function );
+};
+
+void Lowerer::lowerAwaitSuspend(CoroAwaitSuspendInst *CB) {
+  auto Helper = CB->getHelperFunction();
+  auto Awaiter = CB->getAwaiter();
+  auto FramePtr = CB->getFrame();
+
+  Builder.SetInsertPoint(CB);
+
+  CallBase *NewCall = nullptr;
+  if (auto Invoke = dyn_cast(CB)) {
+auto HelperInvoke =
+Builder.CreateInvoke(Helper, Invoke->getNormalDest(),
+ Invoke->getUnwindDest(), {Awaiter, FramePtr});
+
+HelperInvoke->setCallingConv(Invoke->getCallingConv());
+std::copy(Invoke->bundle_op_info_begin(), Invoke->bundle_op_info_end(),
+  HelperInvoke->bundle_op_info_begin());
+AttributeList NewAttributes =
+Invoke->getAttributes().removeParamAttributes(Context, 2);
+HelperInvoke->setAttributes(NewAttributes);
+HelperInvoke->setDebugLoc(Invoke->getDebugLoc());
+NewCall = HelperInvoke;
+  } else if (auto Call = dyn_cast(CB)) {
+auto HelperCall = Builder.CreateCall(Helper, {Awaiter, FramePtr});
+
+AttributeList NewAttributes =
+Call->getAttributes().removeParamAttributes(Context, 2);
+HelperCall->setAttributes(NewAttributes);
+HelperCall->setDebugLoc(Call->getDebugLoc());
+NewCall = HelperCall;
+  }
+
+  CB->replaceAllUsesWith(NewCall);
+  CB->eraseFromParent();
+
+  InlineFunctionInfo FnInfo;
+  auto InlineRes = InlineFunction(*NewCall, FnInfo);
+  assert(InlineRes.isSuccess() && "Expected inlining to succeed");
+  (void)InlineRes;
+}
+
+void Lowerer::lowerAwaitSuspends(Function ) {

fpasserby wrote:

Moved collection to the construction of Shape

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-02-04 Thread via cfe-commits


@@ -79,6 +79,73 @@ using namespace llvm;
 
 namespace {
 
+// Created on demand if the coro-early pass has work to do.
+class Lowerer : public coro::LowererBase {
+  IRBuilder<> Builder;
+  void lowerAwaitSuspend(CoroAwaitSuspendInst *CB);
+
+public:
+  Lowerer(Module ) : LowererBase(M), Builder(Context) {}
+
+  void lowerAwaitSuspends(Function );
+};
+
+void Lowerer::lowerAwaitSuspend(CoroAwaitSuspendInst *CB) {
+  auto Helper = CB->getHelperFunction();
+  auto Awaiter = CB->getAwaiter();
+  auto FramePtr = CB->getFrame();
+
+  Builder.SetInsertPoint(CB);
+
+  CallBase *NewCall = nullptr;
+  if (auto Invoke = dyn_cast(CB)) {
+auto HelperInvoke =
+Builder.CreateInvoke(Helper, Invoke->getNormalDest(),
+ Invoke->getUnwindDest(), {Awaiter, FramePtr});
+
+HelperInvoke->setCallingConv(Invoke->getCallingConv());
+std::copy(Invoke->bundle_op_info_begin(), Invoke->bundle_op_info_end(),
+  HelperInvoke->bundle_op_info_begin());
+AttributeList NewAttributes =
+Invoke->getAttributes().removeParamAttributes(Context, 2);
+HelperInvoke->setAttributes(NewAttributes);
+HelperInvoke->setDebugLoc(Invoke->getDebugLoc());
+NewCall = HelperInvoke;
+  } else if (auto Call = dyn_cast(CB)) {
+auto HelperCall = Builder.CreateCall(Helper, {Awaiter, FramePtr});
+
+AttributeList NewAttributes =
+Call->getAttributes().removeParamAttributes(Context, 2);
+HelperCall->setAttributes(NewAttributes);
+HelperCall->setDebugLoc(Call->getDebugLoc());
+NewCall = HelperCall;
+  }
+
+  CB->replaceAllUsesWith(NewCall);
+  CB->eraseFromParent();
+
+  InlineFunctionInfo FnInfo;
+  auto InlineRes = InlineFunction(*NewCall, FnInfo);
+  assert(InlineRes.isSuccess() && "Expected inlining to succeed");
+  (void)InlineRes;

fpasserby wrote:

Removed manual inlining

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-02-04 Thread via cfe-commits


@@ -5036,14 +5036,17 @@ class CoroutineSuspendExpr : public Expr {
 
   Stmt *SubExprs[SubExpr::Count];
   OpaqueValueExpr *OpaqueValue = nullptr;
+  OpaqueValueExpr *OpaqueFramePtr = nullptr;

fpasserby wrote:

Now `__builtin_coro_frame` is used.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-02-04 Thread via cfe-commits

https://github.com/fpasserby edited 
https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits

ChuanqiXu9 wrote:

BTW, in my original comment, I said:

> Then in the middle end, we can perform analysis the awaitSuspendFn to see if 
> the coroutine handle escapes or not. If it won't escape, we can replace above 
> intrinsic to the direct call. Otherwise, we will only convert them in the 
> CoroSplit phase.

But this patch only implements the otherwise part. This is not a blocking 
issue. But we need a TODO or a FIXME here.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -338,6 +386,85 @@ static QualType getCoroutineSuspendExprReturnType(const 
ASTContext ,
 }
 #endif
 
+llvm::Function *CodeGenFunction::generateAwaitSuspendHelper(
+Twine const , Twine const ,
+CoroutineSuspendExpr const , bool CanThrow) {
+  std::string FuncName = "__await_suspend_helper_";
+  FuncName += CoroName.str();
+  FuncName += '_';
+  FuncName += SuspendPointName.str();
+
+  ASTContext  = getContext();
+
+  FunctionArgList args;
+
+  ImplicitParamDecl AwaiterDecl(C, C.VoidPtrTy, ImplicitParamKind::Other);
+  ImplicitParamDecl FrameDecl(C, C.VoidPtrTy, ImplicitParamKind::Other);
+  QualType ReturnTy = S.getSuspendExpr()->getType();
+
+  if (ReturnTy->isBooleanType()) {
+ReturnTy = C.BoolTy;
+  } else if (ReturnTy->isVoidPointerType()) {
+ReturnTy = C.VoidPtrTy;
+  } else {
+ReturnTy = C.VoidTy;
+  }
+
+  args.push_back();
+  args.push_back();
+
+  const CGFunctionInfo  =
+  CGM.getTypes().arrangeBuiltinFunctionDeclaration(ReturnTy, args);
+
+  llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI);
+
+  llvm::Function *Fn = llvm::Function::Create(
+  LTy, llvm::GlobalValue::PrivateLinkage, FuncName, ());
+
+  Fn->addParamAttr(0, llvm::Attribute::AttrKind::NonNull);
+  Fn->addParamAttr(0, llvm::Attribute::AttrKind::NoUndef);
+
+  Fn->addParamAttr(1, llvm::Attribute::AttrKind::NoUndef);
+
+  Fn->setMustProgress();
+  Fn->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);
+
+  if (!CanThrow) {
+Fn->addFnAttr(llvm::Attribute::AttrKind::NoUnwind);
+  }

ChuanqiXu9 wrote:

We don't need this. If I remember correctly, LLVM can add it automatically as 
long as the await_suspend is marked noexcept.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits

https://github.com/ChuanqiXu9 edited 
https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -1553,6 +1625,9 @@ static bool hasCallsInBlocksBetween(BasicBlock *SaveBB, 
BasicBlock *ResDesBB) {
 }
 
 static bool hasCallsBetween(Instruction *Save, Instruction *ResumeOrDestroy) {
+  if (Save == ResumeOrDestroy)
+return false;

ChuanqiXu9 wrote:

How can this be true?

Oh, I see you use it in `isSimpleHelper`.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -79,6 +79,73 @@ using namespace llvm;
 
 namespace {
 
+// Created on demand if the coro-early pass has work to do.
+class Lowerer : public coro::LowererBase {
+  IRBuilder<> Builder;
+  void lowerAwaitSuspend(CoroAwaitSuspendInst *CB);
+
+public:
+  Lowerer(Module ) : LowererBase(M), Builder(Context) {}
+
+  void lowerAwaitSuspends(Function );
+};
+
+void Lowerer::lowerAwaitSuspend(CoroAwaitSuspendInst *CB) {
+  auto Helper = CB->getHelperFunction();
+  auto Awaiter = CB->getAwaiter();
+  auto FramePtr = CB->getFrame();
+
+  Builder.SetInsertPoint(CB);
+
+  CallBase *NewCall = nullptr;
+  if (auto Invoke = dyn_cast(CB)) {
+auto HelperInvoke =
+Builder.CreateInvoke(Helper, Invoke->getNormalDest(),
+ Invoke->getUnwindDest(), {Awaiter, FramePtr});
+
+HelperInvoke->setCallingConv(Invoke->getCallingConv());
+std::copy(Invoke->bundle_op_info_begin(), Invoke->bundle_op_info_end(),
+  HelperInvoke->bundle_op_info_begin());
+AttributeList NewAttributes =
+Invoke->getAttributes().removeParamAttributes(Context, 2);
+HelperInvoke->setAttributes(NewAttributes);
+HelperInvoke->setDebugLoc(Invoke->getDebugLoc());
+NewCall = HelperInvoke;
+  } else if (auto Call = dyn_cast(CB)) {
+auto HelperCall = Builder.CreateCall(Helper, {Awaiter, FramePtr});
+
+AttributeList NewAttributes =
+Call->getAttributes().removeParamAttributes(Context, 2);
+HelperCall->setAttributes(NewAttributes);
+HelperCall->setDebugLoc(Call->getDebugLoc());
+NewCall = HelperCall;
+  }
+
+  CB->replaceAllUsesWith(NewCall);
+  CB->eraseFromParent();
+
+  InlineFunctionInfo FnInfo;
+  auto InlineRes = InlineFunction(*NewCall, FnInfo);
+  assert(InlineRes.isSuccess() && "Expected inlining to succeed");
+  (void)InlineRes;
+}
+
+void Lowerer::lowerAwaitSuspends(Function ) {

ChuanqiXu9 wrote:

We can collect CoroAwaitSuspendInst during the construction of Shape. So that 
we can save one iterations of the function.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -1744,6 +1744,273 @@ a call to ``llvm.coro.suspend.retcon`` after resuming 
abnormally.
 In a yield-once coroutine, it is undefined behavior if the coroutine
 executes a call to ``llvm.coro.suspend.retcon`` after resuming in any way.
 
+.. _coro.await.suspend:
+
+'llvm.coro.await.suspend' Intrinsic
+^^
+::
+
+  declare void @llvm.coro.await.suspend(
+ptr ,
+ptr ,
+ptr )
+
+Overview:
+"
+
+The '``llvm.coro.await.suspend``' intrinsic hides C++ `await-suspend`
+block code from optimizations on presplit coroutine body 
+to avoid miscompilations. This version of intrinsic corresponds to 
+'``void awaiter.await_suspend(...)``' variant.
+
+Arguments:
+""
+
+The first argument is a pointer to `awaiter` object.
+
+The second argument is a pointer to the current coroutine's frame.
+
+The third argument is a pointer to the helper function encapsulating
+`await-suspend` logic. Its signature must be
+
+.. code-block:: llvm
+
+declare void @await_suspend_function(ptr %awaiter, ptr %hdl)
+
+Semantics:
+""
+
+The intrinsic must be used between corresponding `coro.save`_ and 
+`coro.suspend`_ calls. It is lowered to an inlined 
+`await_suspend_function` call during `CoroSplit`_ pass.
+
+Example:
+
+
+.. code-block:: llvm
+
+  ; before lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+call void @llvm.coro.await.suspend(
+ptr %awaiter,
+ptr %hdl,
+ptr @await_suspend_function)
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+...
+
+  ; after lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+; the call to await_suspend_function is inlined
+call void @await_suspend_function(
+ptr %awaiter,
+ptr %hdl)
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)   
+...
+
+  ; helper function example
+  define void @await_suspend_function(ptr %awaiter, ptr %hdl)
+entry:
+  %hdl.tmp = alloca %"struct.std::coroutine_handle"
+  %hdl.result.tmp = alloca %"struct.std::coroutine_handle"
+  %hdl.promise.tmp = alloca %"struct.std::coroutine_handle.0"
+  %hdl.promise = call ptr 
@"std::corouine_handle::from_address"(ptr %hdl)
+  %hdl.promise.tmp.dive = getelementptr inbounds 
%"struct.std::coroutine_handle.0",
+ptr %hdl.promise.tmp, i32 0, i32 0
+  %hdl.promise.tmp.dive2 = getelementptr inbounds 
%"struct.std::coroutine_handle",
+ptr %hdl.promise.tmp.dive, i32 0, i32 0
+  store ptr %hdl.promise, ptr %hdl.promise.tmp.dive2
+  call void @llvm.memcpy.p0.p0.i64(ptr %hdl.tmp, ptr %hdl.promise.tmp, i64 
8, i1 false)
+  %hdl.tmp.dive = getelementptr inbounds %"struct.std::coroutine_handle",
+ptr %hdl.tmp, i32 0, i32 0
+  %hdl.arg = load ptr, ptr %hdl.tmp.dive
+  call void @"Awaiter::await_suspend"(ptr %awaiter, ptr %hdl.arg)
+  ret void
+
+.. _coro.await.suspend.bool:
+
+'llvm.coro.await.suspend.bool' Intrinsic
+^^
+::
+
+  declare i1 @llvm.coro.await.suspend.bool(
+ptr ,
+ptr ,
+ptr )
+
+Overview:
+"
+
+The '``llvm.coro.await.suspend.bool``' intrinsic hides C++ `await-suspend`
+block code from optimizations on presplit coroutine body 
+to avoid miscompilations. This version of intrinsic corresponds to 
+'``bool awaiter.await_suspend(...)``' variant.
+
+Arguments:
+""
+
+The first argument is a pointer to `awaiter` object.
+
+The second argument is a pointer to the current coroutine's frame.
+
+The third argument is a pointer to the helper function encapsulating
+`await-suspend` logic. Its signature must be
+
+.. code-block:: llvm
+
+declare i1 @await_suspend_function(ptr %awaiter, ptr %hdl)
+
+Semantics:
+""
+
+The intrinsic must be used between corresponding `coro.save`_ and 
+`coro.suspend`_ calls. It is lowered to an inlined 
+`await_suspend_function` call during `CoroSplit`_ pass.
+
+If `await_suspend_function` call returns `true`, the current coroutine is
+immediately resumed.
+
+Example:
+
+
+.. code-block:: llvm
+
+  ; before lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+%resume = call i1 @llvm.coro.await.suspend(
+ptr %awaiter,
+ptr %hdl,
+ptr @await_suspend_function)
+br i1 %resume, %await.suspend.bool, %await.ready
+  await.suspend.bool:
+%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+...
+  await.ready:
+call void @"Awaiter::await_ready"(ptr %awaiter)
+...
+
+  ; after lowering
+  await.suspend:
+%save = call token @llvm.coro.save(ptr %hdl)
+; the call to await_suspend_function is inlined
+%resume = call i1 @await_suspend_function(
+ptr %awaiter,
+ptr %hdl)
+ 

[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -5036,14 +5036,17 @@ class CoroutineSuspendExpr : public Expr {
 
   Stmt *SubExprs[SubExpr::Count];
   OpaqueValueExpr *OpaqueValue = nullptr;
+  OpaqueValueExpr *OpaqueFramePtr = nullptr;

ChuanqiXu9 wrote:

I still think we can get rid of storing `OpaqueFramePtr` in 
`CoroutineSuspendExpr`. For example, we can call ` @llvm.coro.frame()` directly 
to get it. 

https://llvm.org/docs/Coroutines.html#llvm-coro-frame-intrinsic

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits


@@ -232,16 +237,59 @@ static LValueOrRValue 
emitSuspendExpression(CodeGenFunction , CGCoroData 
   auto *NullPtr = llvm::ConstantPointerNull::get(CGF.CGM.Int8PtrTy);
   auto *SaveCall = Builder.CreateCall(CoroSave, {NullPtr});
 
-  CGF.CurCoro.InSuspendBlock = true;
-  auto *SuspendRet = CGF.EmitScalarExpr(S.getSuspendExpr());
-  CGF.CurCoro.InSuspendBlock = false;
+  const auto AwaitSuspendCanThrow =
+  AwaitSuspendStmtCanThrow(S.getSuspendExpr());
+
+  auto SuspendHelper = CodeGenFunction(CGF.CGM).generateAwaitSuspendHelper(
+  CGF.CurFn->getName(), Prefix, S, AwaitSuspendCanThrow);
 
-  if (SuspendRet != nullptr && SuspendRet->getType()->isIntegerTy(1)) {
-// Veto suspension if requested by bool returning await_suspend.
-BasicBlock *RealSuspendBlock =
-CGF.createBasicBlock(Prefix + Twine(".suspend.bool"));
-CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock);
-CGF.EmitBlock(RealSuspendBlock);
+  llvm::CallBase *SuspendRet = nullptr;
+
+  {
+CGF.CurCoro.InSuspendBlock = true;
+
+auto FramePtrBinder = CodeGenFunction::OpaqueValueMappingData::bind(
+CGF, S.getOpaqueFramePtr(), S.getOpaqueFramePtr()->getSourceExpr());
+auto UnbindFramePtrOnExit =
+llvm::make_scope_exit([&] { FramePtrBinder.unbind(CGF); });
+
+SmallVector SuspendHelperCallArgs;
+SuspendHelperCallArgs.push_back(
+
CGF.getOrCreateOpaqueLValueMapping(S.getOpaqueValue()).getPointer(CGF));
+SuspendHelperCallArgs.push_back(
+CGF.getOrCreateOpaqueRValueMapping(S.getOpaqueFramePtr())
+.getScalarVal());
+SuspendHelperCallArgs.push_back(SuspendHelper);
+
+auto IID = llvm::Intrinsic::coro_await_suspend;
+if (S.getSuspendExpr()->getType()->isBooleanType())
+  IID = llvm::Intrinsic::coro_await_suspend_bool;
+else if (S.getSuspendExpr()->getType()->isVoidPointerType())
+  IID = llvm::Intrinsic::coro_await_suspend_handle;
+
+llvm::Function *AwaitSuspendIntrinsic = CGF.CGM.getIntrinsic(IID);
+// FIXME: add call attributes?
+if (AwaitSuspendCanThrow)
+  SuspendRet =
+  CGF.EmitCallOrInvoke(AwaitSuspendIntrinsic, SuspendHelperCallArgs);
+else
+  SuspendRet = CGF.EmitNounwindRuntimeCall(AwaitSuspendIntrinsic,
+   SuspendHelperCallArgs);
+
+CGF.CurCoro.InSuspendBlock = false;
+  }
+
+  if (SuspendRet != nullptr) {
+if (SuspendRet->getType()->isIntegerTy(1)) {
+  // Veto suspension if requested by bool returning await_suspend.
+  BasicBlock *RealSuspendBlock =
+  CGF.createBasicBlock(Prefix + Twine(".suspend.bool"));
+  CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock);
+  CGF.EmitBlock(RealSuspendBlock);
+} else if (SuspendRet->getType()->isPointerTy()) {
+  auto ResumeIntrinsic = 
CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_resume);
+  Builder.CreateCall(ResumeIntrinsic, SuspendRet);
+}

ChuanqiXu9 wrote:

```suggestion
} else
 llvm_unreachable(...);
```

nit: personal interest.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-30 Thread Chuanqi Xu via cfe-commits

https://github.com/ChuanqiXu9 edited 
https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[llvm] [clang] [coroutine] Implement llvm.coro.await.suspend intrinsic (PR #79712)

2024-01-29 Thread Chuanqi Xu via cfe-commits

https://github.com/ChuanqiXu9 commented:

Thanks for looking into this. I haven't looked it in details. Given this is 
complex, it should take a relative longer time.

Here is some quick feedbacks:
- Every time we change the non-static data members of AST nodes, we need to 
update the serializations. Otherwise, modules won't work.
- Every time we change the LLVM intrinsics, we need to change the documentation.
- Every time we change the LLVM with a functional change, we need a test in the 
LLVM part.

And there is something I haven't fully understand:
- I feel odd about `helperFunction`. I don't understand the necessity and I 
feel we should make it in the middle end.
- I feel odd about `IsSuspendNoThrow`. Can't we make it simply during the 
CodeGen time by the attribute of `await_suspend`?.
- I am wondering if we can get rid of the introduction of `OpaqueFramePtr` from 
`CoroutineSuspendExpr` either in CodeGen part or in Sema part.

But I am not sure if my feelings are correct. I need more time looking into it.

https://github.com/llvm/llvm-project/pull/79712
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits