https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/171042
>From f79fe746b475449855b59b62f7dcceb1b2ab563a Mon Sep 17 00:00:00 2001 From: Amr Hesham <[email protected]> Date: Sun, 30 Nov 2025 17:12:04 +0100 Subject: [PATCH 1/2] [CIR] Support Try catch with handler for specific type --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 +- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 3 + clang/lib/CIR/CodeGen/CIRGenCall.cpp | 2 + clang/lib/CIR/CodeGen/CIRGenException.cpp | 149 ++++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 + clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 7 + clang/test/CIR/CodeGen/try-catch-tmp.cpp | 56 ++++++- 7 files changed, 206 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index fcc7585cf81a5..710a91320a155 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -802,7 +802,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [ //===----------------------------------------------------------------------===// defvar CIR_YieldableScopes = [ - "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp", + "ArrayCtor", "ArrayDtor", "AwaitOp", "CallOp", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp" ]; @@ -2956,6 +2956,7 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { let results = (outs Optional<CIR_AnyType>:$result); let arguments = commonArgs; + let regions = (region AnyRegion:$cleanup); let builders = [ OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType, @@ -2965,6 +2966,8 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { $_state.addAttribute("callee", callee); if (resType && !isa<VoidType>(resType)) $_state.addTypes(resType); + // Create region placeholder for potential cleanups. + $_state.addRegion(); }]> ]; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 57b1a1f20aa17..c341a6139cea5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -158,6 +158,9 @@ class CIRGenCXXABI { /// Loads the incoming C++ this pointer as it was passed by the caller. mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf); + virtual CatchTypeInfo + getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty, + QualType catchHandlerType) = 0; virtual CatchTypeInfo getCatchAllTypeInfo(); /// Get the implicit (second) parameter that comes after the "this" pointer, diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 17f0c6dbab35c..3ef443962aaa7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -503,7 +503,9 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc, callOpWithExceptions = builder.createCallOp(callLoc, directFuncOp, cirCallArgs); + cgf.callWithExceptionCtx = callOpWithExceptions; cgf.populateCatchHandlersIfRequired(tryOp); + cgf.callWithExceptionCtx = nullptr; return callOpWithExceptions; } diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index 375828421eb1b..d5e10ccaef456 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -232,6 +232,30 @@ void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) { assert(!cir::MissingFeatures::ehCleanupScope()); } +void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup, + cir::TryOp tryOp) { + const EHPersonality &personality = EHPersonality::get(*this); + // This can always be a call because we necessarily didn't find + // anything on the EH stack which needs our help. + const char *rethrowName = personality.catchallRethrowFn; + if (rethrowName != nullptr && !isCleanup) { + cgm.errorNYI("populateUnwindResumeBlock CatchallRethrowFn"); + return; + } + + unsigned regionsNum = tryOp->getNumRegions(); + mlir::Region *unwindRegion = &tryOp->getRegion(regionsNum - 1); + mlir::Block *unwindResumeBlock = &unwindRegion->front(); + if (!unwindResumeBlock->empty()) + return; + + // Emit cir.resume into the unwind region last block + cir::CIRBaseBuilderTy::InsertPoint ip = builder.saveInsertionPoint(); + builder.setInsertionPointToStart(unwindResumeBlock); + cir::ResumeOp::create(builder, tryOp.getLoc()); + builder.restoreInsertionPoint(ip); +} + mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) { if (s.getTryBlock()->body_empty()) return mlir::LogicalResult::success(); @@ -332,21 +356,88 @@ CIRGenFunction::emitCXXTryStmtUnderScope(const CXXTryStmt &s) { return mlir::success(); } +/// Emit the structure of the dispatch block for the given catch scope. +/// It is an invariant that the dispatch block already exists. +static void emitCatchDispatchBlock(CIRGenFunction &cgf, + EHCatchScope &catchScope, cir::TryOp tryOp) { + if (EHPersonality::get(cgf).isWasmPersonality()) { + cgf.cgm.errorNYI("emitCatchDispatchBlock: WasmPersonality"); + return; + } + + if (EHPersonality::get(cgf).usesFuncletPads()) { + cgf.cgm.errorNYI("emitCatchDispatchBlock: usesFuncletPads"); + return; + } + + unsigned int numHandlers = catchScope.getNumHandlers(); + if (numHandlers == 1 && catchScope.getHandler(0).isCatchAll()) { + return; + } + + // In traditional LLVM codegen, the right handler is selected (with + // calls to eh_typeid_for) and the selector value is loaded. After that, + // blocks get connected for later codegen. In CIR, these are all + // implicit behaviors of cir.catch - not a lot of work to do. + // + // Test against each of the exception types we claim to catch. + for (unsigned i = 0;; ++i) { + assert(i < numHandlers && "ran off end of handlers!"); + const EHCatchScope::Handler &handler = catchScope.getHandler(i); + + [[maybe_unused]] mlir::TypedAttr typeValue = handler.type.rtti; + assert(handler.Type.Flags == 0 && "catch handler flags not supported"); + assert(typeValue && "fell into catch-all case!"); + + // Check for address space mismatch + assert(!cir::MissingFeatures::addressSpace()); + + // If this is the last handler, we're at the end, and the next + // block is the block for the enclosing EH scope. Make sure to call + // populateEHCatchRegions for caching it. + if (i + 1 == numHandlers) { + cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp); + return; + } + + // If the next handler is a catch-all, we're at the end, and the + // next block is that handler. + if (catchScope.getHandler(i + 1).isCatchAll()) + return; + } +} + void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp, bool isFnTryBlock) { unsigned numHandlers = s.getNumHandlers(); EHCatchScope *catchScope = ehStack.pushCatch(numHandlers); for (unsigned i = 0; i != numHandlers; ++i) { const CXXCatchStmt *catchStmt = s.getHandler(i); + mlir::Region *handler = &tryOp.getHandlerRegions()[i]; if (catchStmt->getExceptionDecl()) { - cgm.errorNYI("enterCXXTryStmt: CatchStmt with ExceptionDecl"); - return; - } + // FIXME: Dropping the reference type on the type into makes it + // impossible to correctly implement catch-by-reference + // semantics for pointers. Unfortunately, this is what all + // existing compilers do, and it's not clear that the standard + // personality routine is capable of doing this right. See C++ DR 388: + // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#388 + Qualifiers caughtTypeQuals; + QualType caughtType = cgm.getASTContext().getUnqualifiedArrayType( + catchStmt->getCaughtType().getNonReferenceType(), caughtTypeQuals); + if (caughtType->isObjCObjectPointerType()) { + cgm.errorNYI("enterCXXTryStmt: caughtType ObjCObjectPointerType"); + return; + } - // No exception decl indicates '...', a catch-all. - mlir::Region *handler = &tryOp.getHandlerRegions()[i]; - catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler, - s.getHandler(i)); + CatchTypeInfo typeInfo = cgm.getCXXABI().getAddrOfCXXCatchHandlerType( + getLoc(catchStmt->getSourceRange()), caughtType, + catchStmt->getCaughtType()); + catchScope->setHandler(i, typeInfo, handler, catchStmt); + } else { + // No exception decl indicates '...', a catch-all. + catchScope->setHandler(i, cgm.getCXXABI().getCatchAllTypeInfo(), handler, + s.getHandler(i)); + } // Under async exceptions, catch(...) needs to catch HW exception too // Mark scope with SehTryBegin as a SEH __try scope @@ -385,6 +476,9 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) { return; } + // Emit the structure of the EH dispatch for this catch. + emitCatchDispatchBlock(*this, catchScope, tryOp); + // Copy the handler blocks off before we pop the EH stack. Emitting // the handlers might scribble on this memory. SmallVector<EHCatchScope::Handler> handlers(catchScope.begin(), @@ -486,9 +580,11 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) { // with function local static initializers). mlir::ArrayAttr handlerTypesAttr = tryOp.getHandlerTypesAttr(); if (!handlerTypesAttr || handlerTypesAttr.empty()) { + // Accumulate all the handlers in scope. // Accumulate all the handlers in scope. bool hasCatchAll = false; - llvm::SmallVector<mlir::Attribute, 4> handlerAttrs; + llvm::SmallPtrSet<mlir::Attribute, 4> catchTypes; + llvm::SmallVector<mlir::Attribute> handlerAttrs; for (EHScopeStack::iterator i = ehStack.begin(), e = ehStack.end(); i != e; ++i) { switch (i->getKind()) { @@ -521,8 +617,10 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) { break; } - cgm.errorNYI("emitLandingPad: non catch-all"); - return; + // Check whether we already have a handler for this type. + // If not, keep track to later add to catch op. + if (catchTypes.insert(handler.type.rtti).second) + handlerAttrs.push_back(handler.type.rtti); } if (hasCatchAll) @@ -531,9 +629,12 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) { if (hasCatchAll) { handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext())); - } else { - cgm.errorNYI("emitLandingPad: non catch-all"); - return; + } + + // If there's no catch_all, attach the unwind region. This needs to be the + // last region in the TryOp catch list. + if (!hasCatchAll) { + handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext())); } // Add final array of clauses into TryOp. @@ -558,6 +659,13 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope, return; } + // The dispatch block for the end of the scope chain is a block that + // just resumes unwinding. + if (scope == ehStack.stable_end()) { + populateUnwindResumeBlock(/*isCleanup=*/true, tryOp); + return; + } + // Otherwise, we should look at the actual scope. EHScope &ehScope = *ehStack.find(scope); bool mayThrow = ehScope.mayThrow(); @@ -575,16 +683,25 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope, if (!mayThrow) { switch (ehScope.getKind()) { case EHScope::Catch: { + mayThrow = true; + // LLVM does some optimization with branches here, CIR just keep track of // the corresponding calls. EHCatchScope &catchScope = cast<EHCatchScope>(ehScope); if (catchScope.getNumHandlers() == 1 && catchScope.getHandler(0).isCatchAll()) { - mayThrow = true; break; } - cgm.errorNYI("getEHDispatchBlock: mayThrow non-catch all"); - return; + + assert(callWithExceptionCtx && "expected call information"); + { + mlir::OpBuilder::InsertionGuard guard(builder); + assert(callWithExceptionCtx.getCleanup().empty() && + "one per call: expected empty region at this point"); + builder.createBlock(&callWithExceptionCtx.getCleanup()); + builder.createYield(callWithExceptionCtx.getLoc()); + } + break; } case EHScope::Cleanup: { cgm.errorNYI("getEHDispatchBlock: mayThrow & cleanup"); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 0df812bcfb94e..48bfcddc09009 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -969,6 +969,8 @@ class CIRGenFunction : public CIRGenTypeCache { return false; } + cir::CallOp callWithExceptionCtx = nullptr; + void populateUnwindResumeBlock(bool isCleanup, cir::TryOp tryOp); void populateEHCatchRegions(EHScopeStack::stable_iterator scope, cir::TryOp tryOp); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 7e145f2c57ce6..2e51130e271f5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -118,6 +118,13 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty) override; + CatchTypeInfo + getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty, + QualType catchHandlerType) override { + auto rtti = dyn_cast<cir::GlobalViewAttr>(getAddrOfRTTIDescriptor(loc, ty)); + assert(rtti && "expected GlobalViewAttr"); + return CatchTypeInfo{rtti, 0}; + } bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override { return true; diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp index 078447f844d9a..5f57af9876615 100644 --- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp +++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp @@ -5,7 +5,7 @@ int division(); -void calling_division_inside_try_block() { +void call_function_inside_try_catch_all() { try { division(); } catch (...) { @@ -42,3 +42,57 @@ void calling_division_inside_try_block() { // OGCG: br label %[[TRY_CONT]] // OGCG: [[TRY_CONT]]: // OGCG: ret void + +void call_function_inside_try_catch_with_exception_type() { + try { + division(); + } catch (int e) { + } +} + +// CIR: cir.scope { +// CIR: cir.try { +// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i +// CIR: cir.yield +// CIR: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] { +// CIR: cir.yield +// CIR: } unwind { +// CIR: cir.resume +// CIR: } +// CIR: } + +// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8 +// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4 +// OGCG: %[[E_ADDR:.*]] = alloca i32, align 4 +// OGCG: %[[CALL:.*]] = invoke noundef i32 @_Z8divisionv() +// OGCG: to label %[[INVOKE_NORMAL:.*]] unwind label %[[INVOKE_UNWIND:.*]] +// OGCG: [[INVOKE_NORMAL]]: +// OGCG: br label %try.cont +// OGCG: [[INVOKE_UNWIND]]: +// OGCG: %[[LANDING_PAD:.*]] = landingpad { ptr, i32 } +// OGCG: catch ptr @_ZTIi +// OGCG: %[[EXCEPTION:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0 +// OGCG: store ptr %[[EXCEPTION]], ptr %[[EXCEPTION_ADDR]], align 8 +// OGCG: %[[EH_TYPE_ID:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1 +// OGCG: store i32 %[[EH_TYPE_ID]], ptr %[[EH_TYPE_ID_ADDR]], align 4 +// OGCG: br label %[[CATCH_DISPATCH:.*]] +// OGCG: [[CATCH_DISPATCH]]: +// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4 +// OGCG: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi) +// OGCG: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[TMP_EH_TYPE_ID]], %[[EH_TYPE_ID]] +// OGCG: br i1 %[[TYPE_ID_EQ]], label %[[CATCH_EXCEPTION:.*]], label %[[EH_RESUME:.*]] +// OGCG: [[CATCH_EXCEPTION]]: +// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8 +// OGCG: %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[TMP_EXCEPTION]]) +// OGCG: %[[TMP_BEGIN_CATCH:.*]] = load i32, ptr %[[BEGIN_CATCH]], align 4 +// OGCG: store i32 %[[TMP_BEGIN_CATCH]], ptr %[[E_ADDR]], align 4 +// OGCG: call void @__cxa_end_catch() +// OGCG: br label %[[TRY_NORMA:.*]] +// OGCG: [[TRY_NORMA]]: +// OGCG: ret void +// OGCG: [[EH_RESUME]]: +// OGCG: %[[TMP_EXCEPTION:.*]] = load ptr, ptr %[[EXCEPTION_ADDR]], align 8 +// OGCG: %[[TMP_EH_TYPE_ID:.*]] = load i32, ptr %[[EH_TYPE_ID_ADDR]], align 4 +// OGCG: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[TMP_EXCEPTION]], 0 +// OGCG: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[TMP_EH_TYPE_ID]], 1 +// OGCG: resume { ptr, i32 } %[[EXCEPTION_INFO]] >From 1629f2046003539eebe6249f42bbafc4dc70431c Mon Sep 17 00:00:00 2001 From: Amr Hesham <[email protected]> Date: Sun, 7 Dec 2025 18:26:42 +0100 Subject: [PATCH 2/2] Fix compiling assert --- clang/lib/CIR/CodeGen/CIRGenException.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp index d5e10ccaef456..6716b98a9aaca 100644 --- a/clang/lib/CIR/CodeGen/CIRGenException.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp @@ -386,7 +386,7 @@ static void emitCatchDispatchBlock(CIRGenFunction &cgf, const EHCatchScope::Handler &handler = catchScope.getHandler(i); [[maybe_unused]] mlir::TypedAttr typeValue = handler.type.rtti; - assert(handler.Type.Flags == 0 && "catch handler flags not supported"); + assert(handler.type.flags == 0 && "catch handler flags not supported"); assert(typeValue && "fell into catch-all case!"); // Check for address space mismatch _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
