aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/CodeGen/CGCall.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CodeGen/CGCall.cpp')
-rw-r--r--clang/lib/CodeGen/CGCall.cpp24
1 files changed, 24 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 204858e..d07fb1e 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5507,6 +5507,30 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::AlwaysInline);
}
+ // The await_suspend call performed by co_await is essentially asynchronous
+ // to the execution of the coroutine. Inlining it normally into an unsplit
+ // coroutine can cause miscompilation because the coroutine CFG misrepresents
+ // the true control flow of the program: things that happen in the
+ // await_suspend are not guaranteed to happen prior to the resumption of the
+ // coroutine, and things that happen after the resumption of the coroutine
+ // (including its exit and the potential deallocation of the coroutine frame)
+ // are not guaranteed to happen only after the end of await_suspend.
+ //
+ // The short-term solution to this problem is to mark the call as uninlinable.
+ // But we don't want to do this if the call is known to be trivial, which is
+ // very common.
+ //
+ // The long-term solution may introduce patterns like:
+ //
+ // call @llvm.coro.await_suspend(ptr %awaiter, ptr %handle,
+ // ptr @awaitSuspendFn)
+ //
+ // Then it is much easier to perform the safety analysis in the middle end.
+ // If it is safe to inline the call to awaitSuspend, we can replace it in the
+ // CoroEarly pass. Otherwise we could replace it in the CoroSplit pass.
+ if (inSuspendBlock() && mayCoroHandleEscape())
+ Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoInline);
+
// Disable inlining inside SEH __try blocks.
if (isSEHTryScope()) {
Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoInline);