llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Morris Hafner (mmha) <details> <summary>Changes</summary> 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. --- Patch is 23.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/231046.diff 4 Files Affected: - (modified) clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp (+15-9) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+13-5) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+4) - (modified) clang/test/CIR/CodeGenCoroutines/coro-fallthrough.cpp (+497-2) ``````````diff 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: }, f... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/231046 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
