[clang] [CIR] Support Try catch with handler for specific type (PR #171042)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Sun Dec 14 11:32:30 PST 2025
https://github.com/AmrDeveloper updated https://github.com/llvm/llvm-project/pull/171042
>From 519f4aecb7ecf8ffc19fd0db3d34b5cc29369aaf Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sun, 30 Nov 2025 17:12:04 +0100
Subject: [PATCH 1/6] [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 868b813458aae..2b5eb9738af55 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -837,7 +837,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"
];
@@ -3031,6 +3031,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,
@@ -3040,6 +3041,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 b96d656b91e62..1d5fce3036bf4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -161,6 +161,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 70812452ed999..a4fde69e8f59e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -244,6 +244,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();
@@ -344,21 +368,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
@@ -397,6 +488,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(),
@@ -499,9 +593,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()) {
@@ -534,8 +630,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)
@@ -544,9 +642,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.
@@ -571,6 +672,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();
@@ -588,16 +696,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 90a3c6233d892..1582fb77512b7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -974,6 +974,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 56a735e0410d7..cb1f5728b5d14 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -121,6 +121,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 baf5d102a8b74..e6188e52603ff 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 (...) {
@@ -43,3 +43,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 4ee471d04850dceae9c4341f7230d5f87b179095 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sun, 7 Dec 2025 18:26:42 +0100
Subject: [PATCH 2/6] 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 a4fde69e8f59e..24c417a879432 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -398,7 +398,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
>From 19268db43d4a8d43e604962dd42c9819634c9725 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sun, 7 Dec 2025 19:35:47 +0100
Subject: [PATCH 3/6] Support cleanup region in the parser/printer
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 ++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 +++++++++++
clang/test/CIR/IR/try-catch.cir | 34 ++++++++++++++++++++
3 files changed, 54 insertions(+)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2b5eb9738af55..d87289c294a72 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3033,6 +3033,8 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let arguments = commonArgs;
let regions = (region AnyRegion:$cleanup);
+ let skipDefaultBuilders = 1;
+
let builders = [
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
"mlir::ValueRange":$operands), [{
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 95fc3afffb156..d4464d77c972a 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -847,6 +847,16 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
return mlir::failure();
+ // If exception is present and there are cleanups, this should be latest thing
+ // present (after all attributes, etc).
+ if (!hasDestinationBlocks) {
+ mlir::Region *cleanupRegion = result.addRegion();
+ if (parser.parseOptionalKeyword("cleanup").succeeded()) {
+ if (parser.parseRegion(*cleanupRegion))
+ return failure();
+ }
+ }
+
return mlir::success();
}
@@ -899,6 +909,14 @@ static void printCallCommon(mlir::Operation *op,
printer << " : ";
printer.printFunctionalType(op->getOperands().getTypes(),
op->getResultTypes());
+
+ // If exception is present and there are cleanups, this should be latest thing
+ // present (after all attributes, etc).
+ auto call = dyn_cast<cir::CallOp>(op);
+ if (call && !call.getCleanup().empty()) {
+ printer << " cleanup ";
+ printer.printRegion(call.getCleanup());
+ }
}
mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir
index 8ffce067ba043..94368c46c6ba5 100644
--- a/clang/test/CIR/IR/try-catch.cir
+++ b/clang/test/CIR/IR/try-catch.cir
@@ -1,6 +1,7 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
module {
@@ -103,4 +104,37 @@ cir.func @empty_try_block_with_catch_unwind_contains_resume() {
// CHECK: cir.return
// CHECK: }
+
+cir.func private @function_with_cleanup() -> !s32i
+
+// CHECK: cir.func private @function_with_cleanup() -> !s32i
+
+cir.func @try_catch_with_call_that_has_cleanup() {
+ cir.scope {
+ cir.try {
+ cir.call @function_with_cleanup() : () -> !s32i cleanup {
+ cir.yield
+ }
+ cir.yield
+ } unwind {
+ cir.resume
+ }
+ }
+ cir.return
+}
+
+// CHECK: cir.func @try_catch_with_call_that_has_cleanup() {
+// CHECK: cir.scope {
+// CHECK: cir.try {
+// CHECK: cir.call @function_with_cleanup() : () -> !s32i cleanup {
+// CHECK: cir.yield
+// CHECK: }
+// CHECK: cir.yield
+// CHECK: } unwind {
+// CHECK: cir.resume
+// CHECK: }
+// CHECK: }
+// CHECK: cir.return
+// CHECK: }
+
}
>From 9b59c8d8d2a67042613c2b2c4d6464d5c8c2b0e2 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Wed, 10 Dec 2025 18:06:14 +0100
Subject: [PATCH 4/6] Add support for CatchParamOp
---
clang/include/clang/CIR/MissingFeatures.h | 1 -
clang/lib/CIR/CodeGen/CIRGenException.cpp | 3 -
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 76 ++++++++++++++++++-
clang/test/CIR/CodeGen/try-catch-tmp.cpp | 1 +
4 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index ad8c7ca7336e6..75f3599875372 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -376,7 +376,6 @@ struct MissingFeatures {
static bool tryOp() { return false; }
static bool vecTernaryOp() { return false; }
static bool zextOp() { return false; }
- static bool catchParamOp() { return false; }
// Future CIR attributes
static bool optInfoAttr() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 24c417a879432..05f2be6d077eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -536,9 +536,6 @@ void CIRGenFunction::exitCXXTryStmt(const CXXTryStmt &s, bool isFnTryBlock) {
emitStmt(catchStmt->getHandlerBlock(), /*useCurrentScope=*/true);
assert(emitResult.succeeded() && "failed to emit catch handler block");
- assert(!cir::MissingFeatures::catchParamOp());
- cir::YieldOp::create(builder, tryOp->getLoc());
-
// [except.handle]p11:
// The currently handled exception is rethrown if control
// reaches the end of a handler of the function-try-block of a
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index cb1f5728b5d14..7de2f32a28b0c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2317,7 +2317,6 @@ struct CallEndCatch final : EHScopeStack::Cleanup {
static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Type paramTy,
bool endMightThrow) {
-
auto catchParam = cir::CatchParamOp::create(
cgf.getBuilder(), cgf.getBuilder().getUnknownLoc(), paramTy);
@@ -2328,6 +2327,56 @@ static mlir::Value callBeginCatch(CIRGenFunction &cgf, mlir::Type paramTy,
return catchParam.getParam();
}
+/// A "special initializer" callback for initializing a catch
+/// parameter during catch initialization.
+static void initCatchParam(CIRGenFunction &cgf, const VarDecl &catchParam,
+ Address paramAddr, SourceLocation loc) {
+ CanQualType catchType =
+ cgf.cgm.getASTContext().getCanonicalType(catchParam.getType());
+ // If we're catching by reference, we can just cast the object
+ // pointer to the appropriate pointer.
+ if (isa<ReferenceType>(catchType)) {
+ cgf.cgm.errorNYI(loc, "initCatchParam: ReferenceType");
+ return;
+ }
+
+ // Scalars and complexes.
+ cir::TypeEvaluationKind tek = cgf.getEvaluationKind(catchType);
+ if (tek != cir::TEK_Aggregate) {
+ // Notes for LLVM lowering:
+ // If the catch type is a pointer type, __cxa_begin_catch returns
+ // the pointer by value.
+ if (catchType->hasPointerRepresentation()) {
+ cgf.cgm.errorNYI(loc, "initCatchParam: hasPointerRepresentation");
+ return;
+ }
+
+ mlir::Type cirCatchTy = cgf.convertTypeForMem(catchType);
+ mlir::Value catchParam =
+ callBeginCatch(cgf, cgf.getBuilder().getPointerTo(cirCatchTy), false);
+ LValue srcLV = cgf.makeNaturalAlignAddrLValue(catchParam, catchType);
+ LValue destLV = cgf.makeAddrLValue(paramAddr, catchType);
+ switch (tek) {
+ case cir::TEK_Complex: {
+ cgf.cgm.errorNYI(loc, "initCatchParam: cir::TEK_Complex");
+ return;
+ }
+ case cir::TEK_Scalar: {
+ auto exnLoad = cgf.emitLoadOfScalar(srcLV, loc);
+ cgf.emitStoreOfScalar(exnLoad, destLV, /*isInit=*/true);
+ return;
+ }
+ case cir::TEK_Aggregate:
+ llvm_unreachable("evaluation kind filtered out!");
+ }
+
+ // Otherwise, it returns a pointer into the exception object.
+ llvm_unreachable("bad evaluation kind");
+ }
+
+ cgf.cgm.errorNYI(loc, "initCatchParam: cir::TEK_Aggregate");
+}
+
/// Begins a catch statement by initializing the catch variable and
/// calling __cxa_begin_catch.
void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
@@ -2362,5 +2411,28 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
return;
}
- cgf.cgm.errorNYI("emitBeginCatch: catch with exception decl");
+ auto getCatchParamAllocaIP = [&]() {
+ auto currIns = cgf.getBuilder().saveInsertionPoint();
+ mlir::Operation *currParent = currIns.getBlock()->getParentOp();
+
+ mlir::Block *insertBlock = nullptr;
+ if (auto scopeOp = currParent->getParentOfType<cir::ScopeOp>()) {
+ insertBlock = &scopeOp.getScopeRegion().getBlocks().back();
+ } else if (auto fnOp = currParent->getParentOfType<cir::FuncOp>()) {
+ insertBlock = &fnOp.getRegion().getBlocks().back();
+ } else {
+ llvm_unreachable("unknown outermost scope-like parent");
+ }
+ return cgf.getBuilder().getBestAllocaInsertPoint(insertBlock);
+ };
+
+ // Emit the local. Make sure the alloca's superseed the current scope, since
+ // these are going to be consumed by `cir.catch`, which is not within the
+ // current scope.
+
+ CIRGenFunction::AutoVarEmission var =
+ cgf.emitAutoVarAlloca(*catchParam, getCatchParamAllocaIP());
+ initCatchParam(cgf, *catchParam, var.getObjectAddress(cgf),
+ catchStmt->getBeginLoc());
+ cgf.emitAutoVarCleanups(var);
}
diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
index e6188e52603ff..9e7d4e9a95fe7 100644
--- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp
+++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
@@ -56,6 +56,7 @@ void call_function_inside_try_catch_with_exception_type() {
// CIR: %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> !s32i
// CIR: cir.yield
// CIR: } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] {
+// CIR: %[[CATCH_PARAM:.*]] = cir.catch_param : !cir.ptr<!s32i>
// CIR: cir.yield
// CIR: } unwind {
// CIR: cir.resume
>From 14232df5242b9eb88316d9ca39dc9b5409389799 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Fri, 12 Dec 2025 18:29:46 +0100
Subject: [PATCH 5/6] Address code review comments
---
clang/lib/CIR/CodeGen/CIRGenException.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 05f2be6d077eb..88956cd150ba1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -262,10 +262,9 @@ void CIRGenFunction::populateUnwindResumeBlock(bool isCleanup,
return;
// Emit cir.resume into the unwind region last block
- cir::CIRBaseBuilderTy::InsertPoint ip = builder.saveInsertionPoint();
+ mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToStart(unwindResumeBlock);
cir::ResumeOp::create(builder, tryOp.getLoc());
- builder.restoreInsertionPoint(ip);
}
mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
@@ -590,7 +589,6 @@ 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::SmallPtrSet<mlir::Attribute, 4> catchTypes;
>From 65aa97043fde2c2eb3f0bedd29df8efec0a40b81 Mon Sep 17 00:00:00 2001
From: Amr Hesham <amr96 at programmer.net>
Date: Sun, 14 Dec 2025 20:31:54 +0100
Subject: [PATCH 6/6] Address code review comments
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 +-
clang/lib/CIR/CodeGen/CIRGenException.cpp | 46 +++++--------------
clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 3 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 --------
clang/test/CIR/IR/try-catch.cir | 34 --------------
5 files changed, 15 insertions(+), 91 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d87289c294a72..1ea5da34f1e73 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -837,7 +837,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "ArrayCtor", "ArrayDtor", "AwaitOp", "CallOp", "CaseOp", "DoWhileOp", "ForOp",
+ "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "DoWhileOp", "ForOp",
"GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp", "TryOp"
];
@@ -3031,7 +3031,6 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let results = (outs Optional<CIR_AnyType>:$result);
let arguments = commonArgs;
- let regions = (region AnyRegion:$cleanup);
let skipDefaultBuilders = 1;
@@ -3043,8 +3042,6 @@ 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/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 88956cd150ba1..8a0fc1128fc66 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -381,41 +381,26 @@ static void emitCatchDispatchBlock(CIRGenFunction &cgf,
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);
+ for (const EHCatchScope::Handler &handler : catchScope) {
+ if (handler.isCatchAll())
+ return;
- [[maybe_unused]] mlir::TypedAttr typeValue = handler.type.rtti;
+ 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;
}
+
+ // There was no catch all handler, populate th EH regions for the enclosing
+ // scope.
+ cgf.populateEHCatchRegions(catchScope.getEnclosingEHScope(), tryOp);
}
void CIRGenFunction::enterCXXTryStmt(const CXXTryStmt &s, cir::TryOp tryOp,
@@ -635,15 +620,13 @@ void CIRGenFunction::populateCatchHandlers(cir::TryOp tryOp) {
break;
}
- if (hasCatchAll) {
+ if (hasCatchAll)
handlerAttrs.push_back(cir::CatchAllAttr::get(&getMLIRContext()));
- }
// If there's no catch_all, attach the unwind region. This needs to be the
// last region in the TryOp catch list.
- if (!hasCatchAll) {
+ if (!hasCatchAll)
handlerAttrs.push_back(cir::UnwindAttr::get(&getMLIRContext()));
- }
// Add final array of clauses into TryOp.
tryOp.setHandlerTypesAttr(
@@ -702,13 +685,8 @@ void CIRGenFunction::populateEHCatchRegions(EHScopeStack::stable_iterator scope,
}
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());
- }
+ // TODO(cir): In the incubator we create a new basic block with YieldOp
+ // inside the attached cleanup region, but this part will be redesigned
break;
}
case EHScope::Cleanup: {
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 7de2f32a28b0c..0d0d6c34a255a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -2412,7 +2412,8 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
}
auto getCatchParamAllocaIP = [&]() {
- auto currIns = cgf.getBuilder().saveInsertionPoint();
+ cir::CIRBaseBuilderTy::InsertPoint currIns =
+ cgf.getBuilder().saveInsertionPoint();
mlir::Operation *currParent = currIns.getBlock()->getParentOp();
mlir::Block *insertBlock = nullptr;
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index d4464d77c972a..95fc3afffb156 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -847,16 +847,6 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
return mlir::failure();
- // If exception is present and there are cleanups, this should be latest thing
- // present (after all attributes, etc).
- if (!hasDestinationBlocks) {
- mlir::Region *cleanupRegion = result.addRegion();
- if (parser.parseOptionalKeyword("cleanup").succeeded()) {
- if (parser.parseRegion(*cleanupRegion))
- return failure();
- }
- }
-
return mlir::success();
}
@@ -909,14 +899,6 @@ static void printCallCommon(mlir::Operation *op,
printer << " : ";
printer.printFunctionalType(op->getOperands().getTypes(),
op->getResultTypes());
-
- // If exception is present and there are cleanups, this should be latest thing
- // present (after all attributes, etc).
- auto call = dyn_cast<cir::CallOp>(op);
- if (call && !call.getCleanup().empty()) {
- printer << " cleanup ";
- printer.printRegion(call.getCleanup());
- }
}
mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
diff --git a/clang/test/CIR/IR/try-catch.cir b/clang/test/CIR/IR/try-catch.cir
index 94368c46c6ba5..8ffce067ba043 100644
--- a/clang/test/CIR/IR/try-catch.cir
+++ b/clang/test/CIR/IR/try-catch.cir
@@ -1,7 +1,6 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
!u8i = !cir.int<u, 8>
-!s32i = !cir.int<s, 32>
module {
@@ -104,37 +103,4 @@ cir.func @empty_try_block_with_catch_unwind_contains_resume() {
// CHECK: cir.return
// CHECK: }
-
-cir.func private @function_with_cleanup() -> !s32i
-
-// CHECK: cir.func private @function_with_cleanup() -> !s32i
-
-cir.func @try_catch_with_call_that_has_cleanup() {
- cir.scope {
- cir.try {
- cir.call @function_with_cleanup() : () -> !s32i cleanup {
- cir.yield
- }
- cir.yield
- } unwind {
- cir.resume
- }
- }
- cir.return
-}
-
-// CHECK: cir.func @try_catch_with_call_that_has_cleanup() {
-// CHECK: cir.scope {
-// CHECK: cir.try {
-// CHECK: cir.call @function_with_cleanup() : () -> !s32i cleanup {
-// CHECK: cir.yield
-// CHECK: }
-// CHECK: cir.yield
-// CHECK: } unwind {
-// CHECK: cir.resume
-// CHECK: }
-// CHECK: }
-// CHECK: cir.return
-// CHECK: }
-
}
More information about the cfe-commits
mailing list