https://github.com/xakep8 updated 
https://github.com/llvm/llvm-project/pull/227595

>From 409c2fd9a4bad827c572a1272a86696b112a7059 Mon Sep 17 00:00:00 2001
From: Kunal Dubey <[email protected]>
Date: Wed, 30 Sep 2026 12:42:49 +0530
Subject: [PATCH 1/2] [CIR][EH] Preserved exception pointer address spaces in
 throw calls

CIR previously stored the exception type information and destructor as
symbol names. When these were converted into pointers, they could be
placed in the wrong address space.

Passed them as typed pointer values intead and preserved their address
spaces when creating the __cxa_throw call. Also verified that all throw
calls in a module use compatible pointer types and added HIP regression
tests.
---
 .../clang/CIR/Dialect/IR/CIRDataLayout.h      |   9 ++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  52 ++++----
 clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp |  30 +++--
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |   3 +-
 clang/lib/CIR/CodeGen/CIRGenModule.h          |   9 ++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |  65 +++++++++-
 .../Dialect/Transforms/CIRTransformUtils.cpp  |   4 +-
 .../CIR/Dialect/Transforms/EHABILowering.cpp  |  81 +++++-------
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  22 +---
 .../CodeGen/cleanup-scope-throw-caught.cpp    |   2 +-
 .../CodeGen/cleanup-throw-from-cleanup.cpp    |   4 +-
 .../test/CIR/CodeGen/rtti-member-pointer.cpp  |   2 +-
 clang/test/CIR/CodeGen/rtti-qualfn.cpp        |   2 +-
 clang/test/CIR/CodeGen/ternary-throw.cpp      |  38 +++---
 clang/test/CIR/CodeGen/throws.cpp             |  23 ++--
 clang/test/CIR/CodeGen/try-catch.cpp          |   2 +-
 clang/test/CIR/CodeGenHIP/throw-no-rtti.hip   |  83 ++++++++++++
 clang/test/CIR/IR/invalid-throw.cir           | 122 +++++++++++++++++-
 clang/test/CIR/IR/throw.cir                   |  12 ++
 19 files changed, 422 insertions(+), 143 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenHIP/throw-no-rtti.hip

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h 
b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
index 71214c798afbfa..7b19ad1b12e404 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
@@ -129,6 +129,15 @@ class CIRDataLayout {
       return {};
     return cir::TargetAddressSpaceAttr::get(ctx, allocaASAttr.getUInt());
   }
+
+  mlir::ptr::MemorySpaceAttrInterface
+  getGlobalAddrSpace(mlir::MLIRContext *ctx) const {
+    auto globalASAttr = mlir::dyn_cast_if_present<mlir::IntegerAttr>(
+        layout.getGlobalMemorySpace());
+    if (!globalASAttr || globalASAttr.getUInt() == 0)
+      return {};
+    return cir::TargetAddressSpaceAttr::get(ctx, globalASAttr.getUInt());
+  }
 };
 
 } // namespace cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d46b9135e618cf..7542ae32689d0d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -8537,20 +8537,23 @@ def CIR_VAArgOp : CIR_Op<"va_arg"> {
 
//===----------------------------------------------------------------------===//
 
 // Common base class shared by `cir.throw` and its EH counterpart
-// `cir.try_throw`. Both operations carry the same operands and attributes
-// (an optional exception pointer, an optional RTTI symbol and an optional
-// destructor symbol) and use the same verifier logic; subclasses contribute
-// any additional traits, successors and assembly format pieces.
+// `cir.try_throw`. A normal throw carries an exception pointer, an RTTI 
pointer
+// and a destructor pointer as SSA operands. Null RTTI and destructor pointers
+// are represented by typed null values. The zero-operand form represents a
+// rethrow. Since every normal throw lowers to the same `__cxa_throw` symbol,
+// the address spaces of these three operands must be consistent across the
+// module. Subclasses contribute any additional traits, successors and assembly
+// format pieces.
 class CIR_ThrowOpBase<string mnemonic, list<Trait> traits = []>
-    : CIR_Op<mnemonic, traits> {
+    : CIR_Op<mnemonic, !listconcat(traits, [AttrSizedOperandSegments])> {
   let arguments = (ins
     Optional<CIR_PointerType>:$exception_ptr,
-    OptionalAttr<FlatSymbolRefAttr>:$type_info,
-    OptionalAttr<FlatSymbolRefAttr>:$dtor
+    Optional<CIR_PointerType>:$type_info,
+    Optional<CIR_PointerType>:$dtor
   );
 
   let extraClassDeclaration = [{
-    bool rethrows() { return getNumOperands() == 0; }
+    bool rethrows() { return !getExceptionPtr(); }
   }];
 
   let hasVerifier = 1;
@@ -8566,12 +8569,12 @@ def CIR_ThrowOp : CIR_ThrowOpBase<"throw"> {
     This operation is equivalent to either __cxa_throw or __cxa_rethrow,
     depending on the arguments.
 
-    The absense of arguments for `cir.throw` means it rethrows.
+    The absence of arguments for `cir.throw` means it rethrows.
 
-    For the no-rethrow version, it must have at least two operands, the RTTI
-    information, a pointer to the exception object (likely allocated via
-    `cir.alloc_exception`) and finally an optional dtor, which might run as
-    part of this operation.
+    For the non-rethrow version, it has three operands: a pointer to the
+    exception object (likely allocated via `cir.alloc_exception`), the RTTI
+    pointer and the destructor pointer. The latter two may be typed null
+    pointers.
 
     Example:
 
@@ -8590,15 +8593,16 @@ def CIR_ThrowOp : CIR_ThrowOpBase<"throw"> {
       // Store string addr for "Division by zero condition!"
       cir.store %string_addr, %exception_addr : !cir.ptr<!s8i>,
         !cir.ptr<!cir.ptr<!s8i>>
-      cir.throw %exception_addr : !cir.ptr<!cir.ptr<!u8i>>,
-        @_ZTIPKc
+      %type_info = cir.const #cir.global_view<@_ZTIPKc> : !cir.ptr<!u8i>
+      %dtor = cir.const #cir.ptr<null> : !cir.ptr<!void>
+      cir.throw %exception_addr, %type_info, %dtor :
+        !cir.ptr<!cir.ptr<!u8i>>, !cir.ptr<!u8i>, !cir.ptr<!void>
     ```
   }];
 
   let assemblyFormat = [{
-    ($exception_ptr^ `:` type($exception_ptr))?
-    (`,` $type_info^)?
-    (`,` $dtor^)?
+    ($exception_ptr^ `,` $type_info `,` $dtor `:`
+      type($exception_ptr) `,` type($type_info) `,` type($dtor))?
     attr-dict
   }];
 }
@@ -8618,8 +8622,8 @@ def CIR_TryThrowOp : CIR_ThrowOpBase<"try_throw", 
[Terminator]> {
     analogous to how `cir.try_call` is the EH counterpart of `cir.call`.
 
     Like `cir.throw`, the absence of operands means rethrow. With operands,
-    it carries the same exception pointer, type info, and optional
-    destructor as `cir.throw`.
+    it carries the same exception pointer, type-info pointer, and destructor
+    pointer as `cir.throw`.
 
     This operation is produced by the FlattenCFG pass for `cir.throw`
     operations that appear inside a cleanup scope or try region. It is
@@ -8629,7 +8633,8 @@ def CIR_TryThrowOp : CIR_ThrowOpBase<"try_throw", 
[Terminator]> {
     Example:
 
     ```
-    cir.try_throw %exception_addr : !cir.ptr<!s32i>, @_ZTIi
+    cir.try_throw %exception_addr, %type_info, %dtor :
+        !cir.ptr<!s32i>, !cir.ptr<!u8i>, !cir.ptr<!void>
         ^normalDest, ^unwindDest
     ^normalDest:
       cir.unreachable
@@ -8644,9 +8649,8 @@ def CIR_TryThrowOp : CIR_ThrowOpBase<"try_throw", 
[Terminator]> {
   );
 
   let assemblyFormat = [{
-    ($exception_ptr^ `:` type($exception_ptr))?
-    (`,` $type_info^)?
-    (`,` $dtor^)?
+    ($exception_ptr^ `,` $type_info `,` $dtor `:`
+      type($exception_ptr) `,` type($type_info) `,` type($dtor))?
     $normalDest `,` $unwindDest
     attr-dict
   }];
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 25526f0668f00c..dc76457014e4dc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -1623,8 +1623,8 @@ mlir::Value 
CIRGenItaniumCXXABI::getCXXDestructorImplicitParam(
 // to the throw block and create a block for the remaining operations.
 static void insertThrowAndSplit(mlir::OpBuilder &builder, mlir::Location loc,
                                 mlir::Value exceptionPtr = {},
-                                mlir::FlatSymbolRefAttr typeInfo = {},
-                                mlir::FlatSymbolRefAttr dtor = {}) {
+                                mlir::Value typeInfo = {},
+                                mlir::Value dtor = {}) {
   mlir::Block *currentBlock = builder.getInsertionBlock();
   mlir::Region *region = currentBlock->getParent();
 
@@ -1681,11 +1681,16 @@ void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &cgf,
   CharUnits exnAlign = cgf.getContext().getExnObjectAlignment();
   cgf.emitAnyExprToExn(e->getSubExpr(), Address(exceptionPtr, exnAlign));
 
-  // Get the RTTI symbol address.
-  auto typeInfo = mlir::cast<cir::GlobalViewAttr>(
+  // Materialize the RTTI address as an SSA value. Device compilation may
+  // suppress RTTI emission, in which case this is a typed null pointer in the
+  // target's global address space instead of a view of an RTTI global.
+  auto typeInfoAttr = mlir::cast<mlir::TypedAttr>(
       cgm.getAddrOfRTTIDescriptor(subExprLoc, clangThrowType,
                                   /*forEH=*/true));
-  assert(!typeInfo.getIndices() && "expected no indirection");
+  if (auto globalView = mlir::dyn_cast<cir::GlobalViewAttr>(typeInfoAttr))
+    assert(!globalView.getIndices() && "expected no indirection");
+  mlir::Value typeInfo =
+      cir::ConstantOp::create(builder, subExprLoc, typeInfoAttr);
 
   // The address of the destructor.
   //
@@ -1695,21 +1700,26 @@ void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &cgf,
   // Lowering pass to skip passing the trivial function.
   //
   const auto *cxxrd = clangThrowType->getAsCXXRecordDecl();
-  mlir::FlatSymbolRefAttr dtor{};
+  cir::PointerType dtorTy = builder.getVoidPtrTy();
+  mlir::Value dtor = builder.getNullPtr(dtorTy, subExprLoc);
   if (cxxrd && !cxxrd->hasTrivialDestructor()) {
     // __cxa_throw is declared to take its destructor as void (*)(void *). We
     // must match that if function pointers can be authenticated with a
     // discriminator based on their type.
     assert(!cir::MissingFeatures::pointerAuthentication());
     CXXDestructorDecl *dtorD = cxxrd->getDestructor();
-    dtor = mlir::FlatSymbolRefAttr::get(
-        cgm.getAddrOfCXXStructor(GlobalDecl(dtorD, Dtor_Complete))
-            .getSymNameAttr());
+    cir::FuncOp dtorFunc =
+        cgm.getAddrOfCXXStructor(GlobalDecl(dtorD, Dtor_Complete));
+    cir::PointerType dtorFuncPtrTy =
+        builder.getPointerTo(dtorFunc.getFunctionType());
+    dtor = cir::GetGlobalOp::create(builder, subExprLoc, dtorFuncPtrTy,
+                                    dtorFunc.getSymName());
+    dtor = builder.createBitcast(dtor, dtorTy);
   }
 
   // Now throw the exception.
   mlir::Location loc = cgf.getLoc(e->getSourceRange());
-  insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo.getSymbol(), dtor);
+  insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo, dtor);
 }
 
 CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index ff4ab629e598cd..b64202bc64f401 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -4137,7 +4137,8 @@ mlir::Attribute 
CIRGenModule::getAddrOfRTTIDescriptor(mlir::Location loc,
   // FIXME: should we even be calling this method if RTTI is disabled
   // and it's not for EH?
   if (!shouldEmitRTTI(forEh))
-    return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
+    return builder.getConstNullPtrAttr(forEh ? getGlobalsUInt8PtrTy()
+                                             : builder.getUInt8PtrTy());
 
   if (forEh && ty->isObjCObjectPointerType() &&
       langOpts.ObjCRuntime.isGNUFamily()) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 05ba8b5ee212e0..39b5aa9d11c47a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -604,6 +604,15 @@ class CIRGenModule : public CIRGenTypeCache {
              getTriple().isNVPTX());
   }
 
+  /// Return an i8 pointer in the target's address space for global variables.
+  /// RTTI descriptors use this type, including the null descriptor passed to
+  /// the exception runtime when device compilation suppresses RTTI emission.
+  cir::PointerType getGlobalsUInt8PtrTy() {
+    return builder.getPointerTo(
+        builder.getUInt8Ty(),
+        getDataLayout().getGlobalAddrSpace(&getMLIRContext()));
+  }
+
   /// Emit type info if type of an expression is a variably modified
   /// type. Also emit proper debug info for cast types.
   void emitExplicitCastExprType(const ExplicitCastExpr *e,
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 2c5b785d5fc349..c45e2ab3a82888 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -35,6 +35,8 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/LogicalResult.h"
 
+#include <array>
+
 using namespace mlir;
 using namespace cir;
 
@@ -4934,17 +4936,68 @@ void InlineAsmOp::getEffects(
 // ThrowOp / TryThrowOp
 
//===----------------------------------------------------------------------===//
 
+using ThrowAddressSpaces = std::array<mlir::ptr::MemorySpaceAttrInterface, 3>;
+
+template <typename ThrowOpTy>
+static std::optional<ThrowAddressSpaces> getThrowAddressSpaces(ThrowOpTy op) {
+  if (!op.getExceptionPtr() || !op.getTypeInfo() || !op.getDtor())
+    return std::nullopt;
+
+  return ThrowAddressSpaces{
+      mlir::cast<cir::PointerType>(op.getExceptionPtr().getType())
+          .getAddrSpace(),
+      mlir::cast<cir::PointerType>(op.getTypeInfo().getType()).getAddrSpace(),
+      mlir::cast<cir::PointerType>(op.getDtor().getType()).getAddrSpace()};
+}
+
+static std::optional<ThrowAddressSpaces>
+getThrowAddressSpaces(mlir::Operation *op) {
+  if (auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op))
+    return getThrowAddressSpaces(throwOp);
+  if (auto tryThrowOp = mlir::dyn_cast<cir::TryThrowOp>(op))
+    return getThrowAddressSpaces(tryThrowOp);
+  return std::nullopt;
+}
+
 template <typename ThrowOpTy>
 static mlir::LogicalResult verifyThrowOpImpl(ThrowOpTy op) {
-  if (op.rethrows())
+  bool hasExceptionPtr = static_cast<bool>(op.getExceptionPtr());
+  bool hasTypeInfo = static_cast<bool>(op.getTypeInfo());
+  bool hasDtor = static_cast<bool>(op.getDtor());
+
+  if (!hasExceptionPtr && !hasTypeInfo && !hasDtor)
     return mlir::success();
 
-  if (op.getNumOperands() != 0) {
-    if (op.getTypeInfo())
-      return mlir::success();
-    return op.emitOpError() << "'type_info' symbol attribute missing";
-  }
+  if (!hasExceptionPtr || !hasTypeInfo || !hasDtor)
+    return op.emitOpError()
+           << "must have either no operands for a rethrow or exception, "
+              "type_info, and destructor pointer operands for a throw";
+
+  auto module = op->template getParentOfType<mlir::ModuleOp>();
+  if (!module)
+    return op.emitOpError("expects an enclosing module");
+
+  mlir::Operation *firstThrow = nullptr;
+  std::optional<ThrowAddressSpaces> expectedAddressSpaces;
+  module.walk([&](mlir::Operation *candidate) {
+    expectedAddressSpaces = getThrowAddressSpaces(candidate);
+    if (!expectedAddressSpaces)
+      return mlir::WalkResult::advance();
+    firstThrow = candidate;
+    return mlir::WalkResult::interrupt();
+  });
+
+  assert(expectedAddressSpaces && firstThrow &&
+         "the operation being verified provides a throw signature");
+  if (*expectedAddressSpaces == *getThrowAddressSpaces(op))
+    return mlir::success();
 
+  mlir::InFlightDiagnostic diag = op.emitOpError(
+      "operand address spaces must match the first non-rethrow cir.throw or "
+      "cir.try_throw in the module");
+  diag.attachNote(firstThrow->getLoc())
+      << "the module's __cxa_throw address-space signature is established "
+         "here";
   return mlir::failure();
 }
 
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp 
b/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
index 3d173cf56e6c26..6db17f4dea5088 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
@@ -120,8 +120,8 @@ mlir::Block *cir::replaceThrowWithTryThrow(cir::ThrowOp 
throwOp,
   // Build the try_throw to replace the original throw.
   rewriter.setInsertionPoint(throwOp);
   auto tryThrowOp = cir::TryThrowOp::create(
-      rewriter, loc, throwOp.getExceptionPtr(), throwOp.getTypeInfoAttr(),
-      throwOp.getDtorAttr(), normalDest, unwindDest);
+      rewriter, loc, throwOp.getExceptionPtr(), throwOp.getTypeInfo(),
+      throwOp.getDtor(), normalDest, unwindDest);
 
   // The shared inherent state is already set by TryThrowOp::create. Preserve
   // only auxiliary metadata here.
diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
index 72a8cffc3b0606..eaaed9beab2f0c 100644
--- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
@@ -142,7 +142,9 @@ class ItaniumEHLowering : public EHABILowering {
 
   void ensureRuntimeDecls(mlir::Location loc);
   void ensureClangCallTerminate(mlir::Location loc);
-  void ensureCxaThrowDecl(mlir::Location loc);
+  void ensureCxaThrowDecl(mlir::Location loc, cir::PointerType 
exceptionPtrType,
+                          cir::PointerType typeInfoPtrType,
+                          cir::PointerType dtorPtrType);
   void ensureCxaRethrowDecl(mlir::Location loc);
   void ensureCxaCallUnexpectedDecl(mlir::Location loc);
   mlir::Block *buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc);
@@ -268,12 +270,20 @@ void 
ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) {
 
 /// Ensure the __cxa_throw runtime function is declared in the module.
 ///
-///   void __cxa_throw(void *exception, void *type_info, void *dtor);
-void ItaniumEHLowering::ensureCxaThrowDecl(mlir::Location loc) {
+///   void __cxa_throw(void *exception, global void *type_info, void *dtor);
+///
+/// The type-info pointer may use a target-specific global address space, so
+/// derive the declaration from the typed throw operands instead of assuming
+/// that all three pointers use the default address space.
+void ItaniumEHLowering::ensureCxaThrowDecl(mlir::Location loc,
+                                           cir::PointerType exceptionPtrType,
+                                           cir::PointerType typeInfoPtrType,
+                                           cir::PointerType dtorPtrType) {
   if (cxaThrowFunc)
     return;
-  auto throwFuncTy = cir::FuncType::get({voidPtrType, voidPtrType, 
voidPtrType},
-                                        voidType, /*isVarArg=*/false);
+  auto throwFuncTy = cir::FuncType::get(
+      {exceptionPtrType, typeInfoPtrType, dtorPtrType}, voidType,
+      /*isVarArg=*/false);
   cxaThrowFunc =
       getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_throw", throwFuncTy);
 }
@@ -909,9 +919,9 @@ 
ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op,
 }
 
 /// Lower a cir.try_throw to a cir.try_call of __cxa_throw (or
-/// __cxa_rethrow for the no-operand rethrow form). Materializes the
-/// type_info and dtor pointers from their symbol attributes, bitcasting
-/// each to !cir.ptr<!void> as required by the runtime function signature.
+/// __cxa_rethrow for the no-operand rethrow form). Bitcast each typed pointer
+/// operand to a void pointer in the same address space to match the runtime
+/// function signature.
 mlir::LogicalResult ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
   mlir::Location loc = op.getLoc();
   mlir::Block *normalDest = op.getNormalDest();
@@ -927,45 +937,22 @@ mlir::LogicalResult 
ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
     return mlir::success();
   }
 
-  ensureCxaThrowDecl(loc);
-
-  // Bitcast the exception pointer to void* if necessary.
-  mlir::Value exnPtr = op.getExceptionPtr();
-  if (exnPtr.getType() != voidPtrType)
-    exnPtr = cir::CastOp::create(builder, loc, voidPtrType,
-                                 cir::CastKind::bitcast, exnPtr);
-
-  // Materialize the type_info pointer, looking up the typed symbol in the
-  // module so we get the correct pointer type for cir.get_global, then
-  // bitcasting to void* to match the runtime signature.
-  mlir::FlatSymbolRefAttr typeInfoAttr = op.getTypeInfoAttr();
-  auto typeInfoGlobal = mod.lookupSymbol<cir::GlobalOp>(typeInfoAttr);
-  if (!typeInfoGlobal)
-    return op.emitError("type_info symbol not found in module");
-  auto typeInfoPtrTy = cir::PointerType::get(typeInfoGlobal.getSymType());
-  mlir::Value typeInfo = cir::GetGlobalOp::create(builder, loc, typeInfoPtrTy,
-                                                  typeInfoAttr.getValue());
-  if (typeInfo.getType() != voidPtrType)
-    typeInfo = cir::CastOp::create(builder, loc, voidPtrType,
-                                   cir::CastKind::bitcast, typeInfo);
-
-  // Materialize the dtor pointer (or null if no dtor).
-  mlir::Value dtor;
-  if (mlir::FlatSymbolRefAttr dtorAttr = op.getDtorAttr()) {
-    auto dtorFunc = mod.lookupSymbol<cir::FuncOp>(dtorAttr);
-    if (!dtorFunc)
-      return op.emitError("dtor symbol not found in module");
-    auto dtorPtrTy = cir::PointerType::get(dtorFunc.getFunctionType());
-    dtor =
-        cir::GetGlobalOp::create(builder, loc, dtorPtrTy, dtorAttr.getValue());
-    if (dtor.getType() != voidPtrType)
-      dtor = cir::CastOp::create(builder, loc, voidPtrType,
-                                 cir::CastKind::bitcast, dtor);
-  } else {
-    dtor = cir::ConstantOp::create(
-        builder, loc,
-        cir::ConstPtrAttr::get(voidPtrType, builder.getI64IntegerAttr(0)));
-  }
+  auto castToVoidPtr = [&](mlir::Value value) -> mlir::Value {
+    auto ptrType = mlir::cast<cir::PointerType>(value.getType());
+    auto targetType = cir::PointerType::get(voidType, ptrType.getAddrSpace());
+    if (value.getType() == targetType)
+      return value;
+    return cir::CastOp::create(builder, loc, targetType, 
cir::CastKind::bitcast,
+                               value);
+  };
+
+  mlir::Value exnPtr = castToVoidPtr(op.getExceptionPtr());
+  mlir::Value typeInfo = castToVoidPtr(op.getTypeInfo());
+  mlir::Value dtor = castToVoidPtr(op.getDtor());
+
+  ensureCxaThrowDecl(loc, mlir::cast<cir::PointerType>(exnPtr.getType()),
+                     mlir::cast<cir::PointerType>(typeInfo.getType()),
+                     mlir::cast<cir::PointerType>(dtor.getType()));
 
   cir::TryCallOp::create(
       builder, loc, mlir::FlatSymbolRefAttr::get(cxaThrowFunc), voidType,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index a04aca6b63b431..9c92288c1cafcb 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -4751,29 +4751,19 @@ mlir::LogicalResult 
CIRToLLVMThrowOpLowering::matchAndRewrite(
     return mlir::success();
   }
 
-  auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+  mlir::Value exceptionPtr = adaptor.getExceptionPtr();
+  mlir::Value typeInfo = adaptor.getTypeInfo();
+  mlir::Value dtor = adaptor.getDtor();
   auto fnTy = mlir::LLVM::LLVMFunctionType::get(
-      voidTy, {llvmPtrTy, llvmPtrTy, llvmPtrTy});
+      voidTy, {exceptionPtr.getType(), typeInfo.getType(), dtor.getType()});
 
-  // Get or create `declare void @__cxa_throw(ptr, ptr, ptr)`
+  // Get or create the __cxa_throw declaration with the operand pointer types.
   const llvm::StringRef fnName = "__cxa_throw";
   createLLVMFuncOpIfNotExist(rewriter, symbolTables, op, fnName, fnTy);
 
-  mlir::Value typeInfo = mlir::LLVM::AddressOfOp::create(
-      rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
-      adaptor.getTypeInfoAttr());
-
-  mlir::Value dtor;
-  if (op.getDtor()) {
-    dtor = mlir::LLVM::AddressOfOp::create(rewriter, loc, llvmPtrTy,
-                                           adaptor.getDtorAttr());
-  } else {
-    dtor = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
-  }
-
   auto cxaThrowCall = mlir::LLVM::CallOp::create(
       rewriter, loc, mlir::TypeRange{}, fnName,
-      mlir::ValueRange{adaptor.getExceptionPtr(), typeInfo, dtor});
+      mlir::ValueRange{exceptionPtr, typeInfo, dtor});
 
   rewriter.replaceOp(op, cxaThrowCall);
   return mlir::success();
diff --git a/clang/test/CIR/CodeGen/cleanup-scope-throw-caught.cpp 
b/clang/test/CIR/CodeGen/cleanup-scope-throw-caught.cpp
index ff313033e9a850..c9fc0fcc2cdb2c 100644
--- a/clang/test/CIR/CodeGen/cleanup-scope-throw-caught.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-scope-throw-caught.cpp
@@ -31,7 +31,7 @@ int testCaughtThrowSharedDispatch() {
 // CIR:   cir.try {
 // CIR:     cir.call @_ZN1CC1Ev(%[[A]])
 // CIR:     cir.cleanup.scope {
-// CIR:       cir.throw %{{.*}} : !cir.ptr<!cir.ptr<!rec_C>>, @_ZTIP1C
+// CIR:       cir.throw %{{.*}}, %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:       cir.unreachable
 // CIR:     } cleanup all {
 // CIR:       cir.call @_ZN1CD1Ev(%[[A]]) nothrow
diff --git a/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp 
b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
index 6b71f5d0683c1a..e5d2fae458dcb1 100644
--- a/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
@@ -24,7 +24,7 @@ void testSwitchWithCleanup(int n) {
 // CIR:     %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR:     %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
 // CIR:     cir.store{{.*}} %[[VAL]], %[[EXN]] : !s32i, !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXN]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR:     cir.yield
 // CIR:   } cleanup all {
@@ -47,7 +47,7 @@ void testSwitchWithCleanup(int n) {
 // CIR-FLAT:   %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR-FLAT:   %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
 // CIR-FLAT:   cir.store{{.*}} %[[VAL]], %[[EXN]]
-// CIR-FLAT:   cir.try_throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi 
^[[UNREACH:.+]], ^[[UNWIND:.+]]
+// CIR-FLAT:   cir.try_throw %[[EXN]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void> ^[[UNREACH:.+]], ^[[UNWIND:.+]]
 // CIR-FLAT-NEXT: ^[[UNREACH]]:
 // CIR-FLAT-NEXT:   cir.unreachable
 // CIR-FLAT: ^[[UNWIND]]:
diff --git a/clang/test/CIR/CodeGen/rtti-member-pointer.cpp 
b/clang/test/CIR/CodeGen/rtti-member-pointer.cpp
index 93975ebd0814d1..d5b901dcce4e40 100644
--- a/clang/test/CIR/CodeGen/rtti-member-pointer.cpp
+++ b/clang/test/CIR/CodeGen/rtti-member-pointer.cpp
@@ -21,7 +21,7 @@ void throw_data_member_ptr() {
 // CIR-DAG: cir.global {{.*}} @_ZTSM1Ai = #cir.const_array<"M1Ai" : 
!cir.array<!s8i x 4>, trailing_zeros> : !cir.array<!s8i x 5>
 // CIR-DAG: cir.global {{.*}} @_ZTIM1Ai = 
#cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv129__pointer_to_member_type_infoE,
 [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSM1Ai> : !cir.ptr<!u8i>, 
#cir.int<0> : !u32i, #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>, 
#cir.global_view<@_ZTI1A> : !cir.ptr<!u8i>}>
 
-// CIR-DAG: cir.throw %{{.*}} : !cir.ptr<!s64i>, @_ZTIM1Ai
+// CIR-DAG: cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s64i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 
 // LLVM-DAG: @_ZTSM1Ai = linkonce_odr constant [5 x i8] c"M1Ai\00", comdat
 // LLVM-DAG: @_ZTS1A = linkonce_odr constant [3 x i8] c"1A\00", comdat
diff --git a/clang/test/CIR/CodeGen/rtti-qualfn.cpp 
b/clang/test/CIR/CodeGen/rtti-qualfn.cpp
index 5fbc782223a98f..db4c71303f18de 100644
--- a/clang/test/CIR/CodeGen/rtti-qualfn.cpp
+++ b/clang/test/CIR/CodeGen/rtti-qualfn.cpp
@@ -21,7 +21,7 @@ void f() noexcept {
 // CIR-DAG: cir.global {{.*}} @_ZTSPDoFvvE = #cir.const_array<"PDoFvvE" : 
!cir.array<!s8i x 7>, trailing_zeros> : !cir.array<!s8i x 8>
 // CIR-DAG: cir.global {{.*}} @_ZTIPDoFvvE = 
#cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv119__pointer_type_infoE, [2 : 
i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSPDoFvvE> : !cir.ptr<!u8i>, 
#cir.int<64> : !u32i, #cir.global_view<@_ZTIFvvE> : !cir.ptr<!u8i>}>
 
-// CIR: cir.throw %{{.*}} : !cir.ptr<!cir.ptr<!cir.func<()>>>, @_ZTIPDoFvvE
+// CIR: cir.throw %{{.*}}, %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.ptr<!cir.func<()>>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 
 // LLVM-DAG: @_ZTSFvvE = linkonce_odr constant [5 x i8] c"FvvE\00", comdat
 // LLVM-DAG: @_ZTSPDoFvvE = linkonce_odr constant [8 x i8] c"PDoFvvE\00", 
comdat
diff --git a/clang/test/CIR/CodeGen/ternary-throw.cpp 
b/clang/test/CIR/CodeGen/ternary-throw.cpp
index ec617bea1c930b..0fcf79a51a2a64 100644
--- a/clang/test/CIR/CodeGen/ternary-throw.cpp
+++ b/clang/test/CIR/CodeGen/ternary-throw.cpp
@@ -22,7 +22,7 @@ const int& test_cond_throw_false(bool flag) {
 // CIR:   %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i>
-// CIR:   cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:   cir.throw %[[EXCEPTION]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:   cir.unreachable
 // CIR-NEXT: }) : (!cir.bool) -> !cir.ptr<!s32i>
 
@@ -83,7 +83,7 @@ const int& test_cond_throw_true(bool flag) {
 // CIR:   %[[EXCEPTION:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store{{.*}} %[[ZERO]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i>
-// CIR:   cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:   cir.throw %[[EXCEPTION]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:   cir.unreachable
 // CIR-NEXT: }, false {
 // CIR:   cir.yield %[[A]] : !cir.ptr<!s32i>
@@ -208,7 +208,7 @@ const int &test_cond_const_true_throw_true() {
 // CIR:  %[[EXCEPTION:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR:  %[[CONST_0:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:  cir.store{{.*}} %[[CONST_0]], %[[EXCEPTION]] : !s32i, !cir.ptr<!s32i>
-// CIR:  cir.throw %[[EXCEPTION]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:  cir.throw %[[EXCEPTION]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:  cir.unreachable
 // CIR: ^[[NO_PRED_LABEL:.*]]:
 // CIR:   %[[CONST_NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
@@ -255,7 +255,7 @@ int test_agg_cond_throw_false(bool flag, struct s6 a1, 
struct s6 a2) {
 // CIR:   %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR:   cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:   cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:   cir.unreachable
 // CIR: }) : (!cir.bool) -> !cir.ptr<!rec_s6>
 // CIR: %[[F0:.*]] = cir.get_member %[[COND_RES]][0] {name = "f0"} : 
!cir.ptr<!rec_s6> -> !cir.ptr<!s32i>
@@ -318,7 +318,7 @@ int test_agg_cond_throw_true(bool flag, struct s6 a1, 
struct s6 a2) {
 // CIR:   %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR:   cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:   cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:   cir.unreachable
 // CIR: }, false {
 // CIR:   cir.yield %[[A1]] : !cir.ptr<!rec_s6>
@@ -416,7 +416,7 @@ const int test_agg_cond_const_true_throw_true(struct s6 a1, 
struct s6 a2) {
 // CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR: cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR: cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 // CIR: ^[[NO_PRED:.*]]:
 // CIR: %[[NULL_REC:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_s6>
@@ -456,7 +456,7 @@ const int test_agg_cond_const_false_throw_false(struct s6 
a1, struct s6 a2) {
 // CIR: %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR: cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR: cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR: cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 // CIR: ^[[NO_PRED:.*]]:
 // CIR: %[[NULL_REC:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_s6>
@@ -539,7 +539,7 @@ void test_agg_throw_true(bool flag) {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:     %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:     cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   } else {
 // CIR:     %[[X:.*]] = cir.get_member %[[A]][0] {name = "x"} : 
!cir.ptr<!rec_Agg> -> !cir.ptr<!s32i>
@@ -608,7 +608,7 @@ void test_agg_throw_false(bool flag) {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
 // CIR:     %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:     cir.store{{.*}} %[[ZERO]], %[[EXC]] : !s32i, !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }
 // CIR:   cir.return
@@ -659,7 +659,7 @@ int test_scalar_throw_true(bool flag, int x) {
 // CIR:   %[[COND:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool
 // CIR:   %{{.*}} = cir.ternary(%[[COND]], true {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }, false {
 // CIR:     %[[X:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
@@ -693,7 +693,7 @@ int test_scalar_throw_false(bool flag, int x) {
 // CIR:     cir.yield %[[X]] : !s32i
 // CIR:   }, false {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }) : (!cir.bool) -> !s32i
 
@@ -715,10 +715,10 @@ void test_both_throw(bool flag) {
 
 // CIR-LABEL: cir.func {{.*}} @_Z15test_both_throwb(
 // CIR:   cir.ternary(%{{.*}}, true {
-// CIR:     cir.throw %{{.*}} : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }, false {
-// CIR:     cir.throw %{{.*}} : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   })
 
@@ -757,7 +757,7 @@ int &test_ref_cond_throw(bool c, int &x) {
 // CIR:     %[[X_PTR:.*]] = cir.load %[[X_REF]] : !cir.ptr<!cir.ptr<!s32i>>, 
!cir.ptr<!s32i>
 // CIR:     cir.yield %[[X_PTR]] : !cir.ptr<!s32i>
 // CIR-NEXT:   }, false {
-// CIR:     cir.throw {{.*}} @_ZTIi
+// CIR:     cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }) : (!cir.bool) -> !cir.ptr<!s32i>
 // CIR:   cir.store %[[RES]], %[[RET_ADDR]] : !cir.ptr<!s32i>, 
!cir.ptr<!cir.ptr<!s32i>>
@@ -799,7 +799,7 @@ void test_assign_through_cond(bool c, int &x) {
 // CIR:     %[[X_PTR:.*]] = cir.load %[[X_REF]] : !cir.ptr<!cir.ptr<!s32i>>, 
!cir.ptr<!s32i>
 // CIR:     cir.yield %[[X_PTR]] : !cir.ptr<!s32i>
 // CIR-NEXT:   }, false {
-// CIR:     cir.throw {{.*}} @_ZTIi
+// CIR:     cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }) : (!cir.bool) -> !cir.ptr<!s32i>
 // CIR:   cir.store{{.*}} %[[FIVE]], %[[RES]] : !s32i, !cir.ptr<!s32i>
@@ -841,7 +841,7 @@ int &test_member_cond_throw(bool c, struct s6 *p) {
 // CIR:     %[[F0:.*]] = cir.get_member %[[P]][0] {name = "f0"} : 
!cir.ptr<!rec_s6> -> !cir.ptr<!s32i>
 // CIR:     cir.yield %[[F0]] : !cir.ptr<!s32i>
 // CIR-NEXT:   }, false {
-// CIR:     cir.throw {{.*}} @_ZTIi
+// CIR:     cir.throw %{{.*}}, %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   }) : (!cir.bool) -> !cir.ptr<!s32i>
 // CIR:   cir.store %[[RES]], %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!cir.ptr<!s32i>>
@@ -941,7 +941,7 @@ void test_agg_assign_throw(bool c, Agg &a) {
 // CIR:   %[[C_VAL:.*]] = cir.load{{.*}} %[[C]] : !cir.ptr<!cir.bool>, 
!cir.bool
 // CIR:   cir.if %[[C_VAL]] {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   } else {
 // CIR:     cir.get_member %[[TMP]][0] {name = "x"}
@@ -988,7 +988,7 @@ void test_agg_nested_throw(bool c1, bool c2) {
 // CIR:     %[[C2_VAL:.*]] = cir.load{{.*}} : !cir.ptr<!cir.bool>, !cir.bool
 // CIR:     cir.if %[[C2_VAL]] {
 // CIR:       %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
-// CIR:       cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:       cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:       cir.unreachable
 // CIR-NEXT:     } else {
 // CIR:       cir.get_member %[[A]][0] {name = "x"}
@@ -1038,7 +1038,7 @@ void test_agg_arg_throw(bool c) {
 // CIR:   %[[TMP:.*]] = cir.alloca "agg.tmp0" {{.*}} : !cir.ptr<!rec_Agg>
 // CIR:   cir.if %{{.*}} {
 // CIR:     %[[EXC:.*]] = cir.alloc.exception{{.*}} -> !cir.ptr<!s32i>
-// CIR:     cir.throw %[[EXC]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.throw %[[EXC]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:     cir.unreachable
 // CIR-NEXT:   } else {
 // CIR:     cir.get_member %[[TMP]][0] {name = "x"}
diff --git a/clang/test/CIR/CodeGen/throws.cpp 
b/clang/test/CIR/CodeGen/throws.cpp
index 4023f41e2c1684..acd478aadd346c 100644
--- a/clang/test/CIR/CodeGen/throws.cpp
+++ b/clang/test/CIR/CodeGen/throws.cpp
@@ -230,7 +230,9 @@ void throw_scalar() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<1> : !s32i
 // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, 
!cir.ptr<!s32i>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR: %[[TYPE_INFO:.*]] = cir.const #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>
+// CIR: %[[DTOR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %[[TYPE_INFO]], %[[DTOR]] : 
!cir.ptr<!s32i>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
@@ -248,7 +250,7 @@ void paren_expr() { (throw 0, 1 + 2); }
 // CIR:   %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR:   %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, 
!cir.ptr<!s32i>
-// CIR:   cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:   cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:   cir.unreachable
 // CIR: ^bb1:
 // CIR:   cir.return
@@ -268,7 +270,7 @@ void throw_complex_expr() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 8 -> 
!cir.ptr<!cir.complex<!cir.float>>
 // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const 
#cir.const_complex<#cir.fp<1.100000e+00> : !cir.float, #cir.fp<2.200000e+00> : 
!cir.float> : !cir.complex<!cir.float>
 // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : 
!cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!cir.complex<!cir.float>>, 
@_ZTICf
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 8)
@@ -292,7 +294,7 @@ void throw_vector_type() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 16 -> 
!cir.ptr<!cir.vector<4 x !s32i>>
 // CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.vector<4 x 
!s32i>>, !cir.vector<4 x !s32i>
 // CIR: cir.store{{.*}} %[[TMP_A]], %[[EXCEPTION_ADDR]] : !cir.vector<4 x 
!s32i>, !cir.ptr<!cir.vector<4 x !s32i>>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, 
@_ZTIDv4_i
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.vector<4 x !s32i>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[A_ADDR:.*]] = alloca <4 x i32>, align 16
@@ -318,7 +320,7 @@ void throw_ext_vector_type() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 16 -> 
!cir.ptr<!cir.vector<4 x !s32i>>
 // CIR: %[[TMP_A:.*]] = cir.load{{.*}} %[[A_ADDR]] : !cir.ptr<!cir.vector<4 x 
!s32i>>, !cir.vector<4 x !s32i>
 // CIR: cir.store{{.*}} %[[TMP_A]], %[[EXCEPTION_ADDR]] : !cir.vector<4 x 
!s32i>, !cir.ptr<!cir.vector<4 x !s32i>>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!cir.vector<4 x !s32i>>, 
@_ZTIDv4_i
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.vector<4 x !s32i>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[A_ADDR:.*]] = alloca <4 x i32>, align 16
@@ -345,7 +347,7 @@ void throw_enum_expr() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!u32i>
 // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !u32i
 // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !u32i, 
!cir.ptr<!u32i>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!u32i>, 
@_ZTIZ15throw_enum_exprvE4Test
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : !cir.ptr<!u32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
@@ -369,7 +371,7 @@ void throw_enum_class_expr() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !s32i
 // CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, 
!cir.ptr<!s32i>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, 
@_ZTIZ21throw_enum_class_exprvE4Test
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
@@ -394,7 +396,7 @@ void throw_pointer_type() {
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 8 -> 
!cir.ptr<!cir.ptr<!s32i>>
 // CIR: %[[TMP_PTR:.*]] = cir.load{{.*}} %[[PTR_ADDR]] : 
!cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
 // CIR: cir.store{{.*}} %[[TMP_PTR]], %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, 
!cir.ptr<!cir.ptr<!s32i>>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!cir.ptr<!s32i>>, @_ZTIPi
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %{{.*}}, %{{.*}} : 
!cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[PTR_ADDR:.*]] = alloca ptr,{{.*}} align 8
@@ -419,7 +421,10 @@ struct ThrowNonTrivialDtor {
 void throw_class_with_nontrivial_dtor() { throw ThrowNonTrivialDtor(); }
 
 // CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 1 -> 
!cir.ptr<!rec_ThrowNonTrivialDtor>
-// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!rec_ThrowNonTrivialDtor>, 
@_ZTI19ThrowNonTrivialDtor, @_ZN19ThrowNonTrivialDtorD1Ev
+// CIR: %[[TYPE_INFO:.*]] = cir.const 
#cir.global_view<@_ZTI19ThrowNonTrivialDtor> : !cir.ptr<!u8i>
+// CIR: %[[DTOR_ADDR:.*]] = cir.get_global @_ZN19ThrowNonTrivialDtorD1Ev
+// CIR: %[[DTOR:.*]] = cir.cast bitcast %[[DTOR_ADDR]] : {{.*}} -> 
!cir.ptr<!void>
+// CIR: cir.throw %[[EXCEPTION_ADDR]], %[[TYPE_INFO]], %[[DTOR]] : 
!cir.ptr<!rec_ThrowNonTrivialDtor>, !cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR: cir.unreachable
 
 // LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 1)
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp 
b/clang/test/CIR/CodeGen/try-catch.cpp
index 68d00449a304f7..8c854127b89fce 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1784,7 +1784,7 @@ void direct_inside_try_catch_with_exception_type() {
 // CIR:       %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR:       %[[FORTYTWO:.*]] = cir.const #cir.int<42> : !s32i
 // CIR:       cir.store{{.*}} %[[FORTYTWO]], %[[EXN]]
-// CIR:       cir.throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:       cir.throw %[[EXN]], %{{.*}}, %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!u8i>, !cir.ptr<!void>
 // CIR:       cir.unreachable
 // CIR:     } catch [type #cir.global_view<@_ZTIi> : !cir.ptr<!u8i>] 
(%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
 // CIR:       %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %{{.*}} : 
!cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
diff --git a/clang/test/CIR/CodeGenHIP/throw-no-rtti.hip 
b/clang/test/CIR/CodeGenHIP/throw-no-rtti.hip
new file mode 100644
index 00000000000000..7bc3a1fafff7b2
--- /dev/null
+++ b/clang/test/CIR/CodeGenHIP/throw-no-rtti.hip
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 -std=c++17 -triple amdgcn-amd-amdhsa -x hip \
+// RUN:   -fcuda-is-device --hipstdpar -fcxx-exceptions -fexceptions \
+// RUN:   -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple amdgcn-amd-amdhsa -x hip \
+// RUN:   -fcuda-is-device --hipstdpar -fcxx-exceptions -fexceptions \
+// RUN:   -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++17 -triple amdgcn-amd-amdhsa -x hip \
+// RUN:   -fcuda-is-device --hipstdpar -fcxx-exceptions -fexceptions \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=OGCG
+
+struct X {
+  ~X();
+};
+
+struct Guard {
+  ~Guard();
+};
+
+inline void throw_int() {
+  throw 1;
+}
+
+inline void throw_object() {
+  throw X{};
+}
+
+inline void throw_with_cleanup() {
+  Guard guard;
+  throw 2;
+}
+
+inline void throw_object_with_cleanup() {
+  Guard guard;
+  throw X{};
+}
+
+int main() {
+  throw_int();
+  throw_object();
+  throw_with_cleanup();
+  throw_object_with_cleanup();
+}
+
+// Device-side HIP compilation disables RTTI. The Itanium throw ABI still
+// requires a type-info argument, so use a null pointer in the target's global
+// address space instead of trying to create a reference to a missing RTTI
+// global.
+
+// CIR: module
+// CIR-NOT: cir.global {{.*}} @_ZTI
+
+// CIR-LABEL: cir.func{{.*}} @_Z9throw_intv()
+// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
+// CIR: %[[TYPE_INFO:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!u8i, 
target_address_space(1)>
+// CIR: %[[DTOR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR: cir.throw %[[EXCEPTION]], %[[TYPE_INFO]], %[[DTOR]] : !cir.ptr<!s32i>, 
!cir.ptr<!u8i, target_address_space(1)>, !cir.ptr<!void>
+
+// CIR-LABEL: cir.func{{.*}} @_Z12throw_objectv()
+// CIR: %[[EXCEPTION:.*]] = cir.alloc.exception 1 -> !cir.ptr<!rec_X>
+// CIR: %[[TYPE_INFO:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!u8i, 
target_address_space(1)>
+// CIR: %[[DTOR_ADDR:.*]] = cir.get_global @_ZN1XD1Ev
+// CIR: %[[DTOR:.*]] = cir.cast bitcast %[[DTOR_ADDR]] : {{.*}} -> 
!cir.ptr<!void>
+// CIR: cir.throw %[[EXCEPTION]], %[[TYPE_INFO]], %[[DTOR]] : 
!cir.ptr<!rec_X>, !cir.ptr<!u8i, target_address_space(1)>, !cir.ptr<!void>
+
+// LLVM-LABEL: define{{.*}} void @_Z9throw_intv()
+// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr null)
+// LLVM-LABEL: define{{.*}} void @_Z12throw_objectv()
+// LLVM: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr 
@_ZN1XD1Ev)
+// LLVM-LABEL: define{{.*}} void @_Z18throw_with_cleanupv()
+// LLVM: invoke void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr null)
+// LLVM-LABEL: define{{.*}} void @_Z25throw_object_with_cleanupv()
+// LLVM: invoke void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr 
@_ZN1XD1Ev)
+// LLVM: declare void @__cxa_throw(ptr, ptr addrspace(1), ptr)
+
+// OGCG-LABEL: define{{.*}} void @_Z9throw_intv()
+// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr null)
+// OGCG-LABEL: define{{.*}} void @_Z12throw_objectv()
+// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr 
@_ZN1XD1Ev)
+// OGCG-LABEL: define{{.*}} void @_Z18throw_with_cleanupv()
+// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr null)
+// OGCG-LABEL: define{{.*}} void @_Z25throw_object_with_cleanupv()
+// OGCG: call void @__cxa_throw(ptr %{{.*}}, ptr addrspace(1) null, ptr 
@_ZN1XD1Ev)
+// OGCG: declare void @__cxa_throw(ptr, ptr addrspace(1), ptr)
diff --git a/clang/test/CIR/IR/invalid-throw.cir 
b/clang/test/CIR/IR/invalid-throw.cir
index 114767a9ad7153..50b8759cfba87d 100644
--- a/clang/test/CIR/IR/invalid-throw.cir
+++ b/clang/test/CIR/IR/invalid-throw.cir
@@ -4,13 +4,129 @@
 
 module {
 
-cir.func dso_local @throw_without_type_info() {
+cir.func dso_local @throw_with_incomplete_operands() {
     %0 = cir.alloca "a" align(4) : !cir.ptr<!s32i>
-    // expected-error @below {{'cir.throw' op 'type_info' symbol attribute 
missing}}
-    cir.throw %0 : !cir.ptr<!s32i>
+    // expected-error @below {{'cir.throw' op must have either no operands for 
a rethrow or exception, type_info, and destructor pointer operands for a throw}}
+    "cir.throw"(%0) <{operandSegmentSizes = array<i32: 1, 0, 0>}> : 
(!cir.ptr<!s32i>) -> ()
     cir.unreachable
   ^bb1:
     cir.return
 }
 
 }
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.func @throw_with_as1(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
+  cir.unreachable
+}
+
+cir.func @throw_with_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(2)>, %dtor: !cir.ptr<!void>) {
+  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
+  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(2)>, !cir.ptr<!void>
+  cir.unreachable
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.func @throw_with_default_spaces(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
+  cir.unreachable
+}
+
+cir.func @throw_with_exception_as2(%exception: !cir.ptr<!s32i, 
target_address_space(2)>, %type_info: !cir.ptr<!u8i, target_address_space(1)>, 
%dtor: !cir.ptr<!void>) {
+  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
+  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i, 
target_address_space(2)>, !cir.ptr<!u8i, target_address_space(1)>, 
!cir.ptr<!void>
+  cir.unreachable
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.func @throw_with_default_spaces(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
+  cir.unreachable
+}
+
+cir.func @throw_with_dtor_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void, 
target_address_space(2)>) {
+  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
+  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void, target_address_space(2)>
+  cir.unreachable
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+cir.func @throw_with_as1(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
+  cir.unreachable
+}
+
+cir.func @try_throw_with_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(2)>, %dtor: !cir.ptr<!void>) {
+  // expected-error @below {{'cir.try_throw' op operand address spaces must 
match the first non-rethrow cir.throw or cir.try_throw in the module}}
+  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  cir.try_throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, 
!cir.ptr<!u8i, target_address_space(2)>, !cir.ptr<!void> ^normal, ^unwind
+^normal:
+  cir.unreachable
+^unwind:
+  cir.unreachable
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module {
+
+// Different pointee types are valid when the address-space signature is the
+// same.
+cir.func @try_throw_one(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
+  cir.try_throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, 
!cir.ptr<!u8i, target_address_space(1)>, !cir.ptr<!void> ^normal, ^unwind
+^normal:
+  cir.unreachable
+^unwind:
+  cir.unreachable
+}
+
+cir.func @throw_two(%exception: !cir.ptr<!u8i>, %type_info: !cir.ptr<!void, 
target_address_space(1)>, %dtor: !cir.ptr<!s32i>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!u8i>, !cir.ptr<!void, 
target_address_space(1)>, !cir.ptr<!s32i>
+  cir.unreachable
+}
+
+}
diff --git a/clang/test/CIR/IR/throw.cir b/clang/test/CIR/IR/throw.cir
index d47d17efe3383e..494f1a13e546cc 100644
--- a/clang/test/CIR/IR/throw.cir
+++ b/clang/test/CIR/IR/throw.cir
@@ -1,6 +1,8 @@
 // RUN: cir-opt %s --verify-roundtrip | FileCheck %s
 
 !s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
 
 module  {
 
@@ -14,6 +16,16 @@ cir.func @throw_with_no_return() {
 // CHECK:  cir.unreachable
 // CHECK: }
 
+cir.func @throw_with_operands(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i>, %dtor: !cir.ptr<!void>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i>, 
!cir.ptr<!void>
+  cir.unreachable
+}
+
+// CHECK: cir.func @throw_with_operands(%[[EXCEPTION:.*]]: !cir.ptr<!s32i>, 
%[[TYPE_INFO:.*]]: !cir.ptr<!u8i>, %[[DTOR:.*]]: !cir.ptr<!void>) {
+// CHECK:   cir.throw %[[EXCEPTION]], %[[TYPE_INFO]], %[[DTOR]] : 
!cir.ptr<!s32i>, !cir.ptr<!u8i>, !cir.ptr<!void>
+// CHECK:   cir.unreachable
+// CHECK: }
+
 cir.func @throw_with_no_return_2(%arg0: !s32i, %arg1: !s32i) -> !s32i {
   %0 = cir.alloca "a" align(4) init : !cir.ptr<!s32i>
   %1 = cir.alloca "b" align(4) init : !cir.ptr<!s32i>

>From 3974393a664a30e9effbae4dd8b1bbafaea04891 Mon Sep 17 00:00:00 2001
From: Kunal Dubey <[email protected]>
Date: Sat, 3 Oct 2026 11:56:49 +0530
Subject: [PATCH 2/2] [CIR][EH] Use the data layout for throw pointer address
 spaces

---
 .../clang/CIR/Dialect/IR/CIRDataLayout.h      |  9 +++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  6 +-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 65 ++++++------------
 .../CIR/Dialect/Transforms/EHABILowering.cpp  | 38 +++++------
 clang/test/CIR/IR/invalid-throw.cir           | 66 +++++++------------
 .../eh-abi-lowering-throw-address-spaces.cir  | 45 +++++++++++++
 6 files changed, 120 insertions(+), 109 deletions(-)
 create mode 100644 
clang/test/CIR/Transforms/eh-abi-lowering-throw-address-spaces.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h 
b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
index 7b19ad1b12e404..e4c7dbed0f690d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
@@ -138,6 +138,15 @@ class CIRDataLayout {
       return {};
     return cir::TargetAddressSpaceAttr::get(ctx, globalASAttr.getUInt());
   }
+
+  mlir::ptr::MemorySpaceAttrInterface
+  getDefaultAddrSpace(mlir::MLIRContext *ctx) const {
+    auto defaultASAttr = mlir::dyn_cast_if_present<mlir::IntegerAttr>(
+        layout.getDefaultMemorySpace());
+    if (!defaultASAttr || defaultASAttr.getUInt() == 0)
+      return {};
+    return cir::TargetAddressSpaceAttr::get(ctx, defaultASAttr.getUInt());
+  }
 };
 
 } // namespace cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 7542ae32689d0d..104f9dcd5dcd59 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -8541,9 +8541,9 @@ def CIR_VAArgOp : CIR_Op<"va_arg"> {
 // and a destructor pointer as SSA operands. Null RTTI and destructor pointers
 // are represented by typed null values. The zero-operand form represents a
 // rethrow. Since every normal throw lowers to the same `__cxa_throw` symbol,
-// the address spaces of these three operands must be consistent across the
-// module. Subclasses contribute any additional traits, successors and assembly
-// format pieces.
+// the exception and destructor pointers use the module's default address space
+// and the RTTI pointer uses its global address space. Subclasses contribute 
any
+// additional traits, successors and assembly format pieces.
 class CIR_ThrowOpBase<string mnemonic, list<Trait> traits = []>
     : CIR_Op<mnemonic, !listconcat(traits, [AttrSizedOperandSegments])> {
   let arguments = (ins
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index c45e2ab3a82888..f5121d47185fb9 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -13,6 +13,7 @@
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
 #include "clang/CIR/Dialect/IR/CIROpsEnums.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 
@@ -35,8 +36,6 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/LogicalResult.h"
 
-#include <array>
-
 using namespace mlir;
 using namespace cir;
 
@@ -4936,29 +4935,6 @@ void InlineAsmOp::getEffects(
 // ThrowOp / TryThrowOp
 
//===----------------------------------------------------------------------===//
 
-using ThrowAddressSpaces = std::array<mlir::ptr::MemorySpaceAttrInterface, 3>;
-
-template <typename ThrowOpTy>
-static std::optional<ThrowAddressSpaces> getThrowAddressSpaces(ThrowOpTy op) {
-  if (!op.getExceptionPtr() || !op.getTypeInfo() || !op.getDtor())
-    return std::nullopt;
-
-  return ThrowAddressSpaces{
-      mlir::cast<cir::PointerType>(op.getExceptionPtr().getType())
-          .getAddrSpace(),
-      mlir::cast<cir::PointerType>(op.getTypeInfo().getType()).getAddrSpace(),
-      mlir::cast<cir::PointerType>(op.getDtor().getType()).getAddrSpace()};
-}
-
-static std::optional<ThrowAddressSpaces>
-getThrowAddressSpaces(mlir::Operation *op) {
-  if (auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op))
-    return getThrowAddressSpaces(throwOp);
-  if (auto tryThrowOp = mlir::dyn_cast<cir::TryThrowOp>(op))
-    return getThrowAddressSpaces(tryThrowOp);
-  return std::nullopt;
-}
-
 template <typename ThrowOpTy>
 static mlir::LogicalResult verifyThrowOpImpl(ThrowOpTy op) {
   bool hasExceptionPtr = static_cast<bool>(op.getExceptionPtr());
@@ -4977,28 +4953,27 @@ static mlir::LogicalResult verifyThrowOpImpl(ThrowOpTy 
op) {
   if (!module)
     return op.emitOpError("expects an enclosing module");
 
-  mlir::Operation *firstThrow = nullptr;
-  std::optional<ThrowAddressSpaces> expectedAddressSpaces;
-  module.walk([&](mlir::Operation *candidate) {
-    expectedAddressSpaces = getThrowAddressSpaces(candidate);
-    if (!expectedAddressSpaces)
-      return mlir::WalkResult::advance();
-    firstThrow = candidate;
-    return mlir::WalkResult::interrupt();
-  });
+  cir::CIRDataLayout dataLayout(module);
+  auto defaultAddrSpace = dataLayout.getDefaultAddrSpace(op.getContext());
+  auto globalAddrSpace = dataLayout.getGlobalAddrSpace(op.getContext());
 
-  assert(expectedAddressSpaces && firstThrow &&
-         "the operation being verified provides a throw signature");
-  if (*expectedAddressSpaces == *getThrowAddressSpaces(op))
-    return mlir::success();
+  auto getAddrSpace = [](mlir::Value operand) {
+    return mlir::cast<cir::PointerType>(operand.getType()).getAddrSpace();
+  };
 
-  mlir::InFlightDiagnostic diag = op.emitOpError(
-      "operand address spaces must match the first non-rethrow cir.throw or "
-      "cir.try_throw in the module");
-  diag.attachNote(firstThrow->getLoc())
-      << "the module's __cxa_throw address-space signature is established "
-         "here";
-  return mlir::failure();
+  if (getAddrSpace(op.getExceptionPtr()) != defaultAddrSpace)
+    return op.emitOpError(
+        "exception pointer must use the module's default address space");
+
+  if (getAddrSpace(op.getTypeInfo()) != globalAddrSpace)
+    return op.emitOpError(
+        "type_info pointer must use the module's global address space");
+
+  if (getAddrSpace(op.getDtor()) != defaultAddrSpace)
+    return op.emitOpError(
+        "destructor pointer must use the module's default address space");
+
+  return mlir::success();
 }
 
 mlir::LogicalResult cir::ThrowOp::verify() { return verifyThrowOpImpl(*this); }
diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
index eaaed9beab2f0c..7e5307e5e40a8b 100644
--- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
@@ -120,6 +120,8 @@ class ItaniumEHLowering : public EHABILowering {
 
   cir::VoidType voidType;
   cir::PointerType voidPtrType;
+  cir::PointerType cxaThrowVoidPtrType;
+  cir::PointerType globalVoidPtrType;
   cir::PointerType u8PtrType;
   cir::IntType u32Type;
   cir::IntType s32Type;
@@ -142,9 +144,7 @@ class ItaniumEHLowering : public EHABILowering {
 
   void ensureRuntimeDecls(mlir::Location loc);
   void ensureClangCallTerminate(mlir::Location loc);
-  void ensureCxaThrowDecl(mlir::Location loc, cir::PointerType 
exceptionPtrType,
-                          cir::PointerType typeInfoPtrType,
-                          cir::PointerType dtorPtrType);
+  void ensureCxaThrowDecl(mlir::Location loc);
   void ensureCxaRethrowDecl(mlir::Location loc);
   void ensureCxaCallUnexpectedDecl(mlir::Location loc);
   mlir::Block *buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc);
@@ -169,6 +169,11 @@ mlir::LogicalResult ItaniumEHLowering::run() {
   // TODO(cir): Move these to the base class if they are also needed for MSVC.
   voidType = cir::VoidType::get(ctx);
   voidPtrType = cir::PointerType::get(voidType);
+  cir::CIRDataLayout dataLayout(mod);
+  auto defaultAddrSpace = dataLayout.getDefaultAddrSpace(ctx);
+  auto globalAddrSpace = dataLayout.getGlobalAddrSpace(ctx);
+  cxaThrowVoidPtrType = cir::PointerType::get(voidType, defaultAddrSpace);
+  globalVoidPtrType = cir::PointerType::get(voidType, globalAddrSpace);
   auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false);
   u8PtrType = cir::PointerType::get(u8Type);
   u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false);
@@ -272,17 +277,13 @@ void 
ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) {
 ///
 ///   void __cxa_throw(void *exception, global void *type_info, void *dtor);
 ///
-/// The type-info pointer may use a target-specific global address space, so
-/// derive the declaration from the typed throw operands instead of assuming
-/// that all three pointers use the default address space.
-void ItaniumEHLowering::ensureCxaThrowDecl(mlir::Location loc,
-                                           cir::PointerType exceptionPtrType,
-                                           cir::PointerType typeInfoPtrType,
-                                           cir::PointerType dtorPtrType) {
+/// The type-info pointer may use a target-specific global address space. The
+/// exception and destructor pointers use the target's default address space.
+void ItaniumEHLowering::ensureCxaThrowDecl(mlir::Location loc) {
   if (cxaThrowFunc)
     return;
   auto throwFuncTy = cir::FuncType::get(
-      {exceptionPtrType, typeInfoPtrType, dtorPtrType}, voidType,
+      {cxaThrowVoidPtrType, globalVoidPtrType, cxaThrowVoidPtrType}, voidType,
       /*isVarArg=*/false);
   cxaThrowFunc =
       getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_throw", throwFuncTy);
@@ -937,22 +938,19 @@ mlir::LogicalResult 
ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
     return mlir::success();
   }
 
-  auto castToVoidPtr = [&](mlir::Value value) -> mlir::Value {
-    auto ptrType = mlir::cast<cir::PointerType>(value.getType());
-    auto targetType = cir::PointerType::get(voidType, ptrType.getAddrSpace());
+  auto castToVoidPtr = [&](mlir::Value value,
+                           cir::PointerType targetType) -> mlir::Value {
     if (value.getType() == targetType)
       return value;
     return cir::CastOp::create(builder, loc, targetType, 
cir::CastKind::bitcast,
                                value);
   };
 
-  mlir::Value exnPtr = castToVoidPtr(op.getExceptionPtr());
-  mlir::Value typeInfo = castToVoidPtr(op.getTypeInfo());
-  mlir::Value dtor = castToVoidPtr(op.getDtor());
+  mlir::Value exnPtr = castToVoidPtr(op.getExceptionPtr(), 
cxaThrowVoidPtrType);
+  mlir::Value typeInfo = castToVoidPtr(op.getTypeInfo(), globalVoidPtrType);
+  mlir::Value dtor = castToVoidPtr(op.getDtor(), cxaThrowVoidPtrType);
 
-  ensureCxaThrowDecl(loc, mlir::cast<cir::PointerType>(exnPtr.getType()),
-                     mlir::cast<cir::PointerType>(typeInfo.getType()),
-                     mlir::cast<cir::PointerType>(dtor.getType()));
+  ensureCxaThrowDecl(loc);
 
   cir::TryCallOp::create(
       builder, loc, mlir::FlatSymbolRefAttr::get(cxaThrowFunc), voidType,
diff --git a/clang/test/CIR/IR/invalid-throw.cir 
b/clang/test/CIR/IR/invalid-throw.cir
index 50b8759cfba87d..a099a06f0cb185 100644
--- a/clang/test/CIR/IR/invalid-throw.cir
+++ b/clang/test/CIR/IR/invalid-throw.cir
@@ -21,16 +21,11 @@ cir.func dso_local @throw_with_incomplete_operands() {
 !u8i = !cir.int<u, 8>
 !void = !cir.void
 
-module {
-
-cir.func @throw_with_as1(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
-  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
-  cir.unreachable
-}
-
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<"dlti.global_memory_space" = 1 : ui64>
+} {
 cir.func @throw_with_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(2)>, %dtor: !cir.ptr<!void>) {
-  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
-  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  // expected-error @below {{'cir.throw' op type_info pointer must use the 
module's global address space}}
   cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(2)>, !cir.ptr<!void>
   cir.unreachable
 }
@@ -43,16 +38,11 @@ cir.func @throw_with_as2(%exception: !cir.ptr<!s32i>, 
%type_info: !cir.ptr<!u8i,
 !u8i = !cir.int<u, 8>
 !void = !cir.void
 
-module {
-
-cir.func @throw_with_default_spaces(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
-  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
-  cir.unreachable
-}
-
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<"dlti.global_memory_space" = 1 : ui64>
+} {
 cir.func @throw_with_exception_as2(%exception: !cir.ptr<!s32i, 
target_address_space(2)>, %type_info: !cir.ptr<!u8i, target_address_space(1)>, 
%dtor: !cir.ptr<!void>) {
-  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
-  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  // expected-error @below {{'cir.throw' op exception pointer must use the 
module's default address space}}
   cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i, 
target_address_space(2)>, !cir.ptr<!u8i, target_address_space(1)>, 
!cir.ptr<!void>
   cir.unreachable
 }
@@ -65,16 +55,11 @@ cir.func @throw_with_exception_as2(%exception: 
!cir.ptr<!s32i, target_address_sp
 !u8i = !cir.int<u, 8>
 !void = !cir.void
 
-module {
-
-cir.func @throw_with_default_spaces(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
-  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
-  cir.unreachable
-}
-
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<"dlti.global_memory_space" = 1 : ui64>
+} {
 cir.func @throw_with_dtor_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void, 
target_address_space(2)>) {
-  // expected-error @below {{'cir.throw' op operand address spaces must match 
the first non-rethrow cir.throw or cir.try_throw in the module}}
-  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  // expected-error @below {{'cir.throw' op destructor pointer must use the 
module's default address space}}
   cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void, target_address_space(2)>
   cir.unreachable
 }
@@ -87,16 +72,11 @@ cir.func @throw_with_dtor_as2(%exception: !cir.ptr<!s32i>, 
%type_info: !cir.ptr<
 !u8i = !cir.int<u, 8>
 !void = !cir.void
 
-module {
-
-cir.func @throw_with_as1(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
-  cir.throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, !cir.ptr<!u8i, 
target_address_space(1)>, !cir.ptr<!void>
-  cir.unreachable
-}
-
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<"dlti.global_memory_space" = 1 : ui64>
+} {
 cir.func @try_throw_with_as2(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(2)>, %dtor: !cir.ptr<!void>) {
-  // expected-error @below {{'cir.try_throw' op operand address spaces must 
match the first non-rethrow cir.throw or cir.try_throw in the module}}
-  // expected-note @-6 {{the module's __cxa_throw address-space signature is 
established here}}
+  // expected-error @below {{'cir.try_throw' op type_info pointer must use the 
module's global address space}}
   cir.try_throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, 
!cir.ptr<!u8i, target_address_space(2)>, !cir.ptr<!void> ^normal, ^unwind
 ^normal:
   cir.unreachable
@@ -112,20 +92,24 @@ cir.func @try_throw_with_as2(%exception: !cir.ptr<!s32i>, 
%type_info: !cir.ptr<!
 !u8i = !cir.int<u, 8>
 !void = !cir.void
 
-module {
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    "dlti.default_memory_space" = 7 : ui64,
+    "dlti.global_memory_space" = 1 : ui64>
+} {
 
 // Different pointee types are valid when the address-space signature is the
 // same.
-cir.func @try_throw_one(%exception: !cir.ptr<!s32i>, %type_info: 
!cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void>) {
-  cir.try_throw %exception, %type_info, %dtor : !cir.ptr<!s32i>, 
!cir.ptr<!u8i, target_address_space(1)>, !cir.ptr<!void> ^normal, ^unwind
+cir.func @try_throw_one(%exception: !cir.ptr<!s32i, target_address_space(7)>, 
%type_info: !cir.ptr<!u8i, target_address_space(1)>, %dtor: !cir.ptr<!void, 
target_address_space(7)>) {
+  cir.try_throw %exception, %type_info, %dtor : !cir.ptr<!s32i, 
target_address_space(7)>, !cir.ptr<!u8i, target_address_space(1)>, 
!cir.ptr<!void, target_address_space(7)> ^normal, ^unwind
 ^normal:
   cir.unreachable
 ^unwind:
   cir.unreachable
 }
 
-cir.func @throw_two(%exception: !cir.ptr<!u8i>, %type_info: !cir.ptr<!void, 
target_address_space(1)>, %dtor: !cir.ptr<!s32i>) {
-  cir.throw %exception, %type_info, %dtor : !cir.ptr<!u8i>, !cir.ptr<!void, 
target_address_space(1)>, !cir.ptr<!s32i>
+cir.func @throw_two(%exception: !cir.ptr<!u8i, target_address_space(7)>, 
%type_info: !cir.ptr<!void, target_address_space(1)>, %dtor: !cir.ptr<!s32i, 
target_address_space(7)>) {
+  cir.throw %exception, %type_info, %dtor : !cir.ptr<!u8i, 
target_address_space(7)>, !cir.ptr<!void, target_address_space(1)>, 
!cir.ptr<!s32i, target_address_space(7)>
   cir.unreachable
 }
 
diff --git a/clang/test/CIR/Transforms/eh-abi-lowering-throw-address-spaces.cir 
b/clang/test/CIR/Transforms/eh-abi-lowering-throw-address-spaces.cir
new file mode 100644
index 00000000000000..f544cee163951b
--- /dev/null
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-throw-address-spaces.cir
@@ -0,0 +1,45 @@
+// RUN: cir-opt %s -cir-eh-abi-lowering | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+module attributes {
+  cir.triple = "x86_64-unknown-linux-gnu",
+  dlti.dl_spec = #dlti.dl_spec<
+    "dlti.default_memory_space" = 7 : ui64,
+    "dlti.global_memory_space" = 3 : ui64>
+} {
+  cir.func @test_nonzero_default_address_space(
+      %exception: !cir.ptr<!s32i, target_address_space(7)>,
+      %type_info: !cir.ptr<!u8i, target_address_space(3)>,
+      %dtor: !cir.ptr<!u8i, target_address_space(7)>) {
+    cir.try_throw %exception, %type_info, %dtor :
+        !cir.ptr<!s32i, target_address_space(7)>,
+        !cir.ptr<!u8i, target_address_space(3)>,
+        !cir.ptr<!u8i, target_address_space(7)> ^normal, ^unwind
+  ^normal:
+    cir.return
+  ^unwind:
+    %token = cir.eh.initiate cleanup : !cir.eh_token
+    cir.br ^cleanup(%token : !cir.eh_token)
+  ^cleanup(%cleanup_token: !cir.eh_token):
+    %cleanup = cir.begin_cleanup %cleanup_token : !cir.eh_token -> 
!cir.cleanup_token
+    cir.end_cleanup %cleanup : !cir.cleanup_token
+    cir.resume %cleanup_token : !cir.eh_token
+  }
+}
+
+// CHECK-LABEL: cir.func @test_nonzero_default_address_space(
+// CHECK: %[[EXCEPTION:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!s32i, 
target_address_space(7)> -> !cir.ptr<!void, target_address_space(7)>
+// CHECK: %[[TYPE_INFO:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!u8i, 
target_address_space(3)> -> !cir.ptr<!void, target_address_space(3)>
+// CHECK: %[[DTOR:.*]] = cir.cast bitcast %{{.*}} : !cir.ptr<!u8i, 
target_address_space(7)> -> !cir.ptr<!void, target_address_space(7)>
+// CHECK: cir.try_call @__cxa_throw(%[[EXCEPTION]], %[[TYPE_INFO]], %[[DTOR]])
+// CHECK-SAME: (!cir.ptr<!void, target_address_space(7)>, !cir.ptr<!void, 
target_address_space(3)>, !cir.ptr<!void, target_address_space(7)>) -> ()
+// CHECK: %[[INFLIGHT_EXN:.*]], %[[TYPE_ID:.*]] = cir.eh.inflight_exception 
cleanup
+// CHECK: cir.br {{.*}}(%[[INFLIGHT_EXN]], %[[TYPE_ID]] : !cir.ptr<!void>, 
!u32i)
+
+// CHECK: cir.func private @__cxa_throw(
+// CHECK-SAME: !cir.ptr<!void, target_address_space(7)>,
+// CHECK-SAME: !cir.ptr<!void, target_address_space(3)>,
+// CHECK-SAME: !cir.ptr<!void, target_address_space(7)>)

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

Reply via email to