https://github.com/adams381 created 
https://github.com/llvm/llvm-project/pull/229579

When a handler catches a reference to a pointer, __cxa_begin_catch returns the 
caught pointer by value instead of the address of the exception object, and how 
the reference is bound depends on whether the pointer points to a class.  The 
CIR type of the catch parameter cannot always tell whether it is a reference to 
a pointer, or whether the pointee is a class.  CIRGen now records both facts 
from the AST in two new InitCatchKind values, reference_to_pointer and 
reference_to_record_pointer.  The Itanium EH lowering switches on the kind 
alone, the way classic codegen does.

Assisted-by: Claude Code / Claude Opus 5.5


>From 4a44b71157e4fe58dc598b6cd415693ec5d4905d Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 6 Oct 2026 14:50:44 -0700
Subject: [PATCH] [CIR] Classify reference-to-pointer catch parameters in
 CIRGen

When a handler catches a reference to a pointer, __cxa_begin_catch returns
the caught pointer by value instead of the address of the exception object,
and how the reference is bound depends on whether the pointer points to a
class.  The CIR type of the catch parameter cannot always tell whether it is
a reference to a pointer, or whether the pointee is a class.
CIRGen now records both facts from the AST in two new InitCatchKind values,
reference_to_pointer and reference_to_record_pointer.  The Itanium EH
lowering switches on the kind alone, the way classic codegen does.

Assisted-by: Claude Code / Claude Opus 5.5
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  23 +-
 clang/lib/CIR/CodeGen/CIRGenException.cpp     |  16 +-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 107 ++++++++
 .../CIR/Dialect/Transforms/EHABILowering.cpp  |  65 +++--
 clang/test/CIR/CodeGen/try-catch.cpp          | 206 +++++++++++++-
 clang/test/CIR/IR/construct-catch-param.cir   | 173 ++++++++++++
 .../CIR/IR/invalid-construct-catch-param.cir  |  62 +++++
 .../test/CIR/IR/invalid-init-catch-param.cir  | 258 ++++++++++++++++++
 .../eh-abi-lowering-catch-param-ref.cir       | 216 +++++++++++++++
 ...h-abi-lowering-construct-catch-invalid.cir |  88 +++++-
 10 files changed, 1173 insertions(+), 41 deletions(-)
 create mode 100644 clang/test/CIR/IR/invalid-init-catch-param.cir
 create mode 100644 
clang/test/CIR/Transforms/eh-abi-lowering-catch-param-ref.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 985cc3712c581d..e14ffdfdac3922 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -8700,6 +8700,7 @@ def CIR_TryOp : CIR_RegionBranchOpBase<"try", [
   ];
 
   let hasVerifier = 1;
+  let hasRegionVerifier = 1;
   let hasLLVMLowering = false;
 }
 
@@ -9286,6 +9287,8 @@ def CIR_InitCatchKind : CIR_I32Enum<
     I32EnumCase<"Objc", 3, "objc">,
     I32EnumCase<"TrivialCopy", 4, "trivial_copy">,
     I32EnumCase<"NonTrivialCopy", 5, "non_trivial_copy">,
+    I32EnumCase<"ReferenceToPointer", 6, "reference_to_pointer">,
+    I32EnumCase<"ReferenceToRecordPointer", 7, "reference_to_record_pointer">,
 ]>;
 
 def CIR_InitCatchKindAttr : CIR_EnumAttr<CIR_InitCatchKind, "init_catch">;
@@ -9304,6 +9307,16 @@ def CIR_InitCatchParamOp : CIR_Op<"init_catch_param"> {
     types, an aggregate copy for record types, or a call to a copy
     constructor when one is required).
 
+    A catch parameter that is a reference to a pointer uses the
+    `reference_to_record_pointer` kind when the pointer's source-level pointee
+    type is a record (class, struct, or union), and `reference_to_pointer`
+    otherwise. A reference to any other type uses `reference`.
+
+    A `reference_to_pointer` or `non_trivial_copy` operation requires its catch
+    handler to start with a `cir.construct_catch_param` of the same kind on the
+    same parameter. When the handler starts with a `cir.construct_catch_param`
+    on the parameter of the operation, the two must have the same kind.
+
     Example:
 
     ```
@@ -9326,6 +9339,7 @@ def CIR_InitCatchParamOp : CIR_Op<"init_catch_param"> {
       qualified(type($exn_ptr)) `,` qualified(type($param_addr)) attr-dict
   }];
 
+  let hasVerifier = 1;
   let hasLLVMLowering = false;
 }
 
@@ -9349,9 +9363,11 @@ def CIR_ConstructCatchParamOp : 
CIR_Op<"construct_catch_param", [
     copy of the object before `__cxa_begin_catch` is invoked.
 
     This operation takes a `!cir.eh_token` that represents the in-flight
-    exception and the alloca value that is used for the local copy of the
-    exception object. The `copy_fn` attribute is a flat symbol reference to a
-    `cir.func` thunk that copies the exception object to a local alloca value.
+    exception and the alloca value for the catch parameter. The optional
+    `copy_fn` attribute, present for `non_trivial_copy`, is a flat symbol
+    reference to a `cir.func` thunk that copies the exception object to a
+    local alloca value. For `reference_to_pointer`, the operation binds the
+    reference to the exception object itself.
 
     This operation is replaced with a target-specific representation during
     the EHABI lowering pass. For some targets, such as the Microsoft ABI,
@@ -9377,6 +9393,7 @@ def CIR_ConstructCatchParamOp : 
CIR_Op<"construct_catch_param", [
       qualified(type($param_addr)) attr-dict
   }];
 
+  let hasVerifier = 1;
   let hasLLVMLowering = false;
 }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp 
b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index bd3e2014756e68..a2665798a37fd0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -512,16 +512,16 @@ static void initCatchParam(CIRGenFunction &cgf, 
CIRGenBuilderTy &builder,
   CanQualType catchType =
       cgf.cgm.getASTContext().getCanonicalType(catchParam.getType());
   cir::InitCatchKind kind;
-  bool shouldInitFromExnDirectly = false;
 
-  // If we're catching by reference, we can just cast the object
-  // pointer to the appropriate pointer.
   if (isa<ReferenceType>(catchType)) {
     QualType caughtType = cast<ReferenceType>(catchType)->getPointeeType();
-    if (const PointerType *ptr = dyn_cast<PointerType>(caughtType)) {
-      shouldInitFromExnDirectly = !ptr->getPointeeType()->isRecordType();
-    }
-    kind = cir::InitCatchKind::Reference;
+    const PointerType *ptr = dyn_cast<PointerType>(caughtType);
+    if (!ptr)
+      kind = cir::InitCatchKind::Reference;
+    else if (ptr->getPointeeType()->isRecordType())
+      kind = cir::InitCatchKind::ReferenceToRecordPointer;
+    else
+      kind = cir::InitCatchKind::ReferenceToPointer;
   } else {
     cir::TypeEvaluationKind tek = cgf.getEvaluationKind(catchType);
     if (tek == cir::TEK_Aggregate) {
@@ -555,7 +555,7 @@ static void initCatchParam(CIRGenFunction &cgf, 
CIRGenBuilderTy &builder,
   mlir::Location mloc = cgf.getLoc(loc);
 
   if (kind == cir::InitCatchKind::NonTrivialCopy ||
-      (kind == cir::InitCatchKind::Reference && shouldInitFromExnDirectly)) {
+      kind == cir::InitCatchKind::ReferenceToPointer) {
     // Sanitizer-checked construction (UBSan vptr/derived-class checks, etc.)
     // would require additional adornments that cir.construct_catch_param does
     // not yet carry.
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 51b5bd1c58f4bd..520565476cf7a4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -4995,6 +4995,62 @@ void cir::TryOp::getSuccessorRegions(
     regions.push_back(mlir::RegionSuccessor(&handlerRegion));
 }
 
+/// Return the cir.construct_catch_param that starts the catch handler with
+/// entry block \p entryBlock, or null if there is none.  The handler must
+/// already have passed cir::TryOp::verify.
+static cir::ConstructCatchParamOp
+getLeadingConstructCatchParam(mlir::Block &entryBlock) {
+  mlir::Operation *firstOp = &entryBlock.front();
+  if (mlir::isa<cir::LifetimeStartOp>(firstOp)) {
+    auto lifetimeScope =
+        mlir::cast<cir::CleanupScopeOp>(firstOp->getNextNode());
+    firstOp = &lifetimeScope.getBodyRegion().front().front();
+  }
+  return mlir::dyn_cast<cir::ConstructCatchParamOp>(firstOp);
+}
+
+/// Verify that each cir.init_catch_param in \p handlerRegion, outside any
+/// nested cir.try, agrees with the handler's leading cir.construct_catch_param
+/// \p constructOp, which may be null.  A cir.init_catch_param of kind
+/// reference_to_pointer or non_trivial_copy needs \p constructOp on its
+/// parameter, and a cir.init_catch_param on the parameter of \p constructOp
+/// must have the kind of \p constructOp.
+static LogicalResult
+verifyCatchParamPairing(mlir::Region &handlerRegion,
+                        cir::ConstructCatchParamOp constructOp) {
+  mlir::WalkResult result =
+      handlerRegion.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
+        if (mlir::isa<cir::TryOp>(op))
+          return mlir::WalkResult::skip();
+        auto initOp = mlir::dyn_cast<cir::InitCatchParamOp>(op);
+        if (!initOp)
+          return mlir::WalkResult::advance();
+
+        cir::InitCatchKind kind = initOp.getKind();
+        bool sameParam =
+            constructOp && constructOp.getParamAddr() == initOp.getParamAddr();
+        if (sameParam && constructOp.getKind() != kind) {
+          initOp.emitOpError("kind '")
+              << cir::stringifyInitCatchKind(kind)
+              << "' does not match the kind '"
+              << cir::stringifyInitCatchKind(constructOp.getKind())
+              << "' of the preceding 'cir.construct_catch_param'";
+          return mlir::WalkResult::interrupt();
+        }
+        if ((kind == cir::InitCatchKind::ReferenceToPointer ||
+             kind == cir::InitCatchKind::NonTrivialCopy) &&
+            !sameParam) {
+          initOp.emitOpError("'")
+              << cir::stringifyInitCatchKind(kind)
+              << "' requires a preceding 'cir.construct_catch_param' of the "
+                 "same kind on the same parameter";
+          return mlir::WalkResult::interrupt();
+        }
+        return mlir::WalkResult::advance();
+      });
+  return failure(result.wasInterrupted());
+}
+
 LogicalResult cir::TryOp::verify() {
   mlir::ArrayAttr handlerTypes = getHandlerTypes();
   if (!handlerTypes) {
@@ -5098,6 +5154,26 @@ LogicalResult cir::TryOp::verify() {
   return success();
 }
 
+LogicalResult cir::TryOp::verifyRegions() {
+  mlir::ArrayAttr handlerTypes = getHandlerTypes();
+  if (!handlerTypes)
+    return success();
+
+  for (const auto &[typeAttr, handlerRegion] :
+       llvm::zip(handlerTypes, getHandlerRegions())) {
+    if (mlir::isa<cir::UnwindAttr, cir::EhFilterAttr, cir::EhUnexpectedAttr>(
+            typeAttr))
+      continue;
+    mlir::Block &entryBlock = handlerRegion.front();
+    if (mlir::isa<cir::EhTerminateOp>(entryBlock.front()))
+      continue;
+    if (failed(verifyCatchParamPairing(
+            handlerRegion, getLeadingConstructCatchParam(entryBlock))))
+      return failure();
+  }
+  return success();
+}
+
 static void
 printTryHandlerRegions(mlir::OpAsmPrinter &printer, cir::TryOp op,
                        mlir::MutableArrayRef<mlir::Region> handlerRegions,
@@ -5344,10 +5420,41 @@ LogicalResult cir::MemChrOp::verify() {
                     cir::CIRDialect::getSizeTypeWidthAttrName());
 }
 
+//===----------------------------------------------------------------------===//
+// InitCatchParamOp
+//===----------------------------------------------------------------------===//
+
+/// For the reference_to_pointer and reference_to_record_pointer kinds, emit an
+/// error on \p op unless \p paramAddrType is the type of the address of a
+/// reference to a pointer.  Any other \p kind is accepted.
+static LogicalResult verifyCatchParamAddr(mlir::Operation *op,
+                                          cir::InitCatchKind kind,
+                                          cir::PointerType paramAddrType) {
+  if (kind != cir::InitCatchKind::ReferenceToPointer &&
+      kind != cir::InitCatchKind::ReferenceToRecordPointer)
+    return success();
+
+  auto refType = mlir::dyn_cast<cir::PointerType>(paramAddrType.getPointee());
+  if (!refType || !mlir::isa<cir::PointerType>(refType.getPointee()))
+    return op->emitOpError("'")
+           << cir::stringifyInitCatchKind(kind)
+           << "' requires 'param_addr' to be the address of a reference to a "
+              "pointer";
+  return success();
+}
+
+LogicalResult cir::InitCatchParamOp::verify() {
+  return verifyCatchParamAddr(*this, getKind(), getParamAddr().getType());
+}
+
 
//===----------------------------------------------------------------------===//
 // ConstructCatchParamOp
 
//===----------------------------------------------------------------------===//
 
+LogicalResult cir::ConstructCatchParamOp::verify() {
+  return verifyCatchParamAddr(*this, getKind(), getParamAddr().getType());
+}
+
 LogicalResult cir::ConstructCatchParamOp::verifySymbolUses(
     SymbolTableCollection &symbolTable) {
   auto copyFnAttr = getCopyFnAttr();
diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
index 77ce7434c8ed31..72a8cffc3b0606 100644
--- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
@@ -22,7 +22,9 @@
 //   - cir.resume                 → cir.resume.flat
 //   - !cir.eh_token values       → (!cir.ptr<!void>, !u32i) value pairs
 //   - cir.construct_catch_param  → __cxa_get_exception_ptr + inlined
-//                                  catch-copy thunk body
+//                                  catch-copy thunk body, or a store of the
+//                                  exception object address for
+//                                  reference_to_pointer
 //   - personality function set on functions requiring EH
 //
 
//===----------------------------------------------------------------------===//
@@ -31,6 +33,7 @@
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/IRMapping.h"
 #include "mlir/IR/PatternMatch.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
@@ -828,7 +831,7 @@ 
ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op,
   cir::PointerType paramAddrType =
       mlir::cast<cir::PointerType>(paramAddr.getType());
 
-  if (op.getKind() == cir::InitCatchKind::Reference) {
+  if (op.getKind() == cir::InitCatchKind::ReferenceToPointer) {
     assert(!MissingFeatures::sizeOfUnwindException());
     constexpr unsigned headerSize = 32;
 
@@ -848,8 +851,8 @@ 
ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op,
   }
 
   if (op.getKind() != cir::InitCatchKind::NonTrivialCopy)
-    return op.emitError(
-        "ConstructCatchParam: only non_trivial_copy is supported");
+    return op.emitOpError("only reference_to_pointer and non_trivial_copy "
+                          "kinds are supported");
 
   ensureRuntimeDecls(loc);
   ensureClangCallTerminate(loc);
@@ -976,9 +979,14 @@ mlir::LogicalResult 
ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
 /// pointer returned by __cxa_begin_catch. The shape of the lowering
 /// depends on the init catch kind:
 ///
-///   - Reference: the begin_catch result is
-///     the pointer value itself, so just bitcast and store it into the alloca
-///     except if it reference of pointer of record.
+///   - Reference: the begin_catch result is the adjusted exception object
+///     pointer, so just bitcast and store it into the alloca.
+///   - ReferenceToPointer: the companion `cir.construct_catch_param`
+///     already bound the reference to the exception object, so this lowering
+///     is a no-op.
+///   - ReferenceToRecordPointer: the begin_catch result is the adjusted
+///     pointer value itself, so store it into a temporary and bind the
+///     reference to the temporary.
 ///   - Pointer: the begin_catch result is
 ///     the pointer value itself, so just bitcast and store it into the
 ///     alloca.
@@ -1002,24 +1010,41 @@ void 
ItaniumEHLowering::lowerInitCatchParam(cir::InitCatchParamOp op) {
 
   switch (kind) {
   case InitCatchKind::Reference: {
-    // We have no way to tell the personality function that we're
-    // catching by reference, so if we're catching a pointer,
-    // __cxa_begin_catch will actually return that pointer by value.
-    if (const auto ref = mlir::dyn_cast<cir::PointerType>(elementType)) {
-      // When catching by reference, generally we should just ignore
-      // this by-value pointer and use the exception object instead.
-      if (auto ptr = mlir::dyn_cast<cir::PointerType>(ref.getPointee()))
-        if (!mlir::isa<cir::RecordType>(ptr.getPointee()))
-          // Extracting and storing the actual exception object was performed 
by
-          // cir.construct_catch_param before cir.begin_catch.
-          break;
-    }
-
     mlir::Value casted = cir::CastOp::create(builder, loc, elementType,
                                              cir::CastKind::bitcast, exnPtr);
     cir::StoreOp::create(builder, loc, casted, paramAddr, {}, {}, {}, {}, {});
     break;
   }
+  case InitCatchKind::ReferenceToPointer:
+    // The reference was bound to the exception object by the matching
+    // cir.construct_catch_param before cir.begin_catch.
+    break;
+  case InitCatchKind::ReferenceToRecordPointer: {
+    // __cxa_begin_catch returns the caught pointer by value, and the
+    // personality function may have adjusted it.  Store the adjusted pointer
+    // into a temporary and bind the reference to it.  Assigning to the catch
+    // parameter then changes the temporary, not the exception object, which
+    // departs from the language semantics.
+    auto caughtType = mlir::cast<cir::PointerType>(
+        mlir::cast<cir::PointerType>(elementType).getPointee());
+    cir::AllocaOp byRefTmp;
+    {
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      builder.setInsertionPointToStart(
+          &op->getParentOfType<cir::FuncOp>().getBody().front());
+      cir::CIRDataLayout dataLayout(mod);
+      uint64_t alignment =
+          dataLayout.getAlignment(caughtType, /*useABIAlign=*/true).value();
+      byRefTmp =
+          cir::AllocaOp::create(builder, loc, elementType, "exn.byref.tmp",
+                                builder.getI64IntegerAttr(alignment));
+    }
+    mlir::Value casted = cir::CastOp::create(builder, loc, caughtType,
+                                             cir::CastKind::bitcast, exnPtr);
+    cir::StoreOp::create(builder, loc, casted, byRefTmp, {}, {}, {}, {}, {});
+    cir::StoreOp::create(builder, loc, byRefTmp, paramAddr, {}, {}, {}, {}, 
{});
+    break;
+  }
   case InitCatchKind::TrivialCopy: {
     mlir::Value srcPtr = cir::CastOp::create(builder, loc, paramAddrType,
                                              cir::CastKind::bitcast, exnPtr);
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp 
b/clang/test/CIR/CodeGen/try-catch.cpp
index 2b4108ca0d270e..8f34a2987c216e 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefixes=LLVM,SHARED
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=OGCG,SHARED
 
 void empty_try_block_with_catch_all() {
   try {} catch (...) {}
@@ -1121,9 +1121,9 @@ void 
call_function_inside_try_catch_with_ref_ptr_of_record_exception_type() {
 // CIR:     %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> (!s32i 
{llvm.noundef})
 // CIR:     cir.yield
 // CIR:   } catch [type #cir.global_view<@_ZTIP6Record> : !cir.ptr<!u8i>] 
(%[[EH_TOKEN:.*]]: !cir.eh_token {{.*}}) {
-// CIR:     %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch 
%[[EH_TOKEN]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR-NEXT:     %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch 
%[[EH_TOKEN]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
 // CIR:     cir.cleanup.scope {
-// CIR:       cir.init_catch_param reference %[[EXN_PTR]] to %{{.*}} : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>
+// CIR:       cir.init_catch_param reference_to_record_pointer %[[EXN_PTR]] to 
%[[E_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>
 // CIR:       cir.yield
 // CIR:     } cleanup all {
 // CIR:       cir.end_catch %[[CATCH_TOKEN]] : !cir.catch_token
@@ -1136,6 +1136,8 @@ void 
call_function_inside_try_catch_with_ref_ptr_of_record_exception_type() {
 // CIR: }
 
 // LLVM: define {{.*}} void 
@_Z68call_function_inside_try_catch_with_ref_ptr_of_record_exception_typev() 
{{.*}} personality ptr @__gxx_personality_v0
+// LLVM:   %[[EXN_BYREF_TMP:.*]] = alloca ptr, align 8
+// LLVM:   %[[E_ADDR:.*]] = alloca ptr, align 8
 // LLVM:   br label %[[TRY_SCOPE:.*]]
 // LLVM: [[TRY_SCOPE]]:
 // LLVM:   br label %[[TRY_BEGIN:.*]]
@@ -1163,10 +1165,12 @@ void 
call_function_inside_try_catch_with_ref_ptr_of_record_exception_type() {
 // LLVM: [[BEGIN_CATCH]]:
 // LLVM:   %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], 
%[[DISPATCH:.*]] ]
 // LLVM:   %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], 
%[[DISPATCH:.*]] ]
-// LLVM:   %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM-NEXT:   %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr 
%[[EXN_OBJ_PHI2]])
 // LLVM:   br label %[[CATCH_BODY:.*]]
 // LLVM: [[CATCH_BODY]]:
-// LLVM:   br label %[[END_CATCH:.*]]
+// LLVM-NEXT:   store ptr %[[TOKEN]], ptr %[[EXN_BYREF_TMP]], align 8
+// LLVM-NEXT:   store ptr %[[EXN_BYREF_TMP]], ptr %[[E_ADDR]], align 8
+// LLVM-NEXT:   br label %[[END_CATCH:.*]]
 // LLVM: [[END_CATCH]]:
 // LLVM:   call void @__cxa_end_catch()
 // LLVM:   br label %[[END_DISPATCH:.*]]
@@ -1638,10 +1642,10 @@ int init_catch_param_with_ref_to_ptr_to_non_record() {
 // CIR:       %[[CALL:.*]] = cir.call @_Z8divisionv() : () -> (!s32i 
{llvm.noundef})
 // CIR:       cir.yield
 // CIR:     } catch [type #cir.global_view<@_ZTIPi> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
-// CIR:       cir.construct_catch_param reference %[[TOKEN]] to %[[P_ADDR]] 
using : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+// CIR:       cir.construct_catch_param reference_to_pointer %[[TOKEN]] to 
%[[P_ADDR]] using : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
 // CIR:       %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %{{.*}} : 
!cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
 // CIR:       cir.cleanup.scope {
-// CIR:         cir.init_catch_param reference %[[EXN_PTR]] to %[[P_ADDR]] : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+// CIR:         cir.init_catch_param reference_to_pointer %[[EXN_PTR]] to 
%[[P_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
 // CIR:         %[[TMP_P:.*]] = cir.load %[[P_ADDR]] : 
!cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>, !cir.ptr<!cir.ptr<!s32i>>
 // CIR:         %[[DEREF_P:.*]] = cir.load deref {{.*}} %[[TMP_P]] : 
!cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
 // CIR:         %[[P_VAL:.*]] = cir.load {{.*}} %[[DEREF_P]] : 
!cir.ptr<!s32i>, !s32i
@@ -1862,3 +1866,189 @@ void direct_inside_try_catch_with_exception_type() {
 // OGCG:   resume { ptr, i32 }
 // OGCG: [[UNREACHABLE]]:
 // OGCG:   unreachable
+
+const void *init_catch_param_with_ref_to_nullptr() {
+  try {
+    division();
+  } catch (const decltype(nullptr) &n) {
+    return &n;
+  }
+  return nullptr;
+}
+
+// CIR: cir.func {{.*}} @_Z36init_catch_param_with_ref_to_nullptrv() {{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[N_ADDR:.*]] = cir.alloca "n" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIDn> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference %[[EXN_PTR]] to %[[N_ADDR]] : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+
+// SHARED: define {{.*}} ptr @_Z36init_catch_param_with_ref_to_nullptrv() 
{{.*}} personality ptr @__gxx_personality_v0
+// SHARED:   %[[EXN_PTR:.*]] = call ptr @__cxa_begin_catch(ptr %{{.*}})
+// SHARED:   store ptr %[[EXN_PTR]], ptr %[[N_ADDR:.*]], align 8
+// SHARED-NEXT:   %[[N:.*]] = load ptr, ptr %[[N_ADDR]], align 8
+// SHARED-NEXT:   store ptr %[[N]], ptr %{{.*}}, align 8
+
+const void *init_catch_param_with_ref_to_void_ptr() {
+  try {
+    division();
+  } catch (void *const &p) {
+    return &p;
+  }
+  return nullptr;
+}
+
+// CIR: cir.func {{.*}} @_Z37init_catch_param_with_ref_to_void_ptrv() {{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[P_ADDR:.*]] = cir.alloca "p" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIPv> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: cir.construct_catch_param reference_to_pointer %[[TOKEN]] to 
%[[P_ADDR]] using : !cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference_to_pointer %[[EXN_PTR]] to 
%[[P_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+
+// SHARED: define {{.*}} ptr @_Z37init_catch_param_with_ref_to_void_ptrv() 
{{.*}} personality ptr @__gxx_personality_v0
+// LLVM:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN:.*]], i64 32
+// LLVM-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// LLVM-NEXT:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN]])
+// LLVM-NEXT:   br label %[[CATCH_BODY:.+]]
+// LLVM:      [[CATCH_BODY]]:
+// LLVM-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+// OGCG:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN:.*]])
+// OGCG-NEXT:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN]], i32 32
+// OGCG-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// OGCG-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+const void *init_catch_param_with_ref_to_atomic_record_ptr() {
+  try {
+    division();
+  } catch (_Atomic(Record) *const &p) {
+    return &p;
+  }
+  return nullptr;
+}
+
+// CIR: cir.func {{.*}} @_Z46init_catch_param_with_ref_to_atomic_record_ptrv() 
{{.*}} personality(@__gxx_personality_v0)
+// CIR:   %[[P_ADDR:.*]] = cir.alloca "p" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIPU7_Atomic6Record> : 
!cir.ptr<!u8i>] (%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: cir.construct_catch_param reference_to_pointer %[[TOKEN]] to 
%[[P_ADDR]] using : !cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference_to_pointer %[[EXN_PTR]] to 
%[[P_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>
+
+// SHARED: define {{.*}} ptr 
@_Z46init_catch_param_with_ref_to_atomic_record_ptrv() {{.*}} personality ptr 
@__gxx_personality_v0
+// LLVM:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN:.*]], i64 32
+// LLVM-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// LLVM-NEXT:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN]])
+// LLVM-NEXT:   br label %[[CATCH_BODY:.+]]
+// LLVM:      [[CATCH_BODY]]:
+// LLVM-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+// OGCG:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN:.*]])
+// OGCG-NEXT:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN]], i32 32
+// OGCG-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// OGCG-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+const void *init_catch_param_with_ref_to_ptr_to_member_function_ptr() {
+  try {
+    division();
+  } catch (int (Record::**const &p)()) {
+    return &p;
+  }
+  return nullptr;
+}
+
+// CIR: cir.func {{.*}} 
@_Z55init_catch_param_with_ref_to_ptr_to_member_function_ptrv() {{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[P_ADDR:.*]] = cir.alloca "p" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_anon_struct>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIPM6RecordFivE> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: cir.construct_catch_param reference_to_pointer %[[TOKEN]] to 
%[[P_ADDR]] using : !cir.ptr<!cir.ptr<!cir.ptr<!rec_anon_struct>>>
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference_to_pointer %[[EXN_PTR]] to 
%[[P_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_anon_struct>>>
+
+// SHARED: define {{.*}} ptr 
@_Z55init_catch_param_with_ref_to_ptr_to_member_function_ptrv() {{.*}} 
personality ptr @__gxx_personality_v0
+// LLVM:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN:.*]], i64 32
+// LLVM-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// LLVM-NEXT:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN]])
+// LLVM-NEXT:   br label %[[CATCH_BODY:.+]]
+// LLVM:      [[CATCH_BODY]]:
+// LLVM-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+// OGCG:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN:.*]])
+// OGCG-NEXT:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN]], i32 32
+// OGCG-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[P_ADDR:.+]], align 8
+// OGCG-NEXT:   %{{.+}} = load ptr, ptr %[[P_ADDR]], align 8
+
+Record gr;
+
+int init_catch_param_with_ref_to_member_function_ptr() {
+  try {
+    division();
+  } catch (int (Record::*const &mfp)()) {
+    return (gr.*mfp)();
+  }
+  return 0;
+}
+
+// CIR: cir.func {{.*}} 
@_Z48init_catch_param_with_ref_to_member_function_ptrv() {{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[MFP_ADDR:.*]] = cir.alloca "mfp" {{.*}} const : 
!cir.ptr<!cir.ptr<!rec_anon_struct>>
+// CIR:   } catch [type #cir.global_view<@_ZTIM6RecordFivE> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference %[[EXN_PTR]] to %[[MFP_ADDR]] : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!rec_anon_struct>>
+
+// SHARED: define {{.*}} i32 
@_Z48init_catch_param_with_ref_to_member_function_ptrv() {{.*}} personality ptr 
@__gxx_personality_v0
+// SHARED:   %[[EXN_PTR:.*]] = call ptr @__cxa_begin_catch(ptr %{{.*}})
+// SHARED:   store ptr %[[EXN_PTR]], ptr %[[MFP_ADDR:.*]], align 8
+// SHARED-NEXT:   %[[MFP:.*]] = load ptr, ptr %[[MFP_ADDR]], align 8
+// SHARED-NEXT:   %{{.*}} = load { i64, i64 }, ptr %[[MFP]], align 8
+
+void init_catch_param_with_ref_to_ptr_to_ptr_to_record() {
+  try {
+    division();
+  } catch (Record **&pp) {
+  }
+}
+
+// CIR: cir.func {{.*}} 
@_Z49init_catch_param_with_ref_to_ptr_to_ptr_to_recordv(){{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[PP_ADDR:.*]] = cir.alloca "pp" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIPP6Record> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: cir.construct_catch_param reference_to_pointer %[[TOKEN]] to 
%[[PP_ADDR]] using : !cir.ptr<!cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>>
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference_to_pointer %[[EXN_PTR]] to 
%[[PP_ADDR]] : !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!cir.ptr<!cir.ptr<!rec_Record>>>>
+
+// SHARED: define {{.*}} void 
@_Z49init_catch_param_with_ref_to_ptr_to_ptr_to_recordv() {{.*}} personality 
ptr @__gxx_personality_v0
+// LLVM:   %[[PP_ADDR:.*]] = alloca ptr, align 8
+// LLVM:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN:.*]], i64 32
+// LLVM-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[PP_ADDR]], align 8
+// LLVM-NEXT:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN]])
+// LLVM-NEXT:   br label %[[CATCH_BODY:.+]]
+// LLVM:      [[CATCH_BODY]]:
+// LLVM-NEXT:   br label
+// OGCG:   %{{.*}} = alloca i32, align 4
+// OGCG-NEXT:   %[[PP_ADDR:.*]] = alloca ptr, align 8
+// OGCG:   %{{.*}} = call ptr @__cxa_begin_catch(ptr %[[EXN:.*]])
+// OGCG-NEXT:   %[[EXN_OBJ:.*]] = getelementptr i8, ptr %[[EXN]], i32 32
+// OGCG-NEXT:   store ptr %[[EXN_OBJ]], ptr %[[PP_ADDR]], align 8
+
+union Union {
+  int i;
+};
+
+void init_catch_param_with_ref_to_union_ptr() {
+  try {
+    division();
+  } catch (Union *&up) {
+  }
+}
+
+// CIR: cir.func {{.*}} @_Z38init_catch_param_with_ref_to_union_ptrv(){{.*}} 
personality(@__gxx_personality_v0)
+// CIR:   %[[UP_ADDR:.*]] = cir.alloca "up" {{.*}} const : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Union>>>
+// CIR:   } catch [type #cir.global_view<@_ZTIP5Union> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR-NEXT: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] 
: !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR:     cir.init_catch_param reference_to_record_pointer %[[EXN_PTR]] to 
%[[UP_ADDR]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Union>>>
+
+// SHARED: define {{.*}} void @_Z38init_catch_param_with_ref_to_union_ptrv() 
{{.*}} personality ptr @__gxx_personality_v0
+// LLVM:   %[[TMP:.*]] = alloca ptr, align 8
+// LLVM-NEXT:   %[[UP_ADDR:.*]] = alloca ptr, align 8
+// OGCG:   %{{.*}} = alloca i32, align 4
+// OGCG-NEXT:   %[[UP_ADDR:.*]] = alloca ptr, align 8
+// OGCG-NEXT:   %[[TMP:.*]] = alloca ptr, align 8
+// SHARED:   %[[EXN_PTR:.*]] = call ptr @__cxa_begin_catch(ptr %{{.*}})
+// SHARED:   store ptr %[[EXN_PTR]], ptr %[[TMP]], align 8
+// SHARED-NEXT:   store ptr %[[TMP]], ptr %[[UP_ADDR]], align 8
diff --git a/clang/test/CIR/IR/construct-catch-param.cir 
b/clang/test/CIR/IR/construct-catch-param.cir
index b72eee2e8d0283..63d630231c860f 100644
--- a/clang/test/CIR/IR/construct-catch-param.cir
+++ b/clang/test/CIR/IR/construct-catch-param.cir
@@ -60,4 +60,177 @@ cir.func @construct_catch_param_basic() {
 // CHECK-SAME:        using @__clang_cir_catch_copy__ZTS1E : !cir.ptr<!rec_E>
 // CHECK:           cir.begin_catch %[[EH]]
 
+// A reference to a pointer to a non-record, bound before `cir.begin_catch`.
+cir.func @construct_catch_param_ref_to_ptr() {
+  cir.scope {
+    %param = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param reference_to_pointer %eh_token to %param
+        using : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.cleanup.scope {
+        cir.init_catch_param reference_to_pointer %exn_ptr to %param
+          : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+        cir.yield
+      } cleanup eh {
+        cir.end_catch %catch_token : !cir.catch_token
+        cir.yield
+      }
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @construct_catch_param_ref_to_ptr()
+// CHECK:         %[[P:.*]] = cir.alloca "p" {{.*}} : 
!cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+// CHECK:         } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+// CHECK-SAME:        (%[[EH:.*]]: !cir.eh_token) {
+// CHECK:           cir.construct_catch_param reference_to_pointer %[[EH]] to 
%[[P]] using : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+// CHECK:           %{{.*}}, %[[EXN:.*]] = cir.begin_catch %[[EH]]
+// CHECK:             cir.init_catch_param reference_to_pointer %[[EXN]] to 
%[[P]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+
+// A reference to a pointer to a record, bound by `cir.init_catch_param`.
+cir.func @init_catch_param_ref_to_record_ptr() {
+  cir.scope {
+    %param = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.cleanup.scope {
+        cir.init_catch_param reference_to_record_pointer %exn_ptr to %param
+          : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+        cir.yield
+      } cleanup eh {
+        cir.end_catch %catch_token : !cir.catch_token
+        cir.yield
+      }
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @init_catch_param_ref_to_record_ptr()
+// CHECK:         %[[P:.*]] = cir.alloca "p" {{.*}} : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+// CHECK:           %{{.*}}, %[[EXN:.*]] = cir.begin_catch
+// CHECK:             cir.init_catch_param reference_to_record_pointer 
%[[EXN]] to %[[P]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+
+// A `cir.construct_catch_param` in the handler of a nested `cir.try`.
+cir.func @construct_catch_param_nested_try() {
+  cir.scope {
+    %r = cir.alloca "r" align(8) : !cir.ptr<!cir.ptr<!rec_E>>
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.cleanup.scope {
+        cir.init_catch_param reference %exn_ptr to %r
+          : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!rec_E>>
+        cir.try {
+          cir.yield
+        } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+            (%inner_token: !cir.eh_token) {
+          cir.construct_catch_param reference_to_pointer %inner_token to %p
+            using : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+          %inner_catch, %inner_exn = cir.begin_catch %inner_token
+            : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+          cir.init_catch_param reference_to_pointer %inner_exn to %p
+            : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+          cir.end_catch %inner_catch : !cir.catch_token
+          cir.yield
+        }
+        cir.yield
+      } cleanup eh {
+        cir.end_catch %catch_token : !cir.catch_token
+        cir.yield
+      }
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @construct_catch_param_nested_try()
+// CHECK:         %[[R:.*]] = cir.alloca "r" {{.*}} : 
!cir.ptr<!cir.ptr<!rec_E>>
+// CHECK:         %[[P:.*]] = cir.alloca "p" {{.*}} : 
!cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+// CHECK:             cir.init_catch_param reference %{{.*}} to %[[R]] : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!rec_E>>
+// CHECK:               cir.construct_catch_param reference_to_pointer %{{.*}} 
to %[[P]] using
+// CHECK:               cir.init_catch_param reference_to_pointer %{{.*}} to 
%[[P]]
+
+// A `cir.init_catch_param` on a parameter with no `cir.construct_catch_param`.
+cir.func @construct_catch_param_other_param() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+    %r = cir.alloca "r" align(8) : !cir.ptr<!cir.ptr<!rec_E>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param reference_to_pointer %eh_token to %p
+        using : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.init_catch_param reference_to_pointer %exn_ptr to %p
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+      cir.init_catch_param reference %exn_ptr to %r
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!rec_E>>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @construct_catch_param_other_param()
+// CHECK:         %[[P:.*]] = cir.alloca "p" {{.*}} : 
!cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+// CHECK:         %[[R:.*]] = cir.alloca "r" {{.*}} : 
!cir.ptr<!cir.ptr<!rec_E>>
+// CHECK:           cir.init_catch_param reference_to_pointer %{{.*}} to %[[P]]
+// CHECK:           cir.init_catch_param reference %{{.*}} to %[[R]]
+
+// A `cir.construct_catch_param` inside the lifetime cleanup scope.
+cir.func @construct_catch_param_in_lifetime_scope() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.lifetime.start %p : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+      cir.cleanup.scope {
+        cir.construct_catch_param reference_to_pointer %eh_token to %p
+          using : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+        %catch_token, %exn_ptr = cir.begin_catch %eh_token
+          : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+        cir.init_catch_param reference_to_pointer %exn_ptr to %p
+          : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+        cir.end_catch %catch_token : !cir.catch_token
+        cir.yield
+      } cleanup all {
+        cir.lifetime.end %p : !cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+        cir.yield
+      }
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+// CHECK-LABEL: cir.func @construct_catch_param_in_lifetime_scope()
+// CHECK:         %[[P:.*]] = cir.alloca "p" {{.*}} : 
!cir.ptr<!cir.ptr<!cir.ptr<!s8i>>>
+// CHECK:           cir.lifetime.start %[[P]]
+// CHECK:           cir.cleanup.scope {
+// CHECK-NEXT:        cir.construct_catch_param reference_to_pointer %{{.*}} 
to %[[P]] using
+// CHECK:             cir.init_catch_param reference_to_pointer %{{.*}} to 
%[[P]]
+
 }
diff --git a/clang/test/CIR/IR/invalid-construct-catch-param.cir 
b/clang/test/CIR/IR/invalid-construct-catch-param.cir
index 86c4c6d6612aa1..5df28816755379 100644
--- a/clang/test/CIR/IR/invalid-construct-catch-param.cir
+++ b/clang/test/CIR/IR/invalid-construct-catch-param.cir
@@ -248,3 +248,65 @@ cir.func @construct_not_followed_by_begin_catch() {
 }
 
 }
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_E = !cir.struct<"E" {pad !u8i}>
+
+module {
+
+cir.global "private" constant external @_ZTIP1E : !cir.ptr<!u8i>
+
+cir.func @ref_to_record_ptr_referent_not_a_pointer() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!rec_E>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTIP1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      // expected-error @below {{'cir.construct_catch_param' op 
'reference_to_record_pointer' requires 'param_addr' to be the address of a 
reference to a pointer}}
+      cir.construct_catch_param reference_to_record_pointer %eh_token to %p 
using
+        : !cir.ptr<!cir.ptr<!rec_E>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_E = !cir.struct<"E" {pad !u8i}>
+
+module {
+
+cir.global "private" constant external @_ZTIP1E : !cir.ptr<!u8i>
+
+cir.func @ref_to_ptr_not_a_reference() {
+  cir.scope {
+    %p = cir.alloca "p" align(1) : !cir.ptr<!rec_E>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTIP1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      // expected-error @below {{'cir.construct_catch_param' op 
'reference_to_pointer' requires 'param_addr' to be the address of a reference 
to a pointer}}
+      cir.construct_catch_param reference_to_pointer %eh_token to %p using
+        : !cir.ptr<!rec_E>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
diff --git a/clang/test/CIR/IR/invalid-init-catch-param.cir 
b/clang/test/CIR/IR/invalid-init-catch-param.cir
new file mode 100644
index 00000000000000..556e0edf9840eb
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-init-catch-param.cir
@@ -0,0 +1,258 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+!void = !cir.void
+
+cir.func @ref_to_ptr_not_a_reference(%exn: !cir.ptr<!void>) {
+  %0 = cir.alloca "p" align(4) : !cir.ptr<!s32i>
+  // expected-error @below {{'cir.init_catch_param' op 'reference_to_pointer' 
requires 'param_addr' to be the address of a reference to a pointer}}
+  cir.init_catch_param reference_to_pointer %exn to %0 : !cir.ptr<!void>, 
!cir.ptr<!s32i>
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!void = !cir.void
+
+cir.func @ref_to_record_ptr_not_a_pointer(%exn: !cir.ptr<!void>) {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!s32i>>
+  // expected-error @below {{'cir.init_catch_param' op 
'reference_to_record_pointer' requires 'param_addr' to be the address of a 
reference to a pointer}}
+  cir.init_catch_param reference_to_record_pointer %exn to %0 : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!s32i>>
+  cir.return
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @ref_to_ptr_without_construct() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op 
'reference_to_pointer' requires a preceding 'cir.construct_catch_param' of the 
same kind on the same parameter}}
+      cir.init_catch_param reference_to_pointer %exn_ptr to %p
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @construct_kind_mismatch() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param reference_to_pointer %eh_token to %p using
+        : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op kind 'reference' 
does not match the kind 'reference_to_pointer' of the preceding 
'cir.construct_catch_param'}}
+      cir.init_catch_param reference %exn_ptr to %p
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_E = !cir.struct<"E" {pad !u8i}>
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @non_trivial_copy_without_construct() {
+  cir.scope {
+    %e = cir.alloca "e" align(1) : !cir.ptr<!rec_E>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op 'non_trivial_copy' 
requires a preceding 'cir.construct_catch_param' of the same kind on the same 
parameter}}
+      cir.init_catch_param non_trivial_copy %exn_ptr to %e
+        : !cir.ptr<!void>, !cir.ptr<!rec_E>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @same_kind_different_param() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    %q = cir.alloca "q" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param reference_to_pointer %eh_token to %q using
+        : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op 
'reference_to_pointer' requires a preceding 'cir.construct_catch_param' of the 
same kind on the same parameter}}
+      cir.init_catch_param reference_to_pointer %exn_ptr to %p
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @construct_kind_mismatch_needs_construct() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param non_trivial_copy %eh_token to %p using
+        : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op kind 
'reference_to_pointer' does not match the kind 'non_trivial_copy' of the 
preceding 'cir.construct_catch_param'}}
+      cir.init_catch_param reference_to_pointer %exn_ptr to %p
+        : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @init_operand_count() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.construct_catch_param reference_to_pointer %eh_token to %p using
+        : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token
+        : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+      // expected-error @below {{'cir.init_catch_param' op expected 2 
operands, but found 1}}
+      "cir.init_catch_param"(%exn_ptr) <{kind = 
#cir.init_catch<reference_to_pointer>}>
+        : (!cir.ptr<!void>) -> ()
+      cir.end_catch %catch_token : !cir.catch_token
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.global "private" constant external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func @construct_kind_mismatch_in_lifetime_scope() {
+  cir.scope {
+    %p = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+    cir.try {
+      cir.yield
+    } catch [type #cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>]
+        (%eh_token: !cir.eh_token) {
+      cir.lifetime.start %p : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+      cir.cleanup.scope {
+        cir.construct_catch_param reference_to_pointer %eh_token to %p using
+          : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+        %catch_token, %exn_ptr = cir.begin_catch %eh_token
+          : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+        // expected-error @below {{'cir.init_catch_param' op kind 'reference' 
does not match the kind 'reference_to_pointer' of the preceding 
'cir.construct_catch_param'}}
+        cir.init_catch_param reference %exn_ptr to %p
+          : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+        cir.end_catch %catch_token : !cir.catch_token
+        cir.yield
+      } cleanup all {
+        cir.lifetime.end %p : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+        cir.yield
+      }
+      cir.yield
+    }
+  }
+  cir.return
+}
+
+}
diff --git a/clang/test/CIR/Transforms/eh-abi-lowering-catch-param-ref.cir 
b/clang/test/CIR/Transforms/eh-abi-lowering-catch-param-ref.cir
new file mode 100644
index 00000000000000..93f1c109d36d54
--- /dev/null
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-catch-param-ref.cir
@@ -0,0 +1,216 @@
+// RUN: cir-opt %s -cir-eh-abi-lowering -split-input-file -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_Rec = !cir.struct<"Rec" {data !s32i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+
+cir.global "private" external @_ZTIPi : !cir.ptr<!u8i>
+cir.global "private" external @_ZTIP3Rec : !cir.ptr<!u8i>
+cir.global "private" external @_ZTIDn : !cir.ptr<!u8i>
+cir.global "private" external @_ZTIPU7_Atomic3Rec : !cir.ptr<!u8i>
+
+cir.func private @_Z8mayThrowv()
+
+// Reference to a pointer to a record: bound to an exn.byref.tmp temporary.
+cir.func @ref_to_record_ptr() personality(@__gxx_personality_v0) {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb5
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%2: !cir.eh_token):
+  cir.eh.dispatch %2 : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIP3Rec> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb6
+  ]
+^bb4(%3: !cir.eh_token):
+  %catch_token, %exn_ptr = cir.begin_catch %3 : !cir.eh_token -> 
(!cir.catch_token, !cir.ptr<!void>)
+  cir.init_catch_param reference_to_record_pointer %exn_ptr to %0 : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.end_catch %catch_token : !cir.catch_token
+  cir.br ^bb5
+^bb5:
+  cir.return
+^bb6(%4: !cir.eh_token):
+  cir.resume %4 : !cir.eh_token
+}
+
+// CHECK-LABEL: cir.func @ref_to_record_ptr()
+// CHECK:         %[[TMP:.*]] = cir.alloca "exn.byref.tmp" align(8) : 
!cir.ptr<!cir.ptr<!rec_Rec>>
+// CHECK:         %[[P:.*]] = cir.alloca "p" align(8) : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+// CHECK:         cir.brcond %{{[0-9]+}} ^[[CATCH:bb[0-9]+]](
+// CHECK:       ^[[CATCH]](%[[EXN_ARG:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK-NEXT:    %[[EXN:.*]] = cir.call @__cxa_begin_catch(%[[EXN_ARG]]) : 
(!cir.ptr<!void>) -> !cir.ptr<!u8i>
+// CHECK-NEXT:    %[[EXN_VOID:.*]] = cir.cast bitcast %[[EXN]] : 
!cir.ptr<!u8i> -> !cir.ptr<!void>
+// CHECK-NEXT:    %[[ADJUSTED:.*]] = cir.cast bitcast %[[EXN_VOID]] : 
!cir.ptr<!void> -> !cir.ptr<!rec_Rec>
+// CHECK-NEXT:    cir.store %[[ADJUSTED]], %[[TMP]] : !cir.ptr<!rec_Rec>, 
!cir.ptr<!cir.ptr<!rec_Rec>>
+// CHECK-NEXT:    cir.store %[[TMP]], %[[P]] : !cir.ptr<!cir.ptr<!rec_Rec>>, 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+// CHECK-NEXT:    cir.call @__cxa_end_catch()
+
+// Reference to a pointer to a non-record: bound before __cxa_begin_catch.
+cir.func @ref_to_int_ptr() personality(@__gxx_personality_v0) {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb5
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%2: !cir.eh_token):
+  cir.eh.dispatch %2 : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIPi> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb6
+  ]
+^bb4(%3: !cir.eh_token):
+  cir.construct_catch_param reference_to_pointer %3 to %0 using : 
!cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+  %catch_token, %exn_ptr = cir.begin_catch %3 : !cir.eh_token -> 
(!cir.catch_token, !cir.ptr<!void>)
+  cir.init_catch_param reference_to_pointer %exn_ptr to %0 : !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+  cir.end_catch %catch_token : !cir.catch_token
+  cir.br ^bb5
+^bb5:
+  cir.return
+^bb6(%4: !cir.eh_token):
+  cir.resume %4 : !cir.eh_token
+}
+
+// CHECK-LABEL: cir.func @ref_to_int_ptr()
+// CHECK-NOT:     exn.byref.tmp
+// CHECK:         %[[P:.*]] = cir.alloca "p" align(8) : 
!cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+// CHECK:         cir.brcond %{{[0-9]+}} ^[[CATCH:bb[0-9]+]](
+// CHECK:       ^[[CATCH]](%[[EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK-NEXT:    %[[HEADER:.*]] = cir.const #cir.int<32> : !u32i
+// CHECK-NEXT:    %[[OBJ:.*]] = cir.ptr_stride %[[EXN]], %[[HEADER]] : 
(!cir.ptr<!void>, !u32i) -> !cir.ptr<!void>
+// CHECK-NEXT:    %[[OBJ_CAST:.*]] = cir.cast bitcast %[[OBJ]] : 
!cir.ptr<!void> -> !cir.ptr<!cir.ptr<!s32i>>
+// CHECK-NEXT:    cir.store %[[OBJ_CAST]], %[[P]] : !cir.ptr<!cir.ptr<!s32i>>, 
!cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>
+// CHECK-NEXT:    %[[ADJUSTED:.*]] = cir.call @__cxa_begin_catch(%[[EXN]]) : 
(!cir.ptr<!void>) -> !cir.ptr<!u8i>
+// CHECK-NEXT:    cir.cast bitcast %[[ADJUSTED]] : !cir.ptr<!u8i> -> 
!cir.ptr<!void>
+// CHECK-NEXT:    cir.call @__cxa_end_catch()
+
+// Reference to an _Atomic(Rec) pointer: bound before __cxa_begin_catch.
+cir.func @ref_to_ptr_record_shaped() personality(@__gxx_personality_v0) {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb5
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%2: !cir.eh_token):
+  cir.eh.dispatch %2 : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIPU7_Atomic3Rec> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb6
+  ]
+^bb4(%3: !cir.eh_token):
+  cir.construct_catch_param reference_to_pointer %3 to %0 using : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  %catch_token, %exn_ptr = cir.begin_catch %3 : !cir.eh_token -> 
(!cir.catch_token, !cir.ptr<!void>)
+  cir.init_catch_param reference_to_pointer %exn_ptr to %0 : !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.end_catch %catch_token : !cir.catch_token
+  cir.br ^bb5
+^bb5:
+  cir.return
+^bb6(%4: !cir.eh_token):
+  cir.resume %4 : !cir.eh_token
+}
+
+// CHECK-LABEL: cir.func @ref_to_ptr_record_shaped()
+// CHECK-NOT:     exn.byref.tmp
+// CHECK:         %[[P:.*]] = cir.alloca "p" align(8) : 
!cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+// CHECK:         cir.brcond %{{[0-9]+}} ^[[CATCH:bb[0-9]+]](
+// CHECK:       ^[[CATCH]](%[[EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK-NEXT:    %[[HEADER:.*]] = cir.const #cir.int<32> : !u32i
+// CHECK-NEXT:    %[[OBJ:.*]] = cir.ptr_stride %[[EXN]], %[[HEADER]] : 
(!cir.ptr<!void>, !u32i) -> !cir.ptr<!void>
+// CHECK-NEXT:    %[[OBJ_CAST:.*]] = cir.cast bitcast %[[OBJ]] : 
!cir.ptr<!void> -> !cir.ptr<!cir.ptr<!rec_Rec>>
+// CHECK-NEXT:    cir.store %[[OBJ_CAST]], %[[P]] : 
!cir.ptr<!cir.ptr<!rec_Rec>>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+// CHECK-NEXT:    %[[ADJUSTED:.*]] = cir.call @__cxa_begin_catch(%[[EXN]]) : 
(!cir.ptr<!void>) -> !cir.ptr<!u8i>
+// CHECK-NEXT:    cir.cast bitcast %[[ADJUSTED]] : !cir.ptr<!u8i> -> 
!cir.ptr<!void>
+// CHECK-NEXT:    cir.call @__cxa_end_catch()
+
+// Reference to std::nullptr_t: bound to the __cxa_begin_catch result.
+cir.func @ref_to_nullptr() personality(@__gxx_personality_v0) {
+  %0 = cir.alloca "n" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb5
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%2: !cir.eh_token):
+  cir.eh.dispatch %2 : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIDn> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb6
+  ]
+^bb4(%3: !cir.eh_token):
+  %catch_token, %exn_ptr = cir.begin_catch %3 : !cir.eh_token -> 
(!cir.catch_token, !cir.ptr<!void>)
+  cir.init_catch_param reference %exn_ptr to %0 : !cir.ptr<!void>, 
!cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+  cir.end_catch %catch_token : !cir.catch_token
+  cir.br ^bb5
+^bb5:
+  cir.return
+^bb6(%4: !cir.eh_token):
+  cir.resume %4 : !cir.eh_token
+}
+
+// CHECK-LABEL: cir.func @ref_to_nullptr()
+// CHECK-NOT:     exn.byref.tmp
+// CHECK:         %[[N:.*]] = cir.alloca "n" align(8) : 
!cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+// CHECK:         cir.brcond %{{[0-9]+}} ^[[CATCH:bb[0-9]+]](
+// CHECK:       ^[[CATCH]](%[[EXN_ARG:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK-NEXT:    %[[EXN:.*]] = cir.call @__cxa_begin_catch(%[[EXN_ARG]]) : 
(!cir.ptr<!void>) -> !cir.ptr<!u8i>
+// CHECK-NEXT:    %[[EXN_VOID:.*]] = cir.cast bitcast %[[EXN]] : 
!cir.ptr<!u8i> -> !cir.ptr<!void>
+// CHECK-NEXT:    %[[EXN_CAST:.*]] = cir.cast bitcast %[[EXN_VOID]] : 
!cir.ptr<!void> -> !cir.ptr<!cir.ptr<!void>>
+// CHECK-NEXT:    cir.store %[[EXN_CAST]], %[[N]] : !cir.ptr<!cir.ptr<!void>>, 
!cir.ptr<!cir.ptr<!cir.ptr<!void>>>
+// CHECK-NEXT:    cir.call @__cxa_end_catch()
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_Rec = !cir.struct<"Rec" {data !s32i}>
+
+module attributes {cir.triple = "i386-unknown-linux-gnu",
+                   dlti.dl_spec = #dlti.dl_spec<
+                     #dlti.dl_entry<!cir.ptr<!cir.void>,
+                                    #cir.ptr_spec<size = 32, abi = 32,
+                                                  preferred = 32, index = 
32>>>} {
+
+cir.global "private" external @_ZTIP3Rec : !cir.ptr<!u8i>
+
+cir.func private @_Z8mayThrowv()
+
+// The exn.byref.tmp temporary takes the target's pointer alignment.
+cir.func @ref_to_record_ptr_32() personality(@__gxx_personality_v0) {
+  %0 = cir.alloca "p" align(4) : !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb5
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%2: !cir.eh_token):
+  cir.eh.dispatch %2 : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIP3Rec> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb6
+  ]
+^bb4(%3: !cir.eh_token):
+  %catch_token, %exn_ptr = cir.begin_catch %3 : !cir.eh_token -> 
(!cir.catch_token, !cir.ptr<!void>)
+  cir.init_catch_param reference_to_record_pointer %exn_ptr to %0 : 
!cir.ptr<!void>, !cir.ptr<!cir.ptr<!cir.ptr<!rec_Rec>>>
+  cir.end_catch %catch_token : !cir.catch_token
+  cir.br ^bb5
+^bb5:
+  cir.return
+^bb6(%4: !cir.eh_token):
+  cir.resume %4 : !cir.eh_token
+}
+
+// CHECK-LABEL: cir.func @ref_to_record_ptr_32()
+// CHECK:         cir.alloca "exn.byref.tmp" align(4) : 
!cir.ptr<!cir.ptr<!rec_Rec>>
+
+}
diff --git 
a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir 
b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
index 8d191bb05c8ac7..a0c194fdf95b94 100644
--- a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
@@ -30,7 +30,7 @@ cir.func linkonce_odr hidden 
@__clang_cir_catch_copy__ZTS1E_ok(
   cir.return
 }
 
-// Only non_trivial_copy is currently supported on Itanium.
+// Only reference_to_pointer and non_trivial_copy are supported on Itanium.
 cir.func @bad_kind() {
   %0 = cir.alloca "e" align(1) : !cir.ptr<!rec_E>
   cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
@@ -45,7 +45,7 @@ cir.func @bad_kind() {
     unwind : ^bb5
   ]
 ^bb4(%eh_token.1: !cir.eh_token):
-  // expected-error @below {{ConstructCatchParam: only non_trivial_copy is 
supported}}
+  // expected-error @below {{'cir.construct_catch_param' op only 
reference_to_pointer and non_trivial_copy kinds are supported}}
   cir.construct_catch_param trivial_copy %eh_token.1 to %0
       using @__clang_cir_catch_copy__ZTS1E_ok : !cir.ptr<!rec_E>
   %ct, %exn = cir.begin_catch %eh_token.1
@@ -173,3 +173,87 @@ cir.func @multi_block_thunk() {
 }
 
 }
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_E = !cir.struct<"E" {pad !u8i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+
+cir.global "private" external @_ZTIP1E : !cir.ptr<!u8i>
+
+cir.func private @_Z8mayThrowv()
+
+// reference_to_record_pointer has no cir.construct_catch_param lowering.
+cir.func @ref_to_record_ptr_kind() {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb6
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token: !cir.eh_token):
+  cir.eh.dispatch %eh_token : !cir.eh_token [
+    catch(#cir.global_view<@_ZTIP1E> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb5
+  ]
+^bb4(%eh_token.1: !cir.eh_token):
+  // expected-error @below {{'cir.construct_catch_param' op only 
reference_to_pointer and non_trivial_copy kinds are supported}}
+  cir.construct_catch_param reference_to_record_pointer %eh_token.1 to %0
+      using : !cir.ptr<!cir.ptr<!cir.ptr<!rec_E>>>
+  %ct, %exn = cir.begin_catch %eh_token.1
+      : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+  cir.end_catch %ct : !cir.catch_token
+  cir.br ^bb6
+^bb5(%eh_token.2: !cir.eh_token):
+  cir.resume %eh_token.2 : !cir.eh_token
+^bb6:
+  cir.return
+}
+
+}
+
+// -----
+
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_E = !cir.struct<"E" {pad !u8i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+
+cir.global "private" external @_ZTI1E : !cir.ptr<!u8i>
+
+cir.func private @_Z8mayThrowv()
+
+// reference has no cir.construct_catch_param lowering.
+cir.func @ref_kind() {
+  %0 = cir.alloca "p" align(8) : !cir.ptr<!cir.ptr<!rec_E>>
+  cir.try_call @_Z8mayThrowv() ^bb1, ^bb2 : () -> ()
+^bb1:
+  cir.br ^bb6
+^bb2:
+  %1 = cir.eh.initiate : !cir.eh_token
+  cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token: !cir.eh_token):
+  cir.eh.dispatch %eh_token : !cir.eh_token [
+    catch(#cir.global_view<@_ZTI1E> : !cir.ptr<!u8i>) : ^bb4,
+    unwind : ^bb5
+  ]
+^bb4(%eh_token.1: !cir.eh_token):
+  // expected-error @below {{'cir.construct_catch_param' op only 
reference_to_pointer and non_trivial_copy kinds are supported}}
+  cir.construct_catch_param reference %eh_token.1 to %0
+      using : !cir.ptr<!cir.ptr<!rec_E>>
+  %ct, %exn = cir.begin_catch %eh_token.1
+      : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+  cir.end_catch %ct : !cir.catch_token
+  cir.br ^bb6
+^bb5(%eh_token.2: !cir.eh_token):
+  cir.resume %eh_token.2 : !cir.eh_token
+^bb6:
+  cir.return
+}
+
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to