https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/199121

>From 2b414b95209e59aa4113e0974380950772055f67 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 14 May 2026 17:51:58 -0700
Subject: [PATCH 1/2] [CIR] Handle throwing an exception from a cleanup scope

The CIR FlattenCFG pass had been ignoring any ThrowOp that occurred
inside a cleanup scope or try operation, which led to the thrown
exception not triggering local cleanups and bypassing local catch
handlers.

This change introduces a new CIR operation, TryThrowOp, which is
analagous to the existing TryCallOp. The TryThrowOp (as well as the
ThrowOp) will eventually be lowered to a function call, but which
function gets called is a target-dependent detail, so we need an
abstract operation before EHABI lowering.

The Flatten CFG pass replaces any ThrowOp inside a cleanup scope or
try operation with a TryThrowOp that has an unreachable normal
destination and unwinds to the appropriate cleanup or catch dispatch
block.

Assisted-by: Cursor / claude-opus-4.7-thinking-xhigh
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  86 ++++++++++++--
 .../Dialect/Transforms/CIRTransformUtils.h    |  14 +++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |  26 +++--
 .../Dialect/Transforms/CIRTransformUtils.cpp  |  48 ++++++++
 .../CIR/Dialect/Transforms/EHABILowering.cpp  | 103 ++++++++++++++++
 .../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 110 +++++++++++-------
 .../CodeGen/cleanup-throw-from-cleanup.cpp    |  97 +++++++++++++++
 clang/test/CIR/CodeGen/try-catch.cpp          |  97 +++++++++++++++
 8 files changed, 522 insertions(+), 59 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ad6b9335ec4b6..ca39d3c7b3b71 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -7045,11 +7045,35 @@ def CIR_VAArgOp : CIR_Op<"va_arg"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// ThrowOpBase
+//===----------------------------------------------------------------------===//
+
+// 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.
+class CIR_ThrowOpBase<string mnemonic, list<Trait> traits = []>
+    : CIR_Op<mnemonic, traits> {
+  let arguments = (ins
+    Optional<CIR_PointerType>:$exception_ptr,
+    OptionalAttr<FlatSymbolRefAttr>:$type_info,
+    OptionalAttr<FlatSymbolRefAttr>:$dtor
+  );
+
+  let extraClassDeclaration = [{
+    bool rethrows() { return getNumOperands() == 0; }
+  }];
+
+  let hasVerifier = 1;
+}
+
 
//===----------------------------------------------------------------------===//
 // ThrowOp
 
//===----------------------------------------------------------------------===//
 
-def CIR_ThrowOp : CIR_Op<"throw"> {
+def CIR_ThrowOp : CIR_ThrowOpBase<"throw"> {
   let summary = "(Re)Throws an exception";
   let description = [{
     This operation is equivalent to either __cxa_throw or __cxa_rethrow,
@@ -7084,24 +7108,66 @@ def CIR_ThrowOp : CIR_Op<"throw"> {
     ```
   }];
 
-  let arguments = (ins
-    Optional<CIR_PointerType>:$exception_ptr,
-    OptionalAttr<FlatSymbolRefAttr>:$type_info,
-    OptionalAttr<FlatSymbolRefAttr>:$dtor
-  );
-
   let assemblyFormat = [{
     ($exception_ptr^ `:` type($exception_ptr))?
     (`,` $type_info^)?
     (`,` $dtor^)?
     attr-dict
   }];
+}
 
-  let extraClassDeclaration = [{
-    bool rethrows() { return getNumOperands() == 0; }
+//===----------------------------------------------------------------------===//
+// TryThrowOp
+//===----------------------------------------------------------------------===//
+
+def CIR_TryThrowOp : CIR_ThrowOpBase<"try_throw", [Terminator]> {
+  let summary = "throw an exception with an unwind destination";
+  let description = [{
+    Similar to `cir.throw` but acts as a terminator with two destination
+    blocks: a `normalDest` that should contain a `cir.unreachable`
+    operation (since a throw never returns) and an `unwindDest` that
+    receives control when the throw needs to unwind through an enclosing
+    cleanup or catch handler. This is the EH counterpart of `cir.throw`,
+    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`.
+
+    This operation is produced by the FlattenCFG pass for `cir.throw`
+    operations that appear inside a cleanup scope or try region. It is
+    later lowered by the EHABI lowering pass to a `cir.try_call` of
+    `__cxa_throw` (or `__cxa_rethrow`).
+
+    Example:
+
+    ```
+    cir.try_throw %exception_addr : !cir.ptr<!s32i>, @_ZTIi
+        ^normalDest, ^unwindDest
+    ^normalDest:
+      cir.unreachable
+    ^unwindDest:
+      ...
+    ```
   }];
 
-  let hasVerifier = 1;
+  let successors = (successor
+    AnySuccessor:$normalDest,
+    AnySuccessor:$unwindDest
+  );
+
+  let assemblyFormat = [{
+    ($exception_ptr^ `:` type($exception_ptr))?
+    (`,` $type_info^)?
+    (`,` $dtor^)?
+    $normalDest `,` $unwindDest
+    attr-dict
+  }];
+
+  // The EHABI lowering pass replaces every cir.try_throw with a cir.try_call
+  // of __cxa_throw / __cxa_rethrow before the LLVM lowering runs, so no
+  // direct LLVM lowering pattern is needed for this op.
+  let hasLLVMLowering = false;
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/Transforms/CIRTransformUtils.h 
b/clang/include/clang/CIR/Dialect/Transforms/CIRTransformUtils.h
index b132698dfc7d6..b708da61bf01f 100644
--- a/clang/include/clang/CIR/Dialect/Transforms/CIRTransformUtils.h
+++ b/clang/include/clang/CIR/Dialect/Transforms/CIRTransformUtils.h
@@ -32,6 +32,20 @@ mlir::Block *replaceCallWithTryCall(cir::CallOp callOp, 
mlir::Block *unwindDest,
                                     mlir::Location loc,
                                     mlir::RewriterBase &rewriter);
 
+/// Replace a `cir::ThrowOp` with a `cir::TryThrowOp` whose unwind
+/// destination is \p unwindDest. The throw's parent block is split
+/// immediately after the throw; the resulting suffix block (which should
+/// contain the `cir.unreachable` that follows every throw) becomes the
+/// try_throw's normal destination and is returned to the caller.
+///
+/// All attributes of the original throw other than the operand segment
+/// sizes (which `TryThrowOp::create` sets itself) are copied onto the new
+/// try_throw, and the original throw is erased.
+mlir::Block *replaceThrowWithTryThrow(cir::ThrowOp throwOp,
+                                      mlir::Block *unwindDest,
+                                      mlir::Location loc,
+                                      mlir::RewriterBase &rewriter);
+
 /// Collect ops in blocks that are unreachable from their region's entry,
 /// appending them to \p ops. Used by CIR passes that drive
 /// `applyPartialConversion` and need to feed it operations the conversion
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 46b3cd5a47935..85095a0a2de07 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -4035,21 +4035,27 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser,
 }
 
 
//===----------------------------------------------------------------------===//
-// ThrowOp
+// ThrowOp / TryThrowOp
 
//===----------------------------------------------------------------------===//
 
-mlir::LogicalResult cir::ThrowOp::verify() {
-  // For the no-rethrow version, it must have at least the exception pointer.
-  if (rethrows())
-    return success();
+template <typename ThrowOpTy>
+static mlir::LogicalResult verifyThrowOpImpl(ThrowOpTy op) {
+  if (op.rethrows())
+    return mlir::success();
 
-  if (getNumOperands() != 0) {
-    if (getTypeInfo())
-      return success();
-    return emitOpError() << "'type_info' symbol attribute missing";
+  if (op.getNumOperands() != 0) {
+    if (op.getTypeInfo())
+      return mlir::success();
+    return op.emitOpError() << "'type_info' symbol attribute missing";
   }
 
-  return failure();
+  return mlir::failure();
+}
+
+mlir::LogicalResult cir::ThrowOp::verify() { return verifyThrowOpImpl(*this); }
+
+mlir::LogicalResult cir::TryThrowOp::verify() {
+  return verifyThrowOpImpl(*this);
 }
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp 
b/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
index 91b55c3220245..1653e673859dd 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRTransformUtils.cpp
@@ -105,3 +105,51 @@ mlir::Block *cir::replaceCallWithTryCall(cir::CallOp 
callOp,
   rewriter.eraseOp(callOp);
   return normalDest;
 }
+
+mlir::Block *cir::replaceThrowWithTryThrow(cir::ThrowOp throwOp,
+                                           mlir::Block *unwindDest,
+                                           mlir::Location loc,
+                                           mlir::RewriterBase &rewriter) {
+  // The throw never returns, so the try_throw's normal destination is
+  // literally unreachable. Place it at the end of the parent function
+  // rather than splitting it out of the throw's block in the middle of
+  // the normal control flow.
+  auto funcOp = throwOp->getParentOfType<cir::FuncOp>();
+  assert(funcOp && "throw must be inside a function");
+  mlir::Region &body = funcOp.getBody();
+
+  mlir::Block *normalDest;
+  {
+    mlir::OpBuilder::InsertionGuard guard(rewriter);
+    normalDest = rewriter.createBlock(&body, body.end());
+    cir::UnreachableOp::create(rewriter, loc);
+  }
+
+  // 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);
+
+  // Copy any extra attributes from the original throw. The type_info and
+  // dtor attributes are already set by TryThrowOp::create above.
+  llvm::StringRef excludedAttrs[] = {
+      "type_info",
+      "dtor",
+  };
+  for (mlir::NamedAttribute attr : throwOp->getAttrs()) {
+    if (llvm::is_contained(excludedAttrs, attr.getName()))
+      continue;
+    tryThrowOp->setAttr(attr.getName(), attr.getValue());
+  }
+
+  // Erase the throw along with any operations that followed it in its
+  // parent block (typically a cir.unreachable left over from CIR codegen).
+  // They must be removed because try_throw is a terminator and a block
+  // can have only one terminator.
+  mlir::Block *throwBlock = throwOp->getBlock();
+  while (&throwBlock->back() != tryThrowOp)
+    rewriter.eraseOp(&throwBlock->back());
+
+  return normalDest;
+}
diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
index 802740e800d7f..e6c76fed6f78a 100644
--- a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
@@ -125,6 +125,8 @@ class ItaniumEHLowering : public EHABILowering {
   cir::FuncOp endCatchFunc;
   cir::FuncOp getExceptionPtrFunc;
   cir::FuncOp clangCallTerminateFunc;
+  cir::FuncOp cxaThrowFunc;
+  cir::FuncOp cxaRethrowFunc;
 
   DenseMap<mlir::StringAttr, cir::FuncOp> catchCopyThunks;
 
@@ -133,6 +135,8 @@ class ItaniumEHLowering : public EHABILowering {
 
   void ensureRuntimeDecls(mlir::Location loc);
   void ensureClangCallTerminate(mlir::Location loc);
+  void ensureCxaThrowDecl(mlir::Location loc);
+  void ensureCxaRethrowDecl(mlir::Location loc);
   mlir::Block *buildTerminateBlock(cir::FuncOp funcOp, mlir::Location loc);
   mlir::FailureOr<cir::FuncOp>
   resolveCatchCopyThunk(cir::ConstructCatchParamOp op);
@@ -146,6 +150,7 @@ class ItaniumEHLowering : public EHABILowering {
   mlir::LogicalResult lowerConstructCatchParam(cir::ConstructCatchParamOp op,
                                                mlir::Value exnPtr);
   void lowerInitCatchParam(cir::InitCatchParamOp op);
+  mlir::LogicalResult lowerTryThrow(cir::TryThrowOp op);
 };
 
 /// Lower all EH operations in the module to the Itanium-specific form.
@@ -253,6 +258,29 @@ void 
ItaniumEHLowering::ensureClangCallTerminate(mlir::Location loc) {
   clangCallTerminateFunc = funcOp;
 }
 
+/// 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) {
+  if (cxaThrowFunc)
+    return;
+  auto throwFuncTy = cir::FuncType::get({voidPtrType, voidPtrType, 
voidPtrType},
+                                        voidType, /*isVarArg=*/false);
+  cxaThrowFunc =
+      getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_throw", throwFuncTy);
+}
+
+/// Ensure the __cxa_rethrow runtime function is declared in the module.
+///
+///   void __cxa_rethrow();
+void ItaniumEHLowering::ensureCxaRethrowDecl(mlir::Location loc) {
+  if (cxaRethrowFunc)
+    return;
+  auto rethrowFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
+  cxaRethrowFunc =
+      getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_rethrow", rethrowFuncTy);
+}
+
 /// Create a terminate landing pad block at the end of the specified function.
 mlir::Block *ItaniumEHLowering::buildTerminateBlock(cir::FuncOp funcOp,
                                                     mlir::Location loc) {
@@ -330,6 +358,15 @@ mlir::LogicalResult 
ItaniumEHLowering::lowerFunc(cir::FuncOp funcOp) {
   for (cir::InitCatchParamOp op : initCatchOps)
     lowerInitCatchParam(op);
 
+  // Lower any cir.try_throw ops in this function to cir.try_call of
+  // __cxa_throw / __cxa_rethrow. These are produced by FlattenCFG when a
+  // cir.throw appears inside a cleanup scope or try region.
+  SmallVector<cir::TryThrowOp> tryThrowOps;
+  funcOp.walk([&](cir::TryThrowOp op) { tryThrowOps.push_back(op); });
+  for (cir::TryThrowOp op : tryThrowOps)
+    if (mlir::failed(lowerTryThrow(op)))
+      return mlir::failure();
+
   return mlir::success();
 }
 
@@ -704,6 +741,72 @@ 
ItaniumEHLowering::lowerConstructCatchParam(cir::ConstructCatchParamOp op,
   return mlir::success();
 }
 
+/// 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.
+mlir::LogicalResult ItaniumEHLowering::lowerTryThrow(cir::TryThrowOp op) {
+  mlir::Location loc = op.getLoc();
+  mlir::Block *normalDest = op.getNormalDest();
+  mlir::Block *unwindDest = op.getUnwindDest();
+  builder.setInsertionPoint(op);
+
+  if (op.rethrows()) {
+    ensureCxaRethrowDecl(loc);
+    cir::TryCallOp::create(
+        builder, loc, mlir::FlatSymbolRefAttr::get(cxaRethrowFunc), voidType,
+        normalDest, unwindDest, mlir::ValueRange{});
+    op.erase();
+    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)));
+  }
+
+  cir::TryCallOp::create(
+      builder, loc, mlir::FlatSymbolRefAttr::get(cxaThrowFunc), voidType,
+      normalDest, unwindDest, mlir::ValueRange{exnPtr, typeInfo, dtor});
+  op.erase();
+  return mlir::success();
+}
+
 /// Lower a cir.init_catch_param into the Itanium-specific sequence that
 /// materializes the catch parameter's local variable from the exception
 /// pointer returned by __cxa_begin_catch. The shape of the lowering
diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp 
b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
index a21394dc62332..baab456b652b5 100644
--- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp
@@ -672,6 +672,18 @@ collectThrowingCalls(mlir::Region &region,
   });
 }
 
+// Collect all cir.throw operations in a region that need to be replaced
+// with cir.try_throw operations so they can unwind through an enclosing
+// cleanup or catch handler. Nested cleanup scopes and try ops are always
+// flattened before their enclosing parents, so there are no nested
+// regions to skip here.
+static void
+collectThrows(mlir::Region &region,
+              llvm::SmallVectorImpl<cir::ThrowOp> &throwsToRewrite) {
+  region.walk(
+      [&](cir::ThrowOp throwOp) { throwsToRewrite.push_back(throwOp); });
+}
+
 // Collect all cir.resume operations in a region that come from
 // already-flattened try or cleanup scope operations. These resume ops need
 // to be chained through this scope's EH handler instead of unwinding
@@ -1178,6 +1190,7 @@ class CIRCleanupScopeOpFlattening
   flattenCleanup(cir::CleanupScopeOp cleanupOp,
                  llvm::SmallVectorImpl<CleanupExit> &exits,
                  llvm::SmallVectorImpl<cir::CallOp> &callsToRewrite,
+                 llvm::SmallVectorImpl<cir::ThrowOp> &throwsToRewrite,
                  llvm::SmallVectorImpl<cir::ResumeOp> &resumeOpsToChain,
                  mlir::PatternRewriter &rewriter) const {
     mlir::Location loc = cleanupOp.getLoc();
@@ -1222,20 +1235,20 @@ class CIRCleanupScopeOpFlattening
     // the cleanup region since buildEHCleanupBlocks clones from it. The unwind
     // block is inserted before the EH cleanup entry so that the final layout
     // is: body -> normal cleanup -> exit -> unwind -> EH cleanup -> continue.
-    // EH cleanup blocks are needed when there are throwing calls that need to
-    // be rewritten to try_call, or when there are resume ops from
+    // EH cleanup blocks are needed when there are throwing calls or throws
+    // that need to be rewritten, or when there are resume ops from
     // already-flattened inner cleanup scopes that need to chain through this
     // cleanup's EH handler.
     mlir::Block *unwindBlock = nullptr;
     mlir::Block *ehCleanupEntry = nullptr;
-    if (hasEHCleanup &&
-        (!callsToRewrite.empty() || !resumeOpsToChain.empty())) {
+    if (hasEHCleanup && (!callsToRewrite.empty() || !throwsToRewrite.empty() ||
+                         !resumeOpsToChain.empty())) {
       ehCleanupEntry =
           buildEHCleanupBlocks(cleanupOp, loc, continueBlock, rewriter);
-      // The unwind block is only needed when there are throwing calls that
-      // need a shared unwind destination. Resume ops from inner cleanups
-      // branch directly to the EH cleanup entry.
-      if (!callsToRewrite.empty())
+      // The unwind block is only needed when there are throwing calls or
+      // throws that need a shared unwind destination. Resume ops from inner
+      // cleanups branch directly to the EH cleanup entry.
+      if (!callsToRewrite.empty() || !throwsToRewrite.empty())
         unwindBlock = buildUnwindBlock(ehCleanupEntry, /*isCleanupOnly=*/true,
                                        loc, ehCleanupEntry, rewriter);
     }
@@ -1354,32 +1367,43 @@ class CIRCleanupScopeOpFlattening
       }
     }
 
-    // Replace non-nothrow calls with try_call operations. All calls within
-    // this cleanup scope share the same unwind destination.
+    // Replace non-nothrow calls and throws with try_call/try_throw
+    // operations. All calls and throws within this cleanup scope share the
+    // same unwind destination.
     if (hasEHCleanup) {
       for (cir::CallOp callOp : callsToRewrite)
         replaceCallWithTryCall(callOp, unwindBlock, loc, rewriter);
+      for (cir::ThrowOp throwOp : throwsToRewrite)
+        replaceThrowWithTryThrow(throwOp, unwindBlock, loc, rewriter);
     }
 
-    // Handle throwing calls in EH cleanup blocks. When an exception is thrown
-    // during cleanup code that runs on the exception unwind path, the C++
-    // standard requires that std::terminate() be called. Replace such calls
-    // with try_call operations that unwind to a terminate block containing
+    // Handle throwing calls and throws in EH cleanup blocks. When an
+    // exception is thrown during cleanup code that runs on the exception
+    // unwind path, the C++ standard requires that std::terminate() be
+    // called. Replace such calls and throws with try_call/try_throw
+    // operations that unwind to a terminate block containing
     // cir.eh.initiate + cir.eh.terminate.
     if (ehCleanupEntry) {
       llvm::SmallVector<cir::CallOp> ehCleanupThrowingCalls;
+      llvm::SmallVector<cir::ThrowOp> ehCleanupThrows;
       for (mlir::Block *block = ehCleanupEntry; block != continueBlock;
            block = block->getNextNode()) {
-        block->walk([&](cir::CallOp callOp) {
-          if (!callOp.getNothrow())
-            ehCleanupThrowingCalls.push_back(callOp);
+        block->walk([&](mlir::Operation *op) {
+          if (auto callOp = mlir::dyn_cast<cir::CallOp>(op)) {
+            if (!callOp.getNothrow())
+              ehCleanupThrowingCalls.push_back(callOp);
+          } else if (auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op)) {
+            ehCleanupThrows.push_back(throwOp);
+          }
         });
       }
-      if (!ehCleanupThrowingCalls.empty()) {
+      if (!ehCleanupThrowingCalls.empty() || !ehCleanupThrows.empty()) {
         mlir::Block *terminateBlock =
             buildTerminateUnwindBlock(loc, continueBlock, rewriter);
         for (cir::CallOp callOp : ehCleanupThrowingCalls)
           replaceCallWithTryCall(callOp, terminateBlock, loc, rewriter);
+        for (cir::ThrowOp throwOp : ehCleanupThrows)
+          replaceThrowWithTryThrow(throwOp, terminateBlock, loc, rewriter);
       }
     }
 
@@ -1443,12 +1467,15 @@ class CIRCleanupScopeOpFlattening
 
     assert(!exits.empty() && "cleanup scope body has no exit");
 
-    // Collect non-nothrow calls that need to be converted to try_call.
-    // This is only needed for EH and All cleanup kinds, but the vector
-    // will simply be empty for Normal cleanup.
+    // Collect non-nothrow calls and throws that need to be converted to
+    // try_call/try_throw. This is only needed for EH and All cleanup kinds,
+    // but the vectors will simply be empty for Normal cleanup.
     llvm::SmallVector<cir::CallOp> callsToRewrite;
-    if (cleanupKind != cir::CleanupKind::Normal)
+    llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
+    if (cleanupKind != cir::CleanupKind::Normal) {
       collectThrowingCalls(cleanupOp.getBodyRegion(), callsToRewrite);
+      collectThrows(cleanupOp.getBodyRegion(), throwsToRewrite);
+    }
 
     // Collect resume ops from already-flattened inner cleanup scopes that
     // need to chain through this cleanup's EH handler.
@@ -1456,8 +1483,8 @@ class CIRCleanupScopeOpFlattening
     if (cleanupKind != cir::CleanupKind::Normal)
       collectResumeOps(cleanupOp.getBodyRegion(), resumeOpsToChain);
 
-    return flattenCleanup(cleanupOp, exits, callsToRewrite, resumeOpsToChain,
-                          rewriter);
+    return flattenCleanup(cleanupOp, exits, callsToRewrite, throwsToRewrite,
+                          resumeOpsToChain, rewriter);
   }
 };
 
@@ -1641,9 +1668,11 @@ class CIRTryOpFlattening : public 
mlir::OpRewritePattern<cir::TryOp> {
     mlir::MutableArrayRef<mlir::Region> handlerRegions =
         tryOp.getHandlerRegions();
 
-    // Collect throwing calls in the try body.
+    // Collect throwing calls and throws in the try body.
     llvm::SmallVector<cir::CallOp> callsToRewrite;
     collectThrowingCalls(tryOp.getTryRegion(), callsToRewrite);
+    llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
+    collectThrows(tryOp.getTryRegion(), throwsToRewrite);
 
     // Collect resume ops from already-flattened cleanup scopes in the try 
body.
     llvm::SmallVector<cir::ResumeOp> resumeOpsToChain;
@@ -1677,12 +1706,13 @@ class CIRTryOpFlattening : public 
mlir::OpRewritePattern<cir::TryOp> {
       return mlir::success();
     }
 
-    // If there are no throwing calls and no resume ops from inner cleanup
-    // scopes, exceptions cannot reach the catch handlers. Drop all uses
-    // from the (unreachable) handler regions before erasing the try op,
-    // since handler ops may reference values that were inlined from the
-    // try body into the parent block.
-    if (callsToRewrite.empty() && resumeOpsToChain.empty()) {
+    // If there are no throwing calls, no throws, and no resume ops from
+    // inner cleanup scopes, exceptions cannot reach the catch handlers.
+    // Drop all uses from the (unreachable) handler regions before erasing
+    // the try op, since handler ops may reference values that were inlined
+    // from the try body into the parent block.
+    if (callsToRewrite.empty() && throwsToRewrite.empty() &&
+        resumeOpsToChain.empty()) {
       for (mlir::Region &handlerRegion : handlerRegions)
         for (mlir::Block &block : handlerRegion)
           block.dropAllDefinedValueUses();
@@ -1727,20 +1757,22 @@ class CIRTryOpFlattening : public 
mlir::OpRewritePattern<cir::TryOp> {
           return mlir::isa<cir::CatchAllAttr>(attr);
         });
 
-    // Build a block to be the unwind desination for throwing calls and replace
-    // the calls with try_call ops. Note that the unwind block created here is
-    // something different than the unwind handler that we may have created
-    // above. The unwind handler continues unwinding after uncaught exceptions.
-    // This is the block that will eventually become the landing pad for invoke
-    // instructions.
+    // Build a block to be the unwind desination for throwing calls/throws
+    // and replace the calls/throws with try_call/try_throw ops. Note that
+    // the unwind block created here is something different than the unwind
+    // handler that we may have created above. The unwind handler continues
+    // unwinding after uncaught exceptions. This is the block that will
+    // eventually become the landing pad for invoke instructions.
     bool isCleanupOnly = tryOp.getCleanup() && !hasCatchAll;
-    if (!callsToRewrite.empty()) {
-      // Create a shared unwind block for all throwing calls.
+    if (!callsToRewrite.empty() || !throwsToRewrite.empty()) {
+      // Create a shared unwind block for all throwing calls/throws.
       mlir::Block *unwindBlock = buildUnwindBlock(dispatchBlock, isCleanupOnly,
                                                   loc, dispatchBlock, 
rewriter);
 
       for (cir::CallOp callOp : callsToRewrite)
         replaceCallWithTryCall(callOp, unwindBlock, loc, rewriter);
+      for (cir::ThrowOp throwOp : throwsToRewrite)
+        replaceThrowWithTryThrow(throwOp, unwindBlock, loc, rewriter);
     }
 
     // Chain resume ops from inner cleanup scopes.
diff --git a/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp 
b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
new file mode 100644
index 0000000000000..4102a2a6f55ae
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
@@ -0,0 +1,97 @@
+// 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: cir-opt --cir-flatten-cfg %t.cir -o %t-flat.cir
+// RUN: FileCheck --input-file=%t-flat.cir %s --check-prefix=CIR-FLAT
+// 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: %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
+
+struct Local { ~Local(); };
+void testSwitchWithCleanup(int n) {
+  Local x;
+  throw 42;
+}
+
+// In CIR, the throw is emitted inside a `cir.cleanup.scope` whose cleanup
+// region runs the destructor for `x` on the EH unwind path.
+
+// CIR: cir.func{{.*}} @_Z21testSwitchWithCleanupi(%[[ARG:.*]]: !s32i
+// CIR:   %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR:   %[[X:.*]] = cir.alloca !rec_Local, !cir.ptr<!rec_Local>, ["x"]
+// CIR:   cir.store %[[ARG]], %[[N_ADDR]] : !s32i
+// CIR:   cir.cleanup.scope {
+// CIR:     %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
+// CIR:     %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
+// CIR:     cir.store align(16) %[[VAL]], %[[EXN]] : !s32i, !cir.ptr<!s32i>
+// CIR:     cir.throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi
+// CIR:     cir.unreachable
+// CIR:     cir.yield
+// CIR:   } cleanup all {
+// CIR:     cir.call @_ZN5LocalD1Ev(%[[X]]) nothrow
+// CIR:     cir.yield
+// CIR:   }
+// CIR:   cir.return
+
+// After CFG flattening the cleanup scope is gone: the `cir.throw` becomes a
+// `cir.try_throw` whose normal destination is a literally-unreachable block
+// at the end of the function and whose unwind destination is the EH
+// cleanup chain that runs the destructor and then resumes.
+
+// CIR-FLAT: cir.func{{.*}} @_Z21testSwitchWithCleanupi(%[[ARG:.*]]: !s32i
+// CIR-FLAT:   %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR-FLAT:   %[[X:.*]] = cir.alloca !rec_Local, !cir.ptr<!rec_Local>, ["x"]
+// CIR-FLAT:   cir.store %[[ARG]], %[[N_ADDR]]
+// CIR-FLAT:   cir.br ^[[BODY:.+]]
+// CIR-FLAT: ^[[BODY]]:
+// CIR-FLAT:   %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
+// CIR-FLAT:   %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
+// CIR-FLAT:   cir.store align(16) %[[VAL]], %[[EXN]]
+// CIR-FLAT:   cir.try_throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi 
^[[UNREACH:.+]], ^[[UNWIND:.+]]
+// CIR-FLAT: ^[[UNWIND]]:
+// CIR-FLAT:   %[[ET:.*]] = cir.eh.initiate cleanup : !cir.eh_token
+// CIR-FLAT:   cir.br ^[[CLEANUP:.+]](%[[ET]] : !cir.eh_token)
+// CIR-FLAT: ^[[CLEANUP]](%[[ET2:.*]]: !cir.eh_token):
+// CIR-FLAT:   %[[CT:.*]] = cir.begin_cleanup %[[ET2]]
+// CIR-FLAT:   cir.call @_ZN5LocalD1Ev(%[[X]]) nothrow
+// CIR-FLAT:   cir.end_cleanup %[[CT]]
+// CIR-FLAT:   cir.resume %[[ET2]]
+// CIR-FLAT:   cir.return
+// CIR-FLAT: ^[[UNREACH]]:
+// CIR-FLAT:   cir.unreachable
+
+// In LLVM IR the throw becomes an `invoke @__cxa_throw` whose unwind
+// destination is a landingpad with a `cleanup` clause, runs the destructor,
+// and resumes. The "normal" destination of the invoke is a block containing
+// just `unreachable`.
+
+// LLVM: define dso_local void @_Z21testSwitchWithCleanupi(i32 noundef 
%{{.*}}) {{.*}} personality ptr @__gxx_personality_v0
+// LLVM:   %[[X:.*]] = alloca %struct.Local
+// LLVM:   %[[EXN:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// LLVM:   store i32 42, ptr %[[EXN]]
+// LLVM:   invoke void @__cxa_throw(ptr %[[EXN]], ptr @_ZTIi, ptr null)
+// LLVM-NEXT: to label %[[NORMAL:.*]] unwind label %[[LPAD:.*]]
+// LLVM: [[LPAD]]:
+// LLVM:   %{{.*}} = landingpad { ptr, i32 }
+// LLVM-NEXT: cleanup
+// LLVM:   call void @_ZN5LocalD1Ev(ptr {{.*}} %[[X]])
+// LLVM:   resume { ptr, i32 } %{{.*}}
+// LLVM: [[NORMAL]]:
+// LLVM:   unreachable
+
+// OGCG produces equivalent IR: an `invoke __cxa_throw` whose unwind path
+// is a `cleanup` landingpad that calls the destructor and resumes.
+
+// OGCG: define dso_local void @_Z21testSwitchWithCleanupi(i32 noundef %n) 
{{.*}} personality ptr @__gxx_personality_v0
+// OGCG:   %[[X:.*]] = alloca %struct.Local
+// OGCG:   %[[EXN:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// OGCG:   store i32 42, ptr %[[EXN]]
+// OGCG:   invoke void @__cxa_throw(ptr %[[EXN]], ptr @_ZTIi, ptr null)
+// OGCG-NEXT: to label %[[NORMAL:.*]] unwind label %[[LPAD:.*]]
+// OGCG: [[LPAD]]:
+// OGCG:   %{{.*}} = landingpad { ptr, i32 }
+// OGCG-NEXT: cleanup
+// OGCG:   call void @_ZN5LocalD1Ev(ptr {{.*}} %[[X]])
+// OGCG:   resume { ptr, i32 } %{{.*}}
+// OGCG: [[NORMAL]]:
+// OGCG:   unreachable
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp 
b/clang/test/CIR/CodeGen/try-catch.cpp
index 08292f297da21..d5da5d3b2285f 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1762,3 +1762,100 @@ int init_catch_param_with_ref_to_ptr_to_non_record() {
 // OGCG:   %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[TMP_EXCEPTION]], 0
 // OGCG:   %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } 
%[[TMP_EXCEPTION_INFO]], i32 %[[TMP_EH_TYPE_ID]], 1
 // OGCG:   resume { ptr, i32 } %[[EXCEPTION_INFO]]
+
+void direct_inside_try_catch_with_exception_type() {
+  try {
+    throw 42;
+  } catch (int e) {
+  }
+}
+
+// CIR: cir.func {{.*}} @_Z43direct_inside_try_catch_with_exception_typev() 
personality(@__gxx_personality_v0)
+// CIR:   cir.scope {
+// CIR:     %[[E:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["e"]
+// CIR:     cir.try {
+// 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.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>)
+// CIR:       cir.cleanup.scope {
+// CIR:         cir.init_catch_param scalar %[[EXN_PTR]] to %[[E]] : 
!cir.ptr<!void>, !cir.ptr<!s32i>
+// CIR:         cir.yield
+// CIR:       } cleanup all {
+// CIR:         cir.end_catch %catch_token : !cir.catch_token
+// CIR:         cir.yield
+// CIR:       }
+// CIR:       cir.yield
+// CIR:     } unwind (%{{.*}}: !cir.eh_token {{.*}}) {
+// CIR:       cir.resume %{{.*}} : !cir.eh_token
+// CIR:     }
+// CIR:   }
+
+// LLVM: define {{.*}} void 
@_Z43direct_inside_try_catch_with_exception_typev() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// LLVM:   %[[E:.*]] = alloca i32
+// LLVM:   %[[EXN:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// LLVM:   store i32 42, ptr %[[EXN]]
+// LLVM:   invoke void @__cxa_throw(ptr %[[EXN]], ptr @_ZTIi, ptr null)
+// LLVM:           to label %[[UNREACHABLE:.*]] unwind label 
%[[LANDING_PAD:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM:   %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM:                   catch ptr @_ZTIi
+// LLVM:   br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM:   br label %[[DISPATCH:.*]]
+// LLVM: [[DISPATCH]]:
+// LLVM:   %[[EXN_PTR:.*]] = phi ptr
+// LLVM:   %[[EH_SELECTOR:.*]] = phi i32
+// LLVM:   %[[INT_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
+// LLVM:   %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR]], %[[INT_TYPE_ID]]
+// LLVM:   br i1 %[[TYPE_ID_EQ]], label %[[CATCH_INT:.*]], label %[[RESUME:.*]]
+// LLVM: [[CATCH_INT]]:
+// LLVM:   %[[EXN_PTR:.*]] = phi ptr
+// LLVM:   %[[EH_SELECTOR:.*]] = phi i32
+// LLVM:   %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_PTR]])
+// LLVM:   call void @__cxa_end_catch()
+// LLVM:   br label %[[AFTER_CATCH:.*]]
+// LLVM: [[AFTER_CATCH]]:
+// LLVM:   br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM:   br label %[[END_TRY:.*]]
+// LLVM: [[RESUME]]:
+// LLVM:   resume { ptr, i32 }
+// LLVM: [[END_TRY]]:
+// LLVM:   br label %[[TRY_CONT:.*]]
+// LLVM: [[TRY_CONT]]:
+// LLVM:   ret void
+// LLVM: [[UNREACHABLE]]:
+// LLVM:   unreachable
+
+// OGCG: define {{.*}} void 
@_Z43direct_inside_try_catch_with_exception_typev() {{.*}} personality ptr 
@__gxx_personality_v0 {
+// OGCG:   %[[EXN_SLOT:.*]] = alloca ptr
+// OGCG:   %[[EH_SELECTOR_SLOT:.*]] = alloca i32
+// OGCG:   %[[E:.*]] = alloca i32
+// OGCG:   %[[EXN:.*]] = call ptr @__cxa_allocate_exception(i64 4)
+// OGCG:   store i32 42, ptr %[[EXN]]
+// OGCG:   invoke void @__cxa_throw(ptr %[[EXN]], ptr @_ZTIi, ptr null)
+// OGCG:           to label %[[UNREACHABLE:.*]] unwind label 
%[[LANDING_PAD:.*]]
+// OGCG: [[LANDING_PAD]]:
+// OGCG:   %[[LP:.*]] = landingpad { ptr, i32 }
+// OGCG:                   catch ptr @_ZTIi
+// OGCG:   br label %[[DISPATCH:.*]]
+// OGCG: [[DISPATCH]]:
+// OGCG:   %[[EH_SELECTOR:.*]] = load i32, ptr %[[EH_SELECTOR_SLOT]]
+// OGCG:   %[[INT_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
+// OGCG:   %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR]], %[[INT_TYPE_ID]]
+// OGCG:   br i1 %[[TYPE_ID_EQ]], label %[[CATCH_INT:.*]], label %[[RESUME:.*]]
+// OGCG: [[CATCH_INT]]:
+// OGCG:   %[[EXN_PTR:.*]] = load ptr, ptr %[[EXN_SLOT]]
+// OGCG:   %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_PTR]])
+// OGCG:   call void @__cxa_end_catch()
+// OGCG:   br label %[[TRY_CONT:.*]]
+// OGCG: [[TRY_CONT]]:
+// OGCG:   ret void
+// OGCG: [[RESUME]]:
+// OGCG:   resume { ptr, i32 }
+// OGCG: [[UNREACHABLE]]:
+// OGCG:   unreachable

>From b119b5d3f3c2e9c7f998ac73c64651a3587c8ed4 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Fri, 22 May 2026 11:58:17 -0700
Subject: [PATCH 2/2] Address review feedback

---
 clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp | 4 ++--
 clang/test/CIR/CodeGen/try-catch.cpp                  | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp 
b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
index 4102a2a6f55ae..c7cf0b03422a8 100644
--- a/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
+++ b/clang/test/CIR/CodeGen/cleanup-throw-from-cleanup.cpp
@@ -23,7 +23,7 @@ void testSwitchWithCleanup(int n) {
 // CIR:   cir.cleanup.scope {
 // CIR:     %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR:     %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
-// CIR:     cir.store align(16) %[[VAL]], %[[EXN]] : !s32i, !cir.ptr<!s32i>
+// CIR:     cir.store{{.*}} %[[VAL]], %[[EXN]] : !s32i, !cir.ptr<!s32i>
 // CIR:     cir.throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi
 // CIR:     cir.unreachable
 // CIR:     cir.yield
@@ -46,7 +46,7 @@ void testSwitchWithCleanup(int n) {
 // CIR-FLAT: ^[[BODY]]:
 // CIR-FLAT:   %[[EXN:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
 // CIR-FLAT:   %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
-// CIR-FLAT:   cir.store align(16) %[[VAL]], %[[EXN]]
+// CIR-FLAT:   cir.store{{.*}} %[[VAL]], %[[EXN]]
 // CIR-FLAT:   cir.try_throw %[[EXN]] : !cir.ptr<!s32i>, @_ZTIi 
^[[UNREACH:.+]], ^[[UNWIND:.+]]
 // CIR-FLAT: ^[[UNWIND]]:
 // CIR-FLAT:   %[[ET:.*]] = cir.eh.initiate cleanup : !cir.eh_token
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp 
b/clang/test/CIR/CodeGen/try-catch.cpp
index d5da5d3b2285f..cf971062437a6 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1645,7 +1645,7 @@ int init_catch_param_with_ref_to_ptr_to_non_record() {
 // CIR:         cir.store {{.*}} %[[P_VAL]], %[[RV_ADDR]] : !s32i, 
!cir.ptr<!s32i>
 // CIR:         cir.yield
 // CIR:       } cleanup all {
-// CIR:         cir.end_catch %catch_token : !cir.catch_token
+// CIR:         cir.end_catch %[[CATCH_TOKEN]] : !cir.catch_token
 // CIR:         cir.yield
 // CIR:       }
 // CIR:       cir.yield
@@ -1785,7 +1785,7 @@ void direct_inside_try_catch_with_exception_type() {
 // CIR:         cir.init_catch_param scalar %[[EXN_PTR]] to %[[E]] : 
!cir.ptr<!void>, !cir.ptr<!s32i>
 // CIR:         cir.yield
 // CIR:       } cleanup all {
-// CIR:         cir.end_catch %catch_token : !cir.catch_token
+// CIR:         cir.end_catch %[[CATCH_TOKEN]] : !cir.catch_token
 // CIR:         cir.yield
 // CIR:       }
 // CIR:       cir.yield

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

Reply via email to