Author: Xun Li
Date: 2021-03-25T13:46:20-07:00
New Revision: c7a39c833af173a7797692757b13b4b8eb801acd

URL: 
https://github.com/llvm/llvm-project/commit/c7a39c833af173a7797692757b13b4b8eb801acd
DIFF: 
https://github.com/llvm/llvm-project/commit/c7a39c833af173a7797692757b13b4b8eb801acd.diff

LOG: [Coroutine][Clang] Force emit lifetime intrinsics for Coroutines

tl;dr Correct implementation of Corouintes requires having lifetime intrinsics 
available.

Coroutine functions are functions that can be suspended and resumed latter. To 
do so, data that need to stay alive after suspension must be put on the heap 
(i.e. the coroutine frame).
The optimizer is responsible for analyzing each AllocaInst and figure out 
whether it should be put on the stack or the frame.
In most cases, for data that we are unable to accurately analyze lifetime, we 
can just conservatively put them on the heap.
Unfortunately, there exists a few cases where certain data MUST be put on the 
stack, not on the heap. Without lifetime intrinsics, we are unable to correctly 
analyze those data's lifetime.

To dig into more details, there exists cases where at certain code points, the 
current coroutine frame may have already been destroyed. Hence no frame access 
would be allowed beyond that point.
The following is a common code pattern called "Symmetric Transfer" in coroutine:
```
auto tmp = await_suspend();
__builtin_coro_resume(tmp.address());
return;
```
In the above code example, `await_suspend()` returns a new coroutine handle, 
which we will obtain the address and then resume that coroutine. This 
essentially "transfered" from the current coroutine to a different coroutine.
During the call to `await_suspend()`, the current coroutine may be destroyed, 
which should be fine because we are not accessing any data afterwards.
However when LLVM is emitting IR for the above code, it needs to emit an 
AllocaInst for `tmp`. It will then call the `address` function on tmp. 
`address` function is a member function of coroutine, and there is no way for 
the LLVM optimizer to know that it does not capture the `tmp` pointer. So when 
the optimizer looks at it, it has to conservatively assume that `tmp` may 
escape and hence put it on the heap. Furthermore, in some cases `address` call 
would be inlined, which will generate a bunch of store/load instructions that 
move the `tmp` pointer around. Those stores will also make the compiler to 
think that `tmp` might escape.
To summarize, it's really difficult for the mid-end to figure out that the 
`tmp` data is short-lived.
I made some attempt in D98638, but it appears to be way too complex and is 
basically doing the same thing as inserting lifetime intrinsics in coroutines.

Also, for reference, we already force emitting lifetime intrinsics in O0 for 
AlwaysInliner: 
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Passes/PassBuilder.cpp#L1893

Differential Revision: https://reviews.llvm.org/D99227

Added: 
    

Modified: 
    clang/lib/CodeGen/CGCoroutine.cpp
    clang/lib/CodeGen/CodeGenFunction.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/test/CodeGenCoroutines/coro-alloc.cpp
    clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
    clang/test/CodeGenCoroutines/coro-await.cpp
    clang/test/CodeGenCoroutines/coro-dest-slot.cpp
    clang/test/CodeGenCoroutines/coro-params.cpp
    clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
    clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGCoroutine.cpp 
b/clang/lib/CodeGen/CGCoroutine.cpp
index 5c57ad0685d5..038238c84046 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -556,6 +556,8 @@ void CodeGenFunction::EmitCoroutineBody(const 
CoroutineBodyStmt &S) {
       {Builder.getInt32(NewAlign), NullPtr, NullPtr, NullPtr});
   createCoroData(*this, CurCoro, CoroId);
   CurCoro.Data->SuspendBB = RetBB;
+  assert(ShouldEmitLifetimeMarkers &&
+         "Must emit lifetime intrinsics for coroutines");
 
   // Backend is allowed to elide memory allocations, to help it, emit
   // auto mem = coro.alloc() ? 0 : ... allocation code ...;

diff  --git a/clang/lib/CodeGen/CodeGenFunction.cpp 
b/clang/lib/CodeGen/CodeGenFunction.cpp
index e3fdf54716ab..600312e15ef4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1318,10 +1318,16 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, 
llvm::Function *Fn,
 
   Stmt *Body = FD->getBody();
 
-  // Initialize helper which will detect jumps which can cause invalid lifetime
-  // markers.
-  if (Body && ShouldEmitLifetimeMarkers)
-    Bypasses.Init(Body);
+  if (Body) {
+    // Coroutines always emit lifetime markers.
+    if (isa<CoroutineBodyStmt>(Body))
+      ShouldEmitLifetimeMarkers = true;
+
+    // Initialize helper which will detect jumps which can cause invalid
+    // lifetime markers.
+    if (ShouldEmitLifetimeMarkers)
+      Bypasses.Init(Body);
+  }
 
   // Emit the standard function prologue.
   StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin());

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 11de9166335b..c14918912323 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1886,8 +1886,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// function attribute.
   unsigned LargestVectorWidth = 0;
 
-  /// True if we need emit the life-time markers.
-  const bool ShouldEmitLifetimeMarkers;
+  /// True if we need emit the life-time markers. This is initially set in
+  /// the constructor, but could be overwritten to true if this is a coroutine.
+  bool ShouldEmitLifetimeMarkers;
 
   /// Add OpenCL kernel arg metadata and the kernel attribute metadata to
   /// the function metadata.

diff  --git a/clang/test/CodeGenCoroutines/coro-alloc.cpp 
b/clang/test/CodeGenCoroutines/coro-alloc.cpp
index bf8edf012a33..c60ca5a83d48 100644
--- a/clang/test/CodeGenCoroutines/coro-alloc.cpp
+++ b/clang/test/CodeGenCoroutines/coro-alloc.cpp
@@ -245,6 +245,8 @@ extern "C" int f4(promise_on_alloc_failure_tag) {
 
   // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]]
   // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]]
+  // CHECK-NEXT: %[[Gro_CAST:.+]] = bitcast i32* %[[Gro]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %[[Gro_CAST]]) #2
   // CHECK-NEXT: br label %[[RetBB]]
 
   // CHECK: [[RetBB]]:

diff  --git a/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp 
b/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
index f8173d8f8df0..1b0c3a1c5c57 100644
--- a/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
+++ b/clang/test/CodeGenCoroutines/coro-await-resume-eh.cpp
@@ -57,12 +57,18 @@ throwing_task f() {
   // CHECK-NEXT: to label %[[RESUMEENDCATCHCONT:.+]] unwind label
   // CHECK: [[RESUMEENDCATCHCONT]]:
   // CHECK-NEXT: br label %[[RESUMETRYCONT]]
+  // CHECK: [[RESUMETRYCONT]]:
+  // CHECK-NEXT: br label %[[CLEANUP:.+]]
+  // CHECK: [[CLEANUP]]:
+  // CHECK: switch i32 %{{.+}}, label %{{.+}} [
+  // CHECK-NEXT: i32 0, label %[[CLEANUPCONT:.+]]
+  // CHECK-NEXT: ]
 
   // The variable RESUMETHREW is loaded and if true, then 'await_resume'
   // threw an exception and the coroutine body is skipped, and the final
   // suspend is executed immediately. Otherwise, the coroutine body is
   // executed, and then the final suspend.
-  // CHECK: [[RESUMETRYCONT]]:
+  // CHECK: [[CLEANUPCONT]]:
   // CHECK-NEXT: %[[RESUMETHREWLOAD:.+]] = load i1, i1* %[[RESUMETHREW]]
   // CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label 
%[[RESUMEDBODY:.+]]
 
@@ -76,7 +82,7 @@ throwing_task f() {
   // CHECK-NEXT: br label %[[COROFINAL]]
 
   // CHECK: [[COROFINAL]]:
-  // CHECK-NEXT: call void @_ZN13throwing_task12promise_type13final_suspendEv
+  // CHECK: call void @_ZN13throwing_task12promise_type13final_suspendEv
   co_return;
 }
 

diff  --git a/clang/test/CodeGenCoroutines/coro-await.cpp 
b/clang/test/CodeGenCoroutines/coro-await.cpp
index 4344cf3db4d8..3fa45d5f9ab6 100644
--- a/clang/test/CodeGenCoroutines/coro-await.cpp
+++ b/clang/test/CodeGenCoroutines/coro-await.cpp
@@ -231,7 +231,9 @@ extern "C" void TestScalar() {
 
   int Val = co_await ScalarAwaiter{};
   // CHECK: %[[Result2:.+]] = call i32 
@_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter*
-  // CHECK: store i32 %[[Result2]], i32* %Val
+  // CHECK: store i32 %[[Result2]], i32* %[[TMP_EXPRCLEANUP:.+]],
+  // CHECK: %[[TMP:.+]] = load i32, i32* %[[TMP_EXPRCLEANUP]],
+  // CHECK: store i32 %[[TMP]], i32* %Val,
 
   co_await ScalarAwaiter{};
   // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter*
@@ -312,19 +314,25 @@ void AwaitReturnsLValue(double) {
   // CHECK: %[[YVAR:.+]] = alloca %struct.RefTag*,
   // CHECK-NEXT: %[[TMP1:.+]] = alloca %struct.AwaitResumeReturnsLValue,
 
+  // CHECK: %[[TMP_EXPRCLEANUP1:.+]] = alloca %struct.RefTag*,
   // CHECK: %[[ZVAR:.+]] = alloca %struct.RefTag*,
   // CHECK-NEXT: %[[TMP2:.+]] = alloca %struct.AwaitResumeReturnsLValue,
+  // CHECK: %[[TMP_EXPRCLEANUP2:.+]] = alloca %struct.RefTag*,
 
   // CHECK: %[[RES1:.+]] = call nonnull align 1 dereferenceable({{.*}}) 
%struct.RefTag* 
@_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue*
 {{[^,]*}} %[[AVAR]])
   // CHECK-NEXT: store %struct.RefTag* %[[RES1]], %struct.RefTag** %[[XVAR]],
   RefTag& x = co_await a;
 
   // CHECK: %[[RES2:.+]] = call nonnull align 1 dereferenceable({{.*}}) 
%struct.RefTag* 
@_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue*
 {{[^,]*}} %[[TMP1]])
-  // CHECK-NEXT: store %struct.RefTag* %[[RES2]], %struct.RefTag** %[[YVAR]],
+  // CHECK-NEXT: store %struct.RefTag* %[[RES2]], %struct.RefTag** 
%[[TMP_EXPRCLEANUP1]],
+  // CHECK: %[[LOAD_TMP1:.+]] = load %struct.RefTag*, %struct.RefTag** 
%[[TMP_EXPRCLEANUP1]],
+  // CHECK: store %struct.RefTag* %[[LOAD_TMP1]], %struct.RefTag** %[[YVAR]],
 
   RefTag& y = co_await AwaitResumeReturnsLValue{};
   // CHECK: %[[RES3:.+]] = call nonnull align 1 dereferenceable({{.*}}) 
%struct.RefTag* 
@_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue*
 {{[^,]*}} %[[TMP2]])
-  // CHECK-NEXT: store %struct.RefTag* %[[RES3]], %struct.RefTag** %[[ZVAR]],
+  // CHECK-NEXT: store %struct.RefTag* %[[RES3]], %struct.RefTag** 
%[[TMP_EXPRCLEANUP2]],
+  // CHECK: %[[LOAD_TMP2:.+]] = load %struct.RefTag*, %struct.RefTag** 
%[[TMP_EXPRCLEANUP2]],
+  // CHECK: store %struct.RefTag* %[[LOAD_TMP2]], %struct.RefTag** %[[ZVAR]],
   RefTag& z = co_yield 42;
 }
 

diff  --git a/clang/test/CodeGenCoroutines/coro-dest-slot.cpp 
b/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
index 0c8ef6b04582..762fa1aede3c 100644
--- a/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
+++ b/clang/test/CodeGenCoroutines/coro-dest-slot.cpp
@@ -17,10 +17,24 @@ struct coro {
 extern "C" coro f(int) { co_return; }
 // Verify that cleanup.dest.slot is eliminated in a coroutine.
 // CHECK-LABEL: f(
+// CHECK: %[[INIT_SUSPEND:.+]] = call i8 @llvm.coro.suspend(
+// CHECK-NEXT: switch i8 %[[INIT_SUSPEND]], label
+// CHECK-NEXT:   i8 0, label %[[INIT_READY:.+]]
+// CHECK-NEXT:   i8 1, label %[[INIT_CLEANUP:.+]]
+// CHECK-NEXT: ]
+// CHECK: %[[CLEANUP_DEST0:.+]] = phi i32 [ 0, %[[INIT_READY]] ], [ 2, 
%[[INIT_CLEANUP]] ]
+
+// CHECK: %[[FINAL_SUSPEND:.+]] = call i8 @llvm.coro.suspend(
+// CHECK-NEXT: switch i8 %29, label %coro.ret [
+// CHECK-NEXT:   i8 0, label %[[FINAL_READY:.+]]
+// CHECK-NEXT:   i8 1, label %[[FINAL_CLEANUP:.+]]
+// CHECK-NEXT: ]
+
 // CHECK: call void 
@_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(
-// CHECK: %[[CLEANUP_DEST:.+]] = phi i32 [ 0, %{{.+}} ], [ 2, %{{.+}} ], [ 2, 
%{{.+}} ]
+// CHECK: %[[CLEANUP_DEST1:.+]] = phi i32 [ 0, %[[FINAL_READY]] ], [ 2, 
%[[FINAL_CLEANUP]] ]
+// CHECK: %[[CLEANUP_DEST2:.+]] = phi i32 [ %[[CLEANUP_DEST0]], %{{.+}} ], [ 
%[[CLEANUP_DEST1]], %{{.+}} ], [ 0, %{{.+}} ]
 // CHECK: call i8* @llvm.coro.free(
-// CHECK: switch i32 %cleanup.dest.slot.0, label %{{.+}} [
+// CHECK: switch i32 %[[CLEANUP_DEST2]], label %{{.+}} [
 // CHECK-NEXT: i32 0
 // CHECK-NEXT: i32 2
 // CHECK-NEXT: ]

diff  --git a/clang/test/CodeGenCoroutines/coro-params.cpp 
b/clang/test/CodeGenCoroutines/coro-params.cpp
index d69d3103e39a..12332cd793c4 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -70,7 +70,11 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
 
   // CHECK: call i8* @llvm.coro.begin(
   // CHECK: call void @_ZN8MoveOnlyC1EOS_(%struct.MoveOnly* {{[^,]*}} 
%[[MoCopy]], %struct.MoveOnly* nonnull align 4 dereferenceable(4) %[[MoParam]])
+  // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* 
{{[^,]*}} %[[McCopy]], %struct.MoveAndCopy* nonnull align 4 dereferenceable(4) 
%[[McParam]]) #
+  // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, 
int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: invoke void 
@_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
 
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
@@ -89,9 +93,17 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
 
   // Destroy promise, then parameter copies:
-  // CHECK: call void 
@_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void,
 int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise) #2
+  // CHECK: call void 
@_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void,
 int, MoveOnly, MoveAndCopy>::promise_type"* {{[^,]*}} %__promise)
+  // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, 
int, MoveOnly, MoveAndCopy>::promise_type"* %__promise to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* 
{{[^,]*}} %[[McCopy]])
+  // CHECK-NEXT: bitcast %struct.MoveAndCopy* %[[McCopy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
   // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(%struct.MoveOnly* {{[^,]*}} 
%[[MoCopy]]
+  // CHECK-NEXT: bitcast %struct.MoveOnly* %[[MoCopy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
+  // CHECK-NEXT: bitcast i32* %{{.+}} to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.end.p0i8(
   // CHECK-NEXT: call i8* @llvm.coro.free(
 }
 
@@ -103,9 +115,17 @@ void dependent_params(T x, U, U y) {
   // CHECK-NEXT: %[[y_copy:.+]] = alloca %struct.B
 
   // CHECK: call i8* @llvm.coro.begin
+  // CHECK-NEXT: bitcast %struct.A* %[[x_copy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: call void @_ZN1AC1EOS_(%struct.A* {{[^,]*}} %[[x_copy]], 
%struct.A* nonnull align 4 dereferenceable(512) %x)
+  // CHECK-NEXT: bitcast %struct.B* %[[unnamed_copy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} 
%[[unnamed_copy]], %struct.B* nonnull align 4 dereferenceable(512) %0)
+  // CHECK-NEXT: %10 = bitcast %struct.B* %[[y_copy]] to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: call void @_ZN1BC1EOS_(%struct.B* {{[^,]*}} %[[y_copy]], 
%struct.B* nonnull align 4 dereferenceable(512) %y)
+  // CHECK-NEXT: bitcast %"struct.std::experimental::coroutine_traits<void, A, 
B, B>::promise_type"* %__promise to i8*
+  // CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
   // CHECK-NEXT: invoke void 
@_ZNSt12experimental16coroutine_traitsIJv1A1BS2_EE12promise_typeC1Ev(
 
   co_return;

diff  --git a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp 
b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
index 0725abc91c02..a5592b066f7a 100644
--- a/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
+++ b/clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 
-O1 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 
-O0 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
 
 #include "Inputs/coroutine.h"
 
@@ -50,8 +50,13 @@ detached_task foo() {
 
 // check that the lifetime of the coroutine handle used to obtain the address 
is contained within single basic block, and hence does not live across 
suspension points.
 // CHECK-LABEL: final.suspend:
-// CHECK:         %[[PTR1:.+]] = bitcast 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* 
%[[ADDR_TMP:.+]] to i8*
-// CHECK-NEXT:    call void @llvm.lifetime.start.p0i8(i64 8, i8* %[[PTR1]])
-// CHECK:         call i8* 
@{{.*address.*}}(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"*
 {{[^,]*}} %[[ADDR_TMP]])
-// CHECK-NEXT:    %[[PTR2:.+]] = bitcast 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[ADDR_TMP]] 
to i8*
-// CHECK-NEXT:    call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[PTR2]])
+// CHECK:         %{{.+}} = call token @llvm.coro.save(i8* null)
+// CHECK:         %[[HDL_CAST1:.+]] = bitcast 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL:.+]] to 
i8*
+// CHECK:         call void @llvm.lifetime.start.p0i8(i64 8, i8* 
%[[HDL_CAST1]])
+// CHECK:         %[[CALL:.+]] = call i8* 
@_ZN13detached_task12promise_type13final_awaiter13await_suspendENSt12experimental13coroutines_v116coroutine_handleIS0_EE(
+// CHECK:         %[[HDL_CAST2:.+]] = getelementptr inbounds 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0", 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]], i32 
0, i32 0
+// CHECK:         store i8* %[[CALL]], i8** %[[HDL_CAST2]], align 8
+// CHECK:         %[[HDL_TRANSFER:.+]] = call i8* 
@_ZNKSt12experimental13coroutines_v116coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutines_v1::coroutine_handle.0"*
 nonnull dereferenceable(8) %[[HDL]])
+// CHECK:         %[[HDL_CAST3:.+]] = bitcast 
%"struct.std::experimental::coroutines_v1::coroutine_handle.0"* %[[HDL]] to i8*
+// CHECK:         call void @llvm.lifetime.end.p0i8(i64 8, i8* %[[HDL_CAST3]])
+// CHECK:         call void @llvm.coro.resume(i8* %[[HDL_TRANSFER]])

diff  --git a/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp 
b/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
index 30cec4e5000b..f038c5b3a913 100644
--- a/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
+++ b/clang/test/CodeGenCoroutines/coro-unhandled-exception.cpp
@@ -50,6 +50,8 @@ coro_t f() {
 // CHECK: [[TRYCONT]]:
 // CHECK-NEXT: br label %[[COROFIN:.+]]
 // CHECK: [[COROFIN]]:
+// CHECK-NEXT: bitcast 
%"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-NEXT: call void @llvm.lifetime.start.p0i8(
 // CHECK-NEXT: call void 
@"?final_suspend@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@std@@XZ"(
 
 // CHECK-LPAD: @_Z1fv(
@@ -69,4 +71,6 @@ coro_t f() {
 // CHECK-LPAD: [[TRYCONT]]:
 // CHECK-LPAD: br label %[[COROFIN:.+]]
 // CHECK-LPAD: [[COROFIN]]:
+// CHECK-LPAD-NEXT: bitcast 
%"struct.std::experimental::coroutines_v1::suspend_never"* %{{.+}} to i8*
+// CHECK-LPAD-NEXT: call void @llvm.lifetime.start.p0i8(
 // CHECK-LPAD-NEXT: call void @_ZN6coro_t12promise_type13final_suspendEv(


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to