Skip to content

[Clang][Coroutines] Properly emit EH code for initial suspend await_resume #73073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 22, 2023

Conversation

yuxuanchen1997
Copy link
Member

@yuxuanchen1997 yuxuanchen1997 commented Nov 22, 2023

This change aims to fix an ICE in issue #63803

The crash happens in ExitCXXTryStmt because EmitAnyExpr() adds additional cleanup to the EHScopeStack. This messes up the assumption in ExitCXXTryStmt that the top of the stack should be a EHCatchScope.

However, since we never read a value returned from await_resume() of an init suspend, we can skip the part that builds this RValue.

The code here may not be in the best shape. There's another bug that memberCallExpressionCanThrow doesn't work on the current Expr due to type mismatch. I am preparing a separate PR to address it plus some refactoring might be beneficial.

…f await_resume() is not trivially destructible
@yuxuanchen1997 yuxuanchen1997 marked this pull request as ready for review November 22, 2023 03:38
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. coroutines C++20 coroutines labels Nov 22, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 22, 2023

@llvm/pr-subscribers-coroutines
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Yuxuan Chen (yuxuanchen1997)

Changes

This change aims to fix an ICE in issue #63803

The crash happens in ExitCXXTryStmt because EmitAnyExpr() adds additional cleanup to the EHScopeStack. This messes up the assumption in ExitCXXTryStmt that the top of the stack should be a EHCatchScope.

However, since we never read a value returned from await_resume() of an init suspend, we can skip the part that builds this RValue.


Full diff: https://github.com/llvm/llvm-project/pull/73073.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGCoroutine.cpp (+9-5)
  • (added) clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp (+49)
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 7e449d5af3423cf..aaf122c0f83bc47 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -245,6 +245,15 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
                                          FPOptionsOverride(), Loc, Loc);
     TryStmt = CXXTryStmt::Create(CGF.getContext(), Loc, TryBody, Catch);
     CGF.EnterCXXTryStmt(*TryStmt);
+    CGF.EmitStmt(TryBody);
+    // We don't use EmitCXXTryStmt here. We need to store to ResumeEHVar that
+    // doesn't exist in the body.
+    Builder.CreateFlagStore(false, Coro.ResumeEHVar);
+    CGF.ExitCXXTryStmt(*TryStmt);
+    LValueOrRValue Res;
+    // We are not supposed to obtain the value from init suspend await_resume().
+    Res.RV = RValue::getIgnored();
+    return Res;
   }
 
   LValueOrRValue Res;
@@ -253,11 +262,6 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
   else
     Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
 
-  if (TryStmt) {
-    Builder.CreateFlagStore(false, Coro.ResumeEHVar);
-    CGF.ExitCXXTryStmt(*TryStmt);
-  }
-
   return Res;
 }
 
diff --git a/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp b/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp
new file mode 100644
index 000000000000000..78fc88a071d5c74
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-init-await-nontrivial-return.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-- -emit-llvm -fcxx-exceptions \
+// RUN:            -disable-llvm-passes %s -o - | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+struct NontrivialType {
+  ~NontrivialType() {}
+};
+
+struct Task {
+    struct promise_type;
+    using handle_type = std::coroutine_handle<promise_type>;
+
+    struct initial_suspend_awaiter {
+        bool await_ready() {
+            return false;
+        }
+
+        void await_suspend(handle_type h) {}
+
+        // Nontrivial type caused crash!
+        NontrivialType await_resume() noexcept {
+          return {};
+        }
+    };
+
+    struct promise_type {
+        void return_void() {}
+        void unhandled_exception() {}
+        initial_suspend_awaiter initial_suspend() { return {}; }
+        std::suspend_never final_suspend() noexcept { return {}; }
+        Task get_return_object() {
+            return Task{handle_type::from_promise(*this)};
+        }
+    };
+
+    handle_type handler;
+};
+
+Task coro_create() {
+    co_return;
+}
+
+// CHECK-LABEL: define{{.*}} ptr @_Z11coro_createv(
+// CHECK: init.ready:
+// CHECK-NEXT: store i1 true, ptr {{.*}}
+// CHECK-NEXT: call void @_ZN4Task23initial_suspend_awaiter12await_resumeEv(
+// CHECK-NEXT: call void @_ZN14NontrivialTypeD1Ev(
+// CHECK-NEXT: store i1 false, ptr {{.*}}

@yuxuanchen1997 yuxuanchen1997 self-assigned this Nov 22, 2023
Copy link
Member

@ChuanqiXu9 ChuanqiXu9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks.

@yuxuanchen1997 yuxuanchen1997 merged commit 1fad78b into llvm:main Nov 22, 2023
@yuxuanchen1997 yuxuanchen1997 deleted the cg_coro_crash_fix_3 branch November 22, 2023 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category coroutines C++20 coroutines
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Crashes when the awaiter's await_resume in initial-suspend returns a specific type
3 participants