https://github.com/mmha created https://github.com/llvm/llvm-project/pull/231046

Emit code for statements after a co_return in a new block, which may be 
reachable through a label. Unlike classic codegen, this patch still keeps 
unreachable code alive through flattening to stay closer to the source code, 
though this could still be changed.

>From 62cb9e60967d38451d3651342afca5970e609a96 Mon Sep 17 00:00:00 2001
From: Morris Hafner <[email protected]>
Date: Sun, 11 Oct 2026 22:49:43 +0000
Subject: [PATCH] [CIR] Support statements after co_return

Emit code for statements after a co_return in a new block, which may be 
reachable through a label. Unlike classic codegen, this patch still keeps 
unreachable code alive through flattening to stay closer to the source code, 
though this could still be changed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp     |  24 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  18 +-
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp          |   4 +
 .../CodeGenCoroutines/coro-fallthrough.cpp    | 499 +++++++++++++++++-
 4 files changed, 529 insertions(+), 16 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index ad34df762fe19..f432788242043 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -623,11 +623,17 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt 
&s) {
             res = mlir::failure();
         });
 
-    if (coro && !coro.getBody().empty() &&
-        !coro.getBody().back().mightHaveTerminator()) {
-      mlir::OpBuilder::InsertionGuard guard(builder);
-      builder.setInsertionPointAfter(&coro.getBody().back().back());
-      cir::YieldOp::create(builder, openCurlyLoc);
+    if (coro && !coro.getBody().empty()) {
+      mlir::Block &lastBlock = coro.getBody().back();
+      mlir::Block *prevBlock = lastBlock.getPrevNode();
+      if (lastBlock.empty() && lastBlock.hasNoPredecessors() && prevBlock &&
+          !prevBlock->empty() && isa<cir::CoReturnOp>(prevBlock->back())) {
+        lastBlock.erase();
+      } else if (!lastBlock.mightHaveTerminator()) {
+        mlir::OpBuilder::InsertionGuard guard(builder);
+        builder.setInsertionPointToEnd(&lastBlock);
+        cir::YieldOp::create(builder, openCurlyLoc);
+      }
     }
   }
 
@@ -878,7 +884,6 @@ RValue CIRGenFunction::emitCoyieldExpr(const CoyieldExpr &e,
 
 mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
   ++curCoro.data->coreturnCount;
-  curLexScope->setCoreturn();
 
   const Expr *rv = s.getOperand();
   if (rv && rv->getType()->isVoidType() && !isa<InitListExpr>(rv)) {
@@ -890,11 +895,12 @@ mlir::LogicalResult 
CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
 
   if (emitStmt(s.getPromiseCall(), /*useCurrentScope=*/true).failed())
     return mlir::failure();
-  // Create a new return block (if not existent) and add a branch to
-  // it. The actual return instruction is only inserted during current
-  // scope cleanup handling.
   mlir::Location loc = getLoc(s.getSourceRange());
   cir::CoReturnOp::create(builder, loc);
+  curLexScope->markCoreturnEmitted();
 
+  // Statements after a co_return may stil be reachable through a label.
+  // Continue codegen.
+  builder.createBlock(builder.getBlock()->getParent());
   return mlir::success();
 }
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 6ca9f27748b28..9ec11f66e670e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1563,9 +1563,9 @@ class CIRGenFunction : public CIRGenTypeCache {
 
     // On a coroutine body, the OnFallthrough sub stmt holds the handler
     // (CoreturnStmt) for control flow falling off the body. Keep track
-    // of emitted co_return in this scope and allow OnFallthrough to be
-    // skipeed.
-    bool hasCoreturnStmt = false;
+    // of a co_return emitted in this scope and not followed by a label, and
+    // allow OnFallthrough to be skipped.
+    bool hasCoreturnSinceLastLabel = false;
 
     // Only Regular is used at the moment. Support for other kinds will be
     // added as the relevant statements/expressions are upstreamed.
@@ -1617,8 +1617,16 @@ class CIRGenFunction : public CIRGenTypeCache {
     // ---
     // Coroutine tracking
     // ---
-    bool hasCoreturn() const { return hasCoreturnStmt; }
-    void setCoreturn() { hasCoreturnStmt = true; }
+    bool hasCoreturn() const { return hasCoreturnSinceLastLabel; }
+    // Record an emitted co_return for fallthrough tracking.
+    // A later label clears this state.
+    void markCoreturnEmitted() { hasCoreturnSinceLastLabel = true; }
+    // A goto can reach a label after a co_return, and the code after it, so
+    // the end of this scope and of the enclosing ones may be reachable again.
+    void clearCoreturnInEnclosingScopes() {
+      for (LexicalScope *s = this; s; s = s->parentScope)
+        s->hasCoreturnSinceLastLabel = false;
+    }
 
     // ---
     // Kind
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d06d32f5b33c0..41ae23fa19e36 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -792,6 +792,10 @@ mlir::LogicalResult CIRGenFunction::emitLabel(const 
clang::LabelDecl &d) {
 
   builder.setInsertionPointToEnd(labelBlock);
   cir::LabelOp::create(builder, getLoc(d.getSourceRange()), d.getName());
+  // In a coroutine, the label makes the end of the body reachable again even
+  // after a co_return, so the implicit `co_return;` must not be left out.
+  if (curLexScope)
+    curLexScope->clearCoreturnInEnclosingScopes();
   //  FIXME: emit debug info for labels, incrementProfileCounter
   assert(!cir::MissingFeatures::incrementProfileCounter());
   assert(!cir::MissingFeatures::generateDebugInfo());
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-fallthrough.cpp 
b/clang/test/CIR/CodeGenCoroutines/coro-fallthrough.cpp
index ff3e966ac3324..7ed7d47cb45b5 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-fallthrough.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-fallthrough.cpp
@@ -2,10 +2,12 @@
 // RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-NOEH
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t-eh.cir
 // RUN: FileCheck --input-file=%t-eh.cir %s -check-prefixes=CIR,CIR-EH
+// RUN: cir-opt -cir-flatten-cfg %t.cir -o %t-flat.cir
+// RUN: FileCheck --input-file=%t-flat.cir %s -check-prefix=CIR-FLAT
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm 
-disable-llvm-passes %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=OGCG,OGCG-NOEH
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=OGCG,OGCG-NOEH 
--implicit-check-not=_Z4deadv
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -emit-llvm -disable-llvm-passes %s -o %t-eh.ll
-// RUN: FileCheck --input-file=%t-eh.ll %s -check-prefixes=OGCG,OGCG-EH
+// RUN: FileCheck --input-file=%t-eh.ll %s -check-prefixes=OGCG,OGCG-EH 
--implicit-check-not=_Z4deadv
 
 // When the body of a coroutine falls off its end, the implicit `co_return;`
 // runs after the body: the body's local variables are destroyed first, then
@@ -32,7 +34,19 @@ struct Task {
   };
 };
 
+struct IntTask {
+  struct promise_type {
+    IntTask get_return_object() noexcept;
+    std::suspend_always initial_suspend() noexcept;
+    std::suspend_always final_suspend() noexcept;
+    void return_value(int);
+    void unhandled_exception();
+  };
+};
+
 S make();
+void dead();
+void live();
 
 Task falls_off_end() {
   co_await std::suspend_always{};
@@ -55,6 +69,8 @@ Task falls_off_end() {
 // CIR-NEXT:        }
 // CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
 // CIR-NEXT:        cir.co_return
+// CIR-EH-NEXT:   ^bb1:
+// CIR-EH-NEXT:     cir.yield
 // CIR-EH-NEXT:   } catch all
 // CIR:         }, finalSuspend : {
 
@@ -94,6 +110,8 @@ Task falls_off_end_with_extended_temporary() {
 // CIR-NEXT:        }
 // CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
 // CIR-NEXT:        cir.co_return
+// CIR-EH-NEXT:   ^bb1:
+// CIR-EH-NEXT:     cir.yield
 // CIR-EH-NEXT:   } catch all
 // CIR:         }, finalSuspend : {
 
@@ -138,6 +156,8 @@ Task co_return_in_if(bool b) {
 // CIR-NEXT:        }
 // CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
 // CIR-NEXT:        cir.co_return
+// CIR-EH-NEXT:   ^bb1:
+// CIR-EH-NEXT:     cir.yield
 // CIR-EH-NEXT:   } catch all
 // CIR:         }, finalSuspend : {
 
@@ -174,6 +194,8 @@ Task co_return_at_end() {
 // CIR:             cir.cleanup.scope {
 // CIR-NEXT:          cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
 // CIR-NEXT:          cir.co_return
+// CIR-NEXT:        ^bb1:
+// CIR-NEXT:          cir.yield
 // CIR-NOEH-NEXT:   } cleanup normal {
 // CIR-EH-NEXT:     } cleanup all {
 // CIR-NEXT:          cir.call @_ZN1SD1Ev(%[[S]])
@@ -191,3 +213,476 @@ Task co_return_at_end() {
 // OGCG-EH-NEXT:   call void @_ZN1SD1Ev(
 // OGCG-NOT:     return_void
 // OGCG:         br label %coro.final
+
+// Statements after a co_return with no label are kept despite them being
+// unreachable. Classic codegen drops them.
+
+IntTask co_return_value(int v) {
+  co_return v + 1;
+  dead();
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z15co_return_valuei(
+// CIR:         }, body : {
+// CIR-EH:        cir.try {
+// CIR:             cir.call @_ZN7IntTask12promise_type12return_valueEi(
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.call @_Z4deadv()
+// CIR-NEXT:        cir.yield
+// CIR-EH-NEXT:   } catch all
+// CIR:         }, finalSuspend : {
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z15co_return_valuei(
+// CIR-FLAT:         cir.call @_ZN7IntTask12promise_type12return_valueEi(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+
+// OGCG-LABEL: define {{.*}} @_Z15co_return_valuei(
+// OGCG:         {{call|invoke}} void 
@_ZN7IntTask12promise_type12return_valueEi(
+
+// A co_return at the top level of the body still suppresses the implicit
+// co_return when unreachable statements follow it.
+
+Task co_return_void() {
+  co_return;
+  S s;
+  dead();
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z14co_return_voidv(
+// CIR:         }, body : {
+// CIR-EH:        cir.try {
+// CIR:             cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.cleanup.scope {
+// CIR-NEXT:          cir.call @_Z4deadv()
+// CIR-NEXT:          cir.yield
+// CIR-NOEH-NEXT:   } cleanup normal {
+// CIR-EH-NEXT:     } cleanup all {
+// CIR-NEXT:          cir.call @_ZN1SD1Ev(
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.yield
+// CIR-EH-NEXT:   } catch all
+// CIR-NOT:       @_ZN4Task12promise_type11return_voidEv
+// CIR:         }, finalSuspend : {
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z14co_return_voidv(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT:         cir.call @_Z4deadv()
+// CIR-FLAT-NOT:     @_ZN4Task12promise_type11return_voidEv
+// CIR-FLAT:       }, finalSuspend : {
+
+// OGCG-LABEL: define {{.*}} @_Z14co_return_voidv(
+// OGCG:         {{call|invoke}} void @_ZN4Task12promise_type11return_voidEv(
+// OGCG-NOT:     @_ZN1SD1Ev
+// OGCG:       coro.final:
+
+// Without unreachable statements, the new block stays empty. It is erased at
+// the end of the body region. A cir.try terminates with a cir.yield.
+
+Task co_return_at_end_without_cleanup() {
+  co_await std::suspend_always{};
+  co_return;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z32co_return_at_end_without_cleanupv(
+// CIR:         }, body : {
+// CIR:             cir.await(user,
+// CIR:             cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:        cir.co_return
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   ^bb1:  // no predecessors
+// CIR-EH-NEXT:     cir.yield
+// CIR-EH-NEXT:   } catch all
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z32co_return_at_end_without_cleanupv(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  }, finalSuspend : {
+
+Task co_return_in_block() {
+  {
+    S s;
+    co_return;
+    dead();
+  }
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z18co_return_in_blockv(
+// CIR:         }, body : {
+// CIR:             cir.scope {
+// CIR:               cir.cleanup.scope {
+// CIR-NEXT:            cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:            cir.co_return
+// CIR-NEXT:          ^bb1:  // no predecessors
+// CIR-NEXT:            cir.call @_Z4deadv()
+// CIR-NEXT:            cir.yield
+// CIR-NOEH-NEXT:     } cleanup normal {
+// CIR-EH-NEXT:       } cleanup all {
+// CIR-NEXT:            cir.call @_ZN1SD1Ev(
+// CIR-NEXT:            cir.yield
+// CIR-NEXT:          }
+// CIR-NEXT:        }
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z18co_return_in_blockv(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+
+// OGCG-LABEL: define {{.*}} @_Z18co_return_in_blockv(
+// OGCG:         {{call|invoke}} void @_ZN4Task12promise_type11return_voidEv(
+// OGCG:         call void @_ZN1SD1Ev(
+
+Task co_return_in_if_with_dead_code(bool b) {
+  if (b) {
+    co_return;
+    dead();
+  } else {
+    co_return;
+    dead();
+  }
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z30co_return_in_if_with_dead_codeb(
+// CIR:         }, body : {
+// CIR:             cir.if %{{.*}} {
+// CIR-NEXT:          cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:          cir.co_return
+// CIR-NEXT:        ^bb1:  // no predecessors
+// CIR-NEXT:          cir.call @_Z4deadv()
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        } else {
+// CIR-NEXT:          cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:          cir.co_return
+// CIR-NEXT:        ^bb1:  // no predecessors
+// CIR-NEXT:          cir.call @_Z4deadv()
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z30co_return_in_if_with_dead_codeb(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+
+Task co_return_in_loop(bool b) {
+  while (b) {
+    co_return;
+    dead();
+  }
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z17co_return_in_loopb(
+// CIR:         }, body : {
+// CIR:             cir.while {
+// CIR:             } do {
+// CIR-NEXT:          cir.scope {
+// CIR-NEXT:            cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:            cir.co_return
+// CIR-NEXT:          ^bb1:  // no predecessors
+// CIR-NEXT:            cir.call @_Z4deadv()
+// CIR-NEXT:            cir.yield
+// CIR-NEXT:          }
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z17co_return_in_loopb(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+
+Task co_return_in_switch(int i) {
+  switch (i) {
+  case 0:
+    co_return;
+    dead();
+  default:
+    break;
+  }
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z19co_return_in_switchi(
+// CIR:         }, body : {
+// CIR:             cir.case(equal, [#cir.int<0> : !s32i]) {
+// CIR-NEXT:          cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:          cir.co_return
+// CIR-NEXT:        ^bb1:  // no predecessors
+// CIR-NEXT:          cir.call @_Z4deadv()
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z19co_return_in_switchi(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.call @_Z4deadv()
+
+// An unreachable suspension point is emitted too.
+
+Task dead_co_await() {
+  co_return;
+  co_await std::suspend_never{};
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z13dead_co_awaitv(
+// CIR:         }, body : {
+// CIR:             cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.await(user, ready : {
+// CIR-NEXT:          cir.call @_ZNSt13suspend_never11await_readyEv(
+// CIR-NOT:       @_ZN4Task12promise_type11return_voidEv
+// CIR:         }, finalSuspend : {
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z13dead_co_awaitv(
+// CIR-FLAT:         cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.await(user, ready : {
+
+// OGCG-LABEL: define {{.*}} @_Z13dead_co_awaitv(
+// OGCG-NOT:     suspend_never
+// OGCG:       coro.final:
+
+// A label after a co_return makes the statements after it reachable through a
+// goto, and with them the end of the body. The implicit co_return is then
+// emitted, as in classic codegen, which calls return_void() on that path.
+
+Task label_after_co_return(bool b) {
+  if (b)
+    goto l;
+  co_return;
+l:
+  live();
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z21label_after_co_returnb(
+// CIR:         }, body : {
+// CIR:             cir.goto "l"
+// CIR:             cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE:[0-9]+]])
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.label "l"
+// CIR-NEXT:        cir.call @_Z4livev()
+// CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
+// CIR-NEXT:        cir.co_return
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   ^bb2:  // no predecessors
+// CIR-EH-NEXT:     cir.yield
+// CIR-EH-NEXT:   } catch all
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z21label_after_co_returnb(
+// CIR-FLAT:         cir.goto "l"
+// CIR-FLAT:         cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.label "l"
+// CIR-FLAT-NEXT:    cir.call @_Z4livev()
+// CIR-FLAT-NEXT:    cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-FLAT-NEXT:    cir.co_return
+// CIR-FLAT-NEXT:  }, finalSuspend : {
+
+// OGCG-LABEL: define {{.*}} @_Z21label_after_co_returnb(
+// OGCG:       {{^}}l:
+// OGCG-NEXT:    {{call|invoke}} void @_Z4livev()
+// OGCG:         {{call|invoke}} void @_ZN4Task12promise_type11return_voidEv(
+// OGCG:         br label %coro.final
+
+Task label_in_block_after_co_return(bool b) {
+  if (b)
+    goto l;
+  co_return;
+  {
+  l:
+    live();
+  }
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z30label_in_block_after_co_returnb(
+// CIR:         }, body : {
+// CIR:             cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE:[0-9]+]])
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.scope {
+// CIR:               cir.label "l"
+// CIR-NEXT:          cir.call @_Z4livev()
+// CIR-NEXT:          cir.yield
+// CIR-NEXT:        }
+// CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
+// CIR-NEXT:        cir.co_return
+
+// OGCG-LABEL: define {{.*}} @_Z30label_in_block_after_co_returnb(
+// OGCG:       {{^}}l:
+// OGCG-NEXT:    {{call|invoke}} void @_Z4livev()
+// OGCG:         {{call|invoke}} void @_ZN4Task12promise_type11return_voidEv(
+// OGCG:         br label %coro.final
+
+Task empty_label_after_co_return(bool b) {
+  if (b)
+    goto l;
+  co_return;
+l:;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z27empty_label_after_co_returnb(
+// CIR:         }, body : {
+// CIR:             cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE:[0-9]+]])
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb1:  // no predecessors
+// CIR-NEXT:        cir.label "l"
+// CIR-NEXT:        cir.call 
@_ZN4Task12promise_type11return_voidEv(%[[PROMISE]])
+// CIR-NEXT:        cir.co_return
+
+// OGCG-LABEL: define {{.*}} @_Z27empty_label_after_co_returnb(
+// OGCG:       {{^}}l:
+// OGCG-NEXT:    {{call|invoke}} void @_ZN4Task12promise_type11return_voidEv(
+// OGCG:         br label %coro.final
+
+// A co_return after the label makes the end of the body unreachable again.
+
+Task co_return_after_label(bool b) {
+  if (b)
+    goto l;
+  co_return;
+l:
+  live();
+  co_return;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z21co_return_after_labelb(
+// CIR:         }, body : {
+// CIR:             cir.label "l"
+// CIR-NEXT:        cir.call @_Z4livev()
+// CIR-NEXT:        cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:        cir.co_return
+// CIR-NOT:         @_ZN4Task12promise_type11return_voidEv
+// CIR:         }, finalSuspend : {
+
+// A goto, __builtin_unreachable() or __builtin_trap() at the end of the body
+// leaves an empty block. If no implicit co_return is emitted, add cir.yield.
+
+IntTask goto_at_end() {
+l:
+  co_await std::suspend_always{};
+  goto l;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z11goto_at_endv(
+// CIR:         }, body : {
+// CIR:             cir.label "l"
+// CIR:             cir.goto "l"
+// CIR-NEXT:      ^bb{{[0-9]+}}:  // no predecessors
+// CIR-NEXT:        cir.yield
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   } catch all
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z11goto_at_endv(
+// CIR-FLAT:         cir.goto "l"
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.yield
+// CIR-FLAT-NEXT:  }, finalSuspend : {
+
+Task goto_after_co_return() {
+l:
+  co_await std::suspend_always{};
+  co_return;
+  goto l;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z20goto_after_co_returnv(
+// CIR:         }, body : {
+// CIR:             cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-NEXT:        cir.co_return
+// CIR-NEXT:      ^bb{{[0-9]+}}:  // no predecessors
+// CIR-NEXT:        cir.goto "l"
+// CIR-NEXT:      ^bb{{[0-9]+}}:  // no predecessors
+// CIR-NEXT:        cir.yield
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   } catch all
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z20goto_after_co_returnv(
+// CIR-FLAT:         cir.co_return
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.goto "l"
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.yield
+// CIR-FLAT-NEXT:  }, finalSuspend : {
+
+IntTask unreachable_after_switch(int i) {
+  switch (i) {
+  case 0:
+    co_return 1;
+  default:
+    co_return 2;
+  }
+  __builtin_unreachable();
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z24unreachable_after_switchi(
+// CIR:         }, body : {
+// CIR:             cir.switch
+// CIR:             cir.unreachable
+// CIR-NEXT:      ^bb{{[0-9]+}}:  // no predecessors
+// CIR-NEXT:        cir.yield
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   } catch all
+
+// CIR-FLAT-LABEL: cir.func {{.*}} @_Z24unreachable_after_switchi(
+// CIR-FLAT:         cir.unreachable
+// CIR-FLAT-NEXT:  ^bb{{[0-9]+}}:  // no predecessors
+// CIR-FLAT-NEXT:    cir.yield
+// CIR-FLAT-NEXT:  }, finalSuspend : {
+
+IntTask trap_at_end() {
+  co_await std::suspend_always{};
+  __builtin_trap();
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z11trap_at_endv(
+// CIR:         }, body : {
+// CIR:             cir.trap
+// CIR-NEXT:      ^bb{{[0-9]+}}:  // no predecessors
+// CIR-NEXT:        cir.yield
+// CIR-NOEH-NEXT: }, finalSuspend : {
+// CIR-EH-NEXT:   } catch all
+
+#if __cpp_exceptions
+Task co_return_in_try() {
+  try {
+    co_return;
+    dead();
+  } catch (...) {
+    co_return;
+    dead();
+  }
+}
+#endif
+
+// CIR-EH-LABEL: cir.func coroutine {{.*}} @_Z16co_return_in_tryv(
+// CIR-EH:         }, body : {
+// CIR-EH:           cir.try {
+// CIR-EH:             cir.try {
+// CIR-EH-NEXT:          cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-EH-NEXT:          cir.co_return
+// CIR-EH-NEXT:        ^bb1:  // no predecessors
+// CIR-EH-NEXT:          cir.call @_Z4deadv()
+// CIR-EH-NEXT:          cir.yield
+// CIR-EH-NEXT:        } catch all
+// CIR-EH-NEXT:          cir.begin_catch
+// CIR-EH-NEXT:          cir.cleanup.scope {
+// CIR-EH-NEXT:            cir.call @_ZN4Task12promise_type11return_voidEv(
+// CIR-EH-NEXT:            cir.co_return
+// CIR-EH-NEXT:          ^bb1:  // no predecessors
+// CIR-EH-NEXT:            cir.call @_Z4deadv()
+// CIR-EH-NEXT:            cir.yield
+// CIR-EH-NEXT:          } cleanup all {
+// CIR-EH-NEXT:            cir.end_catch

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to