Skip to content

Conversation

@AmrDeveloper
Copy link
Member

Upstream the CatchParamOp as a prerequisite for implementing exception handlers

Issue #154992

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Oct 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 25, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Amr Hesham (AmrDeveloper)

Changes

Upstream the CatchParamOp as a prerequisite for implementing exception handlers

Issue #154992


Full diff: https://github.com/llvm/llvm-project/pull/165110.diff

4 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+44)
  • (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+17)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+29)
  • (added) clang/test/CIR/IR/invalid-catch-param.cir (+65)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 2b361ed0982c6..57736cd9c93c2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4490,6 +4490,50 @@ def CIR_TryOp : CIR_Op<"try",[
   let hasLLVMLowering = false;
 }
 
+//===----------------------------------------------------------------------===//
+// CatchParamOp
+//===----------------------------------------------------------------------===//
+
+def CIR_CatchParamKind  : CIR_I32EnumAttr<
+  "CatchParamKind", "Designate limits for begin/end of catch param handling", [
+    I32EnumAttrCase<"Begin", 0, "begin">,
+    I32EnumAttrCase<"End",  1, "end">
+]>;
+
+def CIR_CatchParamOp : CIR_Op<"catch_param"> {
+  let summary = "Represents catch clause formal parameter";
+  let description = [{
+    The `cir.catch_param` can operate in two modes: within catch regions of
+    `cir.try` or anywhere else with the `begin` or `end` markers. The `begin`
+    version requires an exception pointer of `cir.ptr<!void>`.
+
+    Example:
+
+    ```mlir
+    %exception = cir.catch_param begin %exception_obj -> !cir.ptr<!s32i>
+
+    %exception = cir.catch_param -> !cir.ptr<!void>
+
+    cir.catch_param end
+    ```
+  }];
+
+  let arguments = (ins
+    Optional<CIR_VoidPtrType>:$exception_ptr,
+    OptionalAttr<CIR_CatchParamKind>:$kind
+  );
+
+  let results = (outs Optional<CIR_AnyType>:$param);
+  let assemblyFormat = [{
+    ($kind^)?
+    ($exception_ptr^)?
+    (`->` qualified(type($param))^)?
+    attr-dict
+  }];
+
+  let hasVerifier = 1;
+}
+
 //===----------------------------------------------------------------------===//
 // Atomic operations
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 2d2ef422bfaef..3531af762fabf 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3096,6 +3096,23 @@ static mlir::ParseResult parseTryHandlerRegions(
   return mlir::success();
 }
 
+//===----------------------------------------------------------------------===//
+// CatchParamOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::CatchParamOp::verify() {
+  std::optional<cir::CatchParamKind> kind = getKind();
+  if (getExceptionPtr()) {
+    if (!kind || *kind != cir::CatchParamKind::Begin)
+      return emitOpError("needs 'begin' to work with exception pointer");
+    return success();
+  }
+
+  if (!kind && !(*this)->getParentOfType<cir::TryOp>())
+    return emitOpError("without 'kind' requires 'cir.try' surrounding scope");
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // TableGen'd op method definitions
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 5a6193fa8d840..b4c815065b2af 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2981,6 +2981,35 @@ mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMCatchParamOpLowering::matchAndRewrite(
+    cir::CatchParamOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  std::optional<cir::CatchParamKind> kind = op.getKind();
+  if (!kind)
+    llvm_unreachable("only begin/end supposed to make to lowering stage");
+
+  if (kind == cir::CatchParamKind::Begin) {
+    // Get or create `declare ptr @__cxa_begin_catch(ptr)`
+    const llvm::StringRef fnName = "__cxa_begin_catch";
+    auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+    auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {llvmPtrTy});
+    createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
+    rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
+        op, mlir::TypeRange{llvmPtrTy}, fnName,
+        mlir::ValueRange{adaptor.getExceptionPtr()});
+    return mlir::success();
+  }
+
+  // Get or create `declare void @__cxa_end_catch()`
+  const llvm::StringRef fnName = "__cxa_end_catch";
+  auto voidTy = mlir::LLVM::LLVMVoidType::get(rewriter.getContext());
+  auto fnTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {});
+  createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
+  rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(op, mlir::TypeRange{}, fnName,
+                                                  mlir::ValueRange{});
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
     cir::AllocExceptionOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/IR/invalid-catch-param.cir b/clang/test/CIR/IR/invalid-catch-param.cir
new file mode 100644
index 0000000000000..87e90b983da50
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-catch-param.cir
@@ -0,0 +1,65 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+!void = !cir.void
+
+module {
+
+cir.func dso_local @catch_param_without_kind_and_without_try_scope() {
+  // expected-error @below {{'cir.catch_param' op without 'kind' requires 'cir.try' surrounding scope}}
+  %0 = cir.catch_param -> !cir.ptr<!void>
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!void = !cir.void
+
+module {
+
+cir.func private @division() -> !s32i
+cir.func dso_local @catch_param_with_exception_ptr_but_without_kind() {
+  cir.scope {
+    cir.try {
+      %0 = cir.call @division() : () -> !s32i
+      cir.yield
+    } catch all {
+      %0 = cir.const #cir.ptr<null> : !cir.ptr<!void>
+      // expected-error @below {{'cir.catch_param' op needs 'begin' to work with exception pointer}}
+      %1 = cir.catch_param %0 -> !cir.ptr<!void>
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!void = !cir.void
+
+module {
+
+cir.func private @division() -> !s32i
+cir.func dso_local @catch_param_with_exception_ptr_but_with_end_kind() {
+  cir.scope {
+    cir.try {
+      %0 = cir.call @division() : () -> !s32i
+      cir.yield
+    } catch all {
+      %0 = cir.const #cir.ptr<null> : !cir.ptr<!void>
+      // expected-error @below {{'cir.catch_param' op needs 'begin' to work with exception pointer}}
+      %1 = cir.catch_param end %0 -> !cir.ptr<!void>
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+

let description = [{
The `cir.catch_param` can operate in two modes: within catch regions of
`cir.try` or anywhere else with the `begin` or `end` markers. The `begin`
version requires an exception pointer of `cir.ptr<!void>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth expanding on how the two modes are used (one that assumes structured control and the other one for lower level lowering). An alternative design would be split this into two operations (let's see if others have any strong opinions here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants