diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 826a4b13f5c0c..8d1a3fe10de1f 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -374,6 +374,9 @@ struct MissingFeatures { // Future CIR attributes static bool optInfoAttr() { return false; } + + // Maybe only needed for Windows exception handling + static bool currentFuncletPad() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 57b1a1f20aa17..b96d656b91e62 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -126,6 +126,9 @@ class CIRGenCXXABI { virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0; + virtual void emitBeginCatch(CIRGenFunction &cgf, + const CXXCatchStmt *catchStmt) = 0; + virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty) = 0; diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 375828421eb1b..06b3aec38daa4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -419,7 +419,8 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) { RunCleanupsScope catchScope(*this); // Initialize the catch variable and set up the cleanups. - assert(!cir::MissingFeatures::catchParamOp()); + assert(!cir::MissingFeatures::currentFuncletPad()); + cgm.getCXXABI().emitBeginCatch(*this, catchStmt); // Emit the PGO counter increment. assert(!cir::MissingFeatures::incrementProfileCounter()); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 7e145f2c57ce6..56a735e0410d7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -81,6 +81,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override; void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override; + void emitBeginCatch(CIRGenFunction &cgf, + const CXXCatchStmt *catchStmt) override; + bool useThunkForDtorVariant(const CXXDestructorDecl *dtor, CXXDtorType dt) const override { // Itanium does not emit any destructor variant as an inline thunk. @@ -2266,3 +2269,91 @@ Address CIRGenItaniumCXXABI::initializeArrayCookie(CIRGenFunction &cgf, CharUnits finalAlignment = baseAlignment.alignmentAtOffset(cookieSize); return Address(finalPtr, newPtr.getElementType(), finalAlignment); } + +namespace { +/// From traditional LLVM, useful info for LLVM lowering support: +/// A cleanup to call __cxa_end_catch. In many cases, the caught +/// exception type lets us state definitively that the thrown exception +/// type does not have a destructor. In particular: +/// - Catch-alls tell us nothing, so we have to conservatively +/// assume that the thrown exception might have a destructor. +/// - Catches by reference behave according to their base types. +/// - Catches of non-record types will only trigger for exceptions +/// of non-record types, which never have destructors. +/// - Catches of record types can trigger for arbitrary subclasses +/// of the caught type, so we have to assume the actual thrown +/// exception type might have a throwing destructor, even if the +/// caught type's destructor is trivial or nothrow. +struct CallEndCatch final : EHScopeStack::Cleanup { + CallEndCatch(bool mightThrow) : mightThrow(mightThrow) {} + bool mightThrow; + + void emit(CIRGenFunction &cgf, Flags flags) override { + if (!mightThrow) { + // Traditional LLVM codegen would emit a call to __cxa_end_catch + // here. For CIR, just let it pass since the cleanup is going + // to be emitted on a later pass when lowering the catch region. + // CGF.EmitNounwindRuntimeCall(getEndCatchFn(CGF.CGM)); + cir::YieldOp::create(cgf.getBuilder(), *cgf.currSrcLoc); + return; + } + + // Traditional LLVM codegen would emit a call to __cxa_end_catch + // here. For CIR, just let it pass since the cleanup is going + // to be emitted on a later pass when lowering the catch region. + // CGF.EmitRuntimeCallOrTryCall(getEndCatchFn(CGF.CGM)); + if (!cgf.getBuilder().getBlock()->mightHaveTerminator()) + cir::YieldOp::create(cgf.getBuilder(), *cgf.currSrcLoc); + } +}; +} // namespace + +static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Type paramTy, + bool endMightThrow) { + + auto catchParam = cir::CatchParamOp::create( + cgf.getBuilder(), cgf.getBuilder().getUnknownLoc(), paramTy); + + cgf.ehStack.pushCleanup( + NormalAndEHCleanup, + endMightThrow && !cgf.cgm.getLangOpts().AssumeNothrowExceptionDtor); + + return catchParam.getParam(); +} + +/// Begins a catch statement by initializing the catch variable and +/// calling __cxa_begin_catch. +void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf, + const CXXCatchStmt *catchStmt) { + // We have to be very careful with the ordering of cleanups here: + // C++ [except.throw]p4: + // The destruction [of the exception temporary] occurs + // immediately after the destruction of the object declared in + // the exception-declaration in the handler. + // + // So the precise ordering is: + // 1. Construct catch variable. + // 2. __cxa_begin_catch + // 3. Enter __cxa_end_catch cleanup + // 4. Enter dtor cleanup + // + // We do this by using a slightly abnormal initialization process. + // Delegation sequence: + // - ExitCXXTryStmt opens a RunCleanupsScope + // - EmitAutoVarAlloca creates the variable and debug info + // - InitCatchParam initializes the variable from the exception + // - CallBeginCatch calls __cxa_begin_catch + // - CallBeginCatch enters the __cxa_end_catch cleanup + // - EmitAutoVarCleanups enters the variable destructor cleanup + // - EmitCXXTryStmt emits the code for the catch body + // - EmitCXXTryStmt close the RunCleanupsScope + + VarDecl *catchParam = catchStmt->getExceptionDecl(); + if (!catchParam) { + callBeginCatch(cgf, cgf.getBuilder().getVoidPtrTy(), + /*endMightThrow=*/true); + return; + } + + cgf.cgm.errorNYI("emitBeginCatch: catch with exception decl"); +} diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp index 078447f844d9a..baf5d102a8b74 100644 --- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp +++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp @@ -17,6 +17,7 @@ void calling_division_inside_try_block() { // CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i // CIR: cir.yield // CIR: } catch all { +// CIR: %[[CATCH_PARAM:.*]] = cir.catch_param : !cir.ptr // CIR: cir.yield // CIR: } // CIR: }