https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/229436
>From a357737d99bfe560a4dab013d47a8925282a8747 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 6 Oct 2026 07:12:56 -0700 Subject: [PATCH 1/2] [CIR] Implement ctor-try-body rethrow This came up in a test suite, but we weren't properly re-throwing exceptions when they were in the body of a constructor's try-body. This patch mirrors classic-codegen's behavior reasonably well, implementing this behavior properly. Side note: this mirrors classic codegen's behavior of including dtor-try-bodies too, but that isn't implemented yet, so those will just be an NYI for now. --- clang/lib/CIR/CodeGen/CIRGenException.cpp | 20 +++++-- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 +- clang/test/CIR/CodeGen/ctor-try-body.cpp | 70 ++++++++++++++++++++++- 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index bd3e2014756e68..14afb80967be9f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -609,9 +609,8 @@ void CIRGenFunction::emitBeginCatch(const CXXCatchStmt *catchStmt, catchStmt->getBeginLoc()); } -mlir::LogicalResult -CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s, - cxxTryBodyEmitter &bodyCallback) { +mlir::LogicalResult CIRGenFunction::emitCXXTryStmt( + const CXXTryStmt &s, cxxTryBodyEmitter &bodyCallback, bool isFnTryBlock) { mlir::Location loc = getLoc(s.getSourceRange()); // Create a scope to hold try local storage for catch params. @@ -700,6 +699,12 @@ CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s, tryOp.setHandlerTypesAttr( mlir::ArrayAttr::get(&getMLIRContext(), handlerAttrs)); + // Determine if we need an implicit rethrow for all these catch handlers; + // see the comment below. + const bool doImplicitRethrow = + isFnTryBlock && (isa<CXXConstructorDecl>(curCodeDecl) || + isa<CXXDestructorDecl>(curCodeDecl)); + // Emit the catch handler bodies. This has to be done after the try op is // created and in place so that we can find the insertion point for the // catch parameter alloca. @@ -736,8 +741,13 @@ CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s, // The currently handled exception is rethrown if control // reaches the end of a handler of the function-try-block of a // constructor or destructor. - - // TODO(cir): Handle implicit rethrow? + // + // It is important that we only do this on fallthrough and not on + // return. Note that it's illegal to put a return in a + // constructor function-try-block's catch handler (p14), so this + // really only applies to destructors. + if (doImplicitRethrow && haveInsertPoint()) + cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); // Fall out through the catch cleanups. handlerScope.forceCleanup(); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 382a0008113e22..b6dedcc913d5be 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -881,7 +881,8 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { ctorTryBodyEmitter emitter{ctor, ctorType, args, isTryBody, body}; mlir::LogicalResult bodyRes = - isTryBody ? emitCXXTryStmt(*cast<CXXTryStmt>(body), emitter) + isTryBody ? emitCXXTryStmt(*cast<CXXTryStmt>(body), emitter, + /*isFnTryBlock=*/true) : emitter(*this); // TODO(cir): propagate this result via mlir::logical result. Just diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index c6427d60a37926..f715f661f8e125 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -2159,7 +2159,8 @@ class CIRGenFunction : public CIRGenTypeCache { void emitBeginCatch(const CXXCatchStmt *catchStmt, mlir::Value ehToken); mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s, - cxxTryBodyEmitter &bodyCallback); + cxxTryBodyEmitter &bodyCallback, + bool isFnTryBlock = false); mlir::LogicalResult emitCXXTryStmt(const clang::CXXTryStmt &s); void emitCtorPrologue(const clang::CXXConstructorDecl *ctor, diff --git a/clang/test/CIR/CodeGen/ctor-try-body.cpp b/clang/test/CIR/CodeGen/ctor-try-body.cpp index 730d736ecd8b0e..83659aaf9527a0 100644 --- a/clang/test/CIR/CodeGen/ctor-try-body.cpp +++ b/clang/test/CIR/CodeGen/ctor-try-body.cpp @@ -5,6 +5,64 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM,OGCG +void mayThrow(); + +struct S { + S(int) {} + S(); + ~S() {} +}; + +S::S() try : S(1) { + mayThrow(); +} catch (...) { +} + +// CIR: cir.func {{.*}} @_ZN1SC2Ev( +// CIR: cir.scope { +// CIR: cir.try { +// CIR: cir.call @_ZN1SC2Ei( +// CIR: cir.cleanup.scope { +// CIR: cir.call @_Z8mayThrowv() : () -> () +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: cir.call @_ZN1SD2Ev({{.*}}) nothrow +// CIR: cir.yield +// CIR: } +// CIR: cir.yield +// CIR: } catch all ({{.*}}: !cir.eh_token{{.*}}) { +// CIR: %[[CATCH_TOK:.*]], %[[EXN_PTR:.*]] = cir.begin_catch {{.*}} : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) +// CIR: cir.cleanup.scope { +// CIR: cir.throw +// CIR: cir.unreachable +// CIR: ^bb1: +// CIR: cir.yield +// CIR: } cleanup all { +// CIR: cir.end_catch %[[CATCH_TOK]] : !cir.catch_token +// CIR: cir.yield +// CIR: } +// CIR: cir.yield +// CIR: } +// CIR: } +// CIR: cir.return +// CIR: } + +// LLVM: define {{.*}} void @_ZN1SC2Ev( +// LLVM: invoke void @_ZN1SC2Ei( +// LLVM: invoke void @_Z8mayThrowv() +// LLVM: call void @_ZN1SD2Ev({{.*}}) +// LLVM: call ptr @__cxa_begin_catch( +// LLVM: invoke void @__cxa_rethrow() +// LLVM: to label %[[UNREACHABLE_DEST:.*]] unwind label %[[CLEANUP_LPAD:.*]] +// LLVM: [[CLEANUP_LPAD]]: +// LLVM: landingpad { ptr, i32 } +// LLVM: cleanup +// LLVMCIR: call void @__cxa_end_catch() +// OGCG: invoke void @__cxa_end_catch() +// LLVM: resume { ptr, i32 } +// LLVM: [[UNREACHABLE_DEST]]: +// LLVM: unreachable + struct Ctor { Ctor(); }; @@ -46,12 +104,15 @@ struct HasThings : Base { // CIR-NEXT: cir.call @_Z11side_effectv() : () -> () // CIR-NEXT: cir.yield // CIR-NEXT: } catch all (%[[CATCH_ARG:.*]]: !cir.eh_token {{.*}}) { -// CIR-NEXT: %[[CATCH_TOK:.*]], %[[EX_PTR:.*]] = cir.begin_catch %[[CATCH_ARG]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) +// CIR-NEXT: %[[CATCH_TOK2:.*]], %[[EX_PTR:.*]] = cir.begin_catch %[[CATCH_ARG]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) // CIR-NEXT: cir.cleanup.scope { // CIR-NEXT: cir.call @_Z12side_effect2v() : () -> () +// CIR-NEXT: cir.throw +// CIR-NEXT: cir.unreachable +// CIR-NEXT: ^bb1: // CIR-NEXT: cir.yield // CIR-NEXT: } cleanup all { -// CIR-NEXT: cir.end_catch %[[CATCH_TOK]] : !cir.catch_token +// CIR-NEXT: cir.end_catch %[[CATCH_TOK2]] : !cir.catch_token // CIR-NEXT: cir.yield // CIR-NEXT: } // CIR-NEXT: cir.yield @@ -70,6 +131,11 @@ struct HasThings : Base { // LLVM: invoke void @_Z11side_effectv() // LLVM: call ptr @__cxa_begin_catch(ptr %{{.*}}) // LLVM: invoke void @_Z12side_effect2v() +// LLVM: invoke void @__cxa_rethrow() +// LLVM: unwind label %[[CLEANUP_LPAD2:.*]] +// LLVM: [[CLEANUP_LPAD2]]: +// LLVM: landingpad { ptr, i32 } +// LLVM: cleanup // LLVMCIR: call void @__cxa_end_catch() // OGCG: invoke void @__cxa_end_catch() }; >From 891fa4c0b710d85463ec442923c6e247b0e7a42e Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 6 Oct 2026 14:51:39 -0700 Subject: [PATCH 2/2] Fixup test mistake --- clang/lib/CIR/CodeGen/CIRGenException.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 9 +++++++++ clang/test/CIR/CodeGen/ctor-try-body.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 14afb80967be9f..29510dc4b9a95a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -746,7 +746,7 @@ mlir::LogicalResult CIRGenFunction::emitCXXTryStmt( // return. Note that it's illegal to put a return in a // constructor function-try-block's catch handler (p14), so this // really only applies to destructors. - if (doImplicitRethrow && haveInsertPoint()) + if (doImplicitRethrow && insertionPointIsReachable()) cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); // Fall out through the catch cleanups. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index f715f661f8e125..c4963806c815b4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -669,6 +669,15 @@ class CIRGenFunction : public CIRGenTypeCache { return builder.getInsertionBlock() != nullptr; } + /// True if code emitted at the builder's insertion point can be reached. + /// After emitting a terminator CIRGen opens a fresh block to continue in, so + /// the insertion block holds unreachable code whenever it is neither its + /// region's entry block nor the target of a branch. + bool insertionPointIsReachable() const { + mlir::Block *block = builder.getInsertionBlock(); + return block && (block->isEntryBlock() || !block->hasNoPredecessors()); + } + // Wrapper for function prototype sources. Wraps either a FunctionProtoType or // an ObjCMethodDecl. struct PrototypeWrapper { diff --git a/clang/test/CIR/CodeGen/ctor-try-body.cpp b/clang/test/CIR/CodeGen/ctor-try-body.cpp index 83659aaf9527a0..a0016fad95ee22 100644 --- a/clang/test/CIR/CodeGen/ctor-try-body.cpp +++ b/clang/test/CIR/CodeGen/ctor-try-body.cpp @@ -104,7 +104,7 @@ struct HasThings : Base { // CIR-NEXT: cir.call @_Z11side_effectv() : () -> () // CIR-NEXT: cir.yield // CIR-NEXT: } catch all (%[[CATCH_ARG:.*]]: !cir.eh_token {{.*}}) { -// CIR-NEXT: %[[CATCH_TOK2:.*]], %[[EX_PTR:.*]] = cir.begin_catch %[[CATCH_ARG]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) +// CIR-NEXT: %[[CATCH_TOK:.*]], %[[EX_PTR:.*]] = cir.begin_catch %[[CATCH_ARG]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>) // CIR-NEXT: cir.cleanup.scope { // CIR-NEXT: cir.call @_Z12side_effect2v() : () -> () // CIR-NEXT: cir.throw @@ -112,7 +112,7 @@ struct HasThings : Base { // CIR-NEXT: ^bb1: // CIR-NEXT: cir.yield // CIR-NEXT: } cleanup all { -// CIR-NEXT: cir.end_catch %[[CATCH_TOK2]] : !cir.catch_token +// CIR-NEXT: cir.end_catch %[[CATCH_TOK]] : !cir.catch_token // CIR-NEXT: cir.yield // CIR-NEXT: } // CIR-NEXT: cir.yield @@ -132,8 +132,8 @@ struct HasThings : Base { // LLVM: call ptr @__cxa_begin_catch(ptr %{{.*}}) // LLVM: invoke void @_Z12side_effect2v() // LLVM: invoke void @__cxa_rethrow() -// LLVM: unwind label %[[CLEANUP_LPAD2:.*]] -// LLVM: [[CLEANUP_LPAD2]]: +// LLVM: unwind label %[[CLEANUP_LPAD:.*]] +// LLVM: [[CLEANUP_LPAD]]: // LLVM: landingpad { ptr, i32 } // LLVM: cleanup // LLVMCIR: call void @__cxa_end_catch() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
