[clang] 375d65e - [CIR] Implement EH lowering to Itanium form and LLVM IR (#184386)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 3 12:09:53 PST 2026
Author: Andy Kaylor
Date: 2026-03-03T20:09:49Z
New Revision: 375d65ee8de7dd65e19faab567db2799a52dfe52
URL: https://github.com/llvm/llvm-project/commit/375d65ee8de7dd65e19faab567db2799a52dfe52
DIFF: https://github.com/llvm/llvm-project/commit/375d65ee8de7dd65e19faab567db2799a52dfe52.diff
LOG: [CIR] Implement EH lowering to Itanium form and LLVM IR (#184386)
This introduces a new pass to lower from a flattened, target-independent
form of CIR to a form that uses Itanium-specific representation for
exception handling. It also includes a small amount of code needed to
lower the Itanium form to LLVM IR.
Substantial amounts of this PR were created using agentic AI tools, but
I have carefully reviewed the code, comments, and tests and made changes
as needed.
Added:
clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/include/clang/CIR/Dialect/Passes.h
clang/include/clang/CIR/Dialect/Passes.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
clang/lib/CIR/Lowering/CIRPasses.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
clang/test/CIR/CodeGen/try-catch-tmp.cpp
clang/test/CIR/CodeGen/try-catch.cpp
clang/tools/cir-opt/cir-opt.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a5a5197cd3ea6..064be6f54def2 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3486,7 +3486,6 @@ def CIR_TryCallOp : CIR_CallOpBase<"try_call",[
);
let skipDefaultBuilders = 1;
- let hasLLVMLowering = false;
let builders = [
OpBuilder<(ins "mlir::SymbolRefAttr":$callee,
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
index 7b57b00f7da8c..d441dfcbc6c14 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -24,6 +24,7 @@ namespace mlir {
std::unique_ptr<Pass> createCIRCanonicalizePass();
std::unique_ptr<Pass> createCIRFlattenCFGPass();
std::unique_ptr<Pass> createCIRSimplifyPass();
+std::unique_ptr<Pass> createCIREHABILoweringPass();
std::unique_ptr<Pass> createCXXABILoweringPass();
std::unique_ptr<Pass> createTargetLoweringPass();
std::unique_ptr<Pass> createHoistAllocasPass();
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index 0d60d5e33dd4b..32cd182aacec7 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -150,6 +150,29 @@ def TargetLowering : Pass<"cir-target-lowering", "mlir::ModuleOp"> {
let dependentDialects = ["cir::CIRDialect"];
}
+def CIREHABILowering : Pass<"cir-eh-abi-lowering", "mlir::ModuleOp"> {
+ let summary = "Lower flattened CIR EH operations to target-specific ABI form";
+ let description = [{
+ This pass lowers the ABI-agnostic exception handling operations produced by
+ CFG flattening into an ABI-specific form. Currently only the Itanium C++
+ ABI is implemented.
+
+ For the Itanium ABI, the pass performs the following transformations:
+ - Replaces `cir.eh.initiate` with `cir.eh.inflight_exception`
+ - Replaces `cir.eh.dispatch` with `cir.eh.typeid` + comparison chains
+ - Removes `cir.begin_cleanup` and `cir.end_cleanup` operations
+ - Replaces `cir.begin_catch` with a call to `__cxa_begin_catch`
+ - Replaces `cir.end_catch` with a call to `__cxa_end_catch`
+ - Replaces `cir.resume` with `cir.resume.flat`
+ - Sets the personality function attribute on functions that require EH
+
+ If a non-Itanium ABI is specified, the pass emits a diagnostic indicating
+ that the target is not yet implemented.
+ }];
+ let constructor = "mlir::createCIREHABILoweringPass()";
+ let dependentDialects = ["cir::CIRDialect"];
+}
+
def LoweringPrepare : Pass<"cir-lowering-prepare"> {
let summary = "Lower to more fine-grained CIR operations before lowering to "
"other dialects";
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 02d22bb628900..ab05d2191d9b0 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -114,8 +114,6 @@ struct MissingFeatures {
static bool opCallExtParameterInfo() { return false; }
static bool opCallCIRGenFuncInfoParamInfo() { return false; }
static bool opCallCIRGenFuncInfoExtParamInfo() { return false; }
- static bool opCallLandingPad() { return false; }
- static bool opCallContinueBlock() { return false; }
static bool opCallChain() { return false; }
static bool opCallExceptionAttr() { return false; }
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 3fe6c5f9ecb55..411bbc386b9d1 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -4,6 +4,7 @@ add_clang_library(MLIRCIRTransforms
CIRCanonicalize.cpp
CIRSimplify.cpp
CXXABILowering.cpp
+ EHABILowering.cpp
TargetLowering.cpp
FlattenCFG.cpp
HoistAllocas.cpp
diff --git a/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
new file mode 100644
index 0000000000000..d5d10b1b251be
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
@@ -0,0 +1,502 @@
+//===- EHABILowering.cpp - Lower flattened CIR EH ops to ABI-specific form ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a pass that lowers ABI-agnostic flattened CIR exception
+// handling operations into an ABI-specific form. Currently only the Itanium
+// C++ ABI is supported.
+//
+// The Itanium ABI lowering performs these transformations:
+// - cir.eh.initiate → cir.eh.inflight_exception (landing pad)
+// - cir.eh.dispatch → cir.eh.typeid + cir.cmp + cir.brcond chains
+// - cir.begin_cleanup → (removed)
+// - cir.end_cleanup → (removed)
+// - cir.begin_catch → call to __cxa_begin_catch
+// - cir.end_catch → call to __cxa_end_catch
+// - cir.resume → cir.resume.flat
+// - !cir.eh_token values → (!cir.ptr<!void>, !u32i) value pairs
+// - personality function set on functions requiring EH
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassDetail.h"
+#include "mlir/IR/Builders.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "clang/CIR/Dialect/Passes.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/TargetParser/Triple.h"
+
+using namespace mlir;
+using namespace cir;
+
+namespace mlir {
+#define GEN_PASS_DEF_CIREHABILOWERING
+#include "clang/CIR/Dialect/Passes.h.inc"
+} // namespace mlir
+
+namespace {
+
+//===----------------------------------------------------------------------===//
+// Shared utilities
+//===----------------------------------------------------------------------===//
+
+/// Ensure a function with the given name and type exists in the module. If it
+/// does not exist, create a private external declaration.
+static cir::FuncOp getOrCreateRuntimeFuncDecl(mlir::ModuleOp mod,
+ mlir::Location loc,
+ StringRef name,
+ cir::FuncType funcTy) {
+ if (auto existing = mod.lookupSymbol<cir::FuncOp>(name))
+ return existing;
+
+ mlir::OpBuilder builder(mod.getContext());
+ builder.setInsertionPointToEnd(mod.getBody());
+ auto funcOp = cir::FuncOp::create(builder, loc, name, funcTy);
+ funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
+ funcOp.setPrivate();
+ return funcOp;
+}
+
+//===----------------------------------------------------------------------===//
+// EH ABI Lowering Base Class
+//===----------------------------------------------------------------------===//
+
+/// Abstract base class for exception-handling ABI lowering.
+/// Each supported ABI (Itanium, Microsoft, etc.) provides a concrete subclass.
+class EHABILowering {
+public:
+ explicit EHABILowering(mlir::ModuleOp mod)
+ : mod(mod), ctx(mod.getContext()), builder(ctx) {}
+ virtual ~EHABILowering() = default;
+
+ /// Lower all EH operations in the module to an ABI-specific form.
+ virtual mlir::LogicalResult run() = 0;
+
+protected:
+ mlir::ModuleOp mod;
+ mlir::MLIRContext *ctx;
+ mlir::OpBuilder builder;
+};
+
+//===----------------------------------------------------------------------===//
+// Itanium EH ABI Lowering
+//===----------------------------------------------------------------------===//
+
+/// Lowers flattened CIR EH operations to the Itanium C++ ABI form.
+///
+/// The entry point is run(), which iterates over all functions and
+/// calls lowerFunc() for each. lowerFunc() drives all lowering from
+/// cir.eh.initiate operations: every other EH op (begin/end_cleanup,
+/// eh.dispatch, begin/end_catch, resume) is reachable by tracing the
+/// eh_token produced by the initiate through its users.
+class ItaniumEHLowering : public EHABILowering {
+public:
+ using EHABILowering::EHABILowering;
+ mlir::LogicalResult run() override;
+
+private:
+ /// Maps a !cir.eh_token value to its Itanium ABI replacement pair:
+ /// an exception pointer (!cir.ptr<!void>) and a type id (!u32i).
+ using EhTokenMap = DenseMap<mlir::Value, std::pair<mlir::Value, mlir::Value>>;
+
+ cir::VoidType voidType;
+ cir::PointerType voidPtrType;
+ cir::PointerType u8PtrType;
+ cir::IntType u32Type;
+
+ // Cached runtime function declarations, initialized when needed by
+ // ensureRuntimeDecls().
+ cir::FuncOp personalityFunc;
+ cir::FuncOp beginCatchFunc;
+ cir::FuncOp endCatchFunc;
+
+ constexpr const static ::llvm::StringLiteral kGxxPersonality =
+ "__gxx_personality_v0";
+
+ void ensureRuntimeDecls(mlir::Location loc);
+ mlir::LogicalResult lowerFunc(cir::FuncOp funcOp);
+ void lowerEhInitiate(cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
+ SmallVectorImpl<mlir::Operation *> &deadOps);
+ void lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value exnPtr,
+ mlir::Value typeId,
+ SmallVectorImpl<mlir::Operation *> &deadOps);
+};
+
+/// Lower all EH operations in the module to the Itanium-specific form.
+mlir::LogicalResult ItaniumEHLowering::run() {
+ // Pre-compute the common types used throughout all function lowerings.
+ // 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);
+ auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false);
+ u8PtrType = cir::PointerType::get(u8Type);
+ u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false);
+
+ for (cir::FuncOp funcOp : mod.getOps<cir::FuncOp>()) {
+ if (mlir::failed(lowerFunc(funcOp)))
+ return mlir::failure();
+ }
+ return mlir::success();
+}
+
+/// Ensure the necessary Itanium runtime function declarations exist in the
+/// module.
+void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) {
+ // TODO(cir): Handle other personality functions. This probably isn't needed
+ // here if we fix codegen to always set the personality function.
+ if (!personalityFunc) {
+ auto s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true);
+ auto personalityFuncTy = cir::FuncType::get({}, s32Type, /*isVarArg=*/true);
+ personalityFunc = getOrCreateRuntimeFuncDecl(mod, loc, kGxxPersonality,
+ personalityFuncTy);
+ }
+
+ if (!beginCatchFunc) {
+ auto beginCatchFuncTy =
+ cir::FuncType::get({voidPtrType}, u8PtrType, /*isVarArg=*/false);
+ beginCatchFunc = getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_begin_catch",
+ beginCatchFuncTy);
+ }
+
+ if (!endCatchFunc) {
+ auto endCatchFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
+ endCatchFunc =
+ getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_end_catch", endCatchFuncTy);
+ }
+}
+
+/// Lower all EH operations in a single function.
+mlir::LogicalResult ItaniumEHLowering::lowerFunc(cir::FuncOp funcOp) {
+ if (funcOp.isDeclaration())
+ return mlir::success();
+
+ // All EH lowering follows from cir.eh.initiate operations. The token each
+ // initiate produces connects it to every other EH op in the function
+ // (begin/end_cleanup, eh.dispatch, begin/end_catch, resume) through the
+ // token graph. A single walk to collect initiates is therefore sufficient.
+ SmallVector<cir::EhInitiateOp> initiateOps;
+ funcOp.walk([&](cir::EhInitiateOp op) { initiateOps.push_back(op); });
+ if (initiateOps.empty())
+ return mlir::success();
+
+ ensureRuntimeDecls(funcOp.getLoc());
+
+ // Set the personality function if it is not already set.
+ // TODO(cir): The personality function should already have been set by this
+ // point. If we've seen a try operation, it will have been set by
+ // emitCXXTryStmt. If we only have cleanups, it may not have been set. We
+ // need to fix that in CodeGen. This is a placeholder until that is done.
+ if (!funcOp.getPersonality())
+ funcOp.setPersonality(kGxxPersonality);
+
+ // Lower each initiate and all EH ops connected to it. The token map is
+ // shared across all initiate operations. Multiple initiates may flow into the
+ // same dispatch block, and the map ensures the arguments are registered
+ // only once. Dispatch ops are scheduled for deferred removal so that sibling
+ // initiates can still read catch types from a shared dispatch.
+ EhTokenMap ehTokenMap;
+ SmallVector<mlir::Operation *> deadOps;
+ for (cir::EhInitiateOp initiateOp : initiateOps)
+ lowerEhInitiate(initiateOp, ehTokenMap, deadOps);
+
+ // Erase operations that were deferred during per-initiate processing
+ // (dispatch ops whose catch types were read by multiple initiates).
+ for (mlir::Operation *op : deadOps)
+ op->erase();
+
+ // Remove the !cir.eh_token block arguments that were replaced by (ptr, u32)
+ // pairs. Iterate in reverse to preserve argument indices during removal.
+ for (mlir::Block &block : funcOp.getBody()) {
+ for (int i = block.getNumArguments() - 1; i >= 0; --i) {
+ if (mlir::isa<cir::EhTokenType>(block.getArgument(i).getType()))
+ block.eraseArgument(i);
+ }
+ }
+
+ return mlir::success();
+}
+
+/// Lower all EH operations connected to a single cir.eh.initiate.
+///
+/// The cir.eh.initiate is the root of a token graph. The token it produces
+/// flows through branch edges to consuming operations:
+///
+/// cir.eh.initiate → (via cir.br) → cir.begin_cleanup
+/// → cir.end_cleanup (via cleanup_token)
+/// → (via cir.br) → cir.eh.dispatch
+/// → (successors) →
+/// cir.begin_catch
+/// → cir.end_catch
+/// (via catch_token)
+/// → cir.resume
+///
+/// A single traversal of the token graph discovers and processes every
+/// connected op inline. The inflight_exception is created up-front without
+/// a catch_type_list; when the dispatch is encountered during traversal,
+/// the catch types are read and set on the inflight op.
+///
+/// Dispatch ops are not erased during per-initiate processing because they may
+/// be used by other initiate ops that haven't yet been lowered. Instead they
+/// are added to \p deadOps and erased by the caller after all initiates have
+/// been lowered.
+///
+/// \p ehTokenMap is shared across all initiates in the function so that block
+/// arguments reachable from multiple sibling initiates are registered once.
+void ItaniumEHLowering::lowerEhInitiate(
+ cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
+ SmallVectorImpl<mlir::Operation *> &deadOps) {
+ mlir::Value rootToken = initiateOp.getEhToken();
+
+ // Create the inflight_exception without a catch_type_list. The catch types
+ // will be set once we encounter the dispatch during the traversal below.
+ builder.setInsertionPoint(initiateOp);
+ auto inflightOp = cir::EhInflightOp::create(
+ builder, initiateOp.getLoc(), /*cleanup=*/initiateOp.getCleanup(),
+ /*catch_type_list=*/mlir::ArrayAttr{});
+
+ ehTokenMap[rootToken] = {inflightOp.getExceptionPtr(),
+ inflightOp.getTypeId()};
+
+ // Single traversal of the token graph. For each token value (the root token
+ // or a block argument that carries it), we snapshot its users, register
+ // (ptr, u32) replacement arguments on successor blocks, then process every
+ // user inline. This avoids collecting ops into separate vectors.
+ SmallVector<mlir::Value> worklist;
+ SmallPtrSet<mlir::Value, 8> visited;
+ worklist.push_back(rootToken);
+
+ while (!worklist.empty()) {
+ mlir::Value current = worklist.pop_back_val();
+ if (!visited.insert(current).second)
+ continue;
+
+ // Snapshot users before modifying any of them (erasing ops during
+ // iteration would invalidate the use-list iterator).
+ SmallVector<mlir::Operation *> users;
+ for (mlir::OpOperand &use : current.getUses())
+ users.push_back(use.getOwner());
+
+ // Register replacement block arguments on successor blocks (extending the
+ // worklist), then lower the op itself.
+ for (mlir::Operation *user : users) {
+ // Trace into successor blocks to register (ptr, u32) replacement
+ // arguments for any !cir.eh_token block arguments found there. Even
+ // if a block arg was already registered by a sibling initiate, it is
+ // still added to the worklist so that the traversal can reach the
+ // shared dispatch to read catch types.
+ for (unsigned s = 0; s < user->getNumSuccessors(); ++s) {
+ mlir::Block *succ = user->getSuccessor(s);
+ for (mlir::BlockArgument arg : succ->getArguments()) {
+ if (!mlir::isa<cir::EhTokenType>(arg.getType()))
+ continue;
+ if (!ehTokenMap.count(arg)) {
+ mlir::Value ptrArg = succ->addArgument(voidPtrType, arg.getLoc());
+ mlir::Value u32Arg = succ->addArgument(u32Type, arg.getLoc());
+ ehTokenMap[arg] = {ptrArg, u32Arg};
+ }
+ worklist.push_back(arg);
+ }
+ }
+
+ if (auto op = mlir::dyn_cast<cir::BeginCleanupOp>(user)) {
+ // begin_cleanup / end_cleanup are no-ops for Itanium. Erase the
+ // end_cleanup first (drops the cleanup_token use) then the begin.
+ for (auto &tokenUsers :
+ llvm::make_early_inc_range(op.getCleanupToken().getUses())) {
+ if (auto endOp =
+ mlir::dyn_cast<cir::EndCleanupOp>(tokenUsers.getOwner()))
+ endOp.erase();
+ }
+ op.erase();
+ } else if (auto op = mlir::dyn_cast<cir::BeginCatchOp>(user)) {
+ // Replace end_catch → __cxa_end_catch (drops the catch_token use),
+ // then replace begin_catch → __cxa_begin_catch.
+ for (auto &tokenUsers :
+ llvm::make_early_inc_range(op.getCatchToken().getUses())) {
+ if (auto endOp =
+ mlir::dyn_cast<cir::EndCatchOp>(tokenUsers.getOwner())) {
+ builder.setInsertionPoint(endOp);
+ cir::CallOp::create(builder, endOp.getLoc(),
+ mlir::FlatSymbolRefAttr::get(endCatchFunc),
+ voidType, mlir::ValueRange{});
+ endOp.erase();
+ }
+ }
+
+ auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
+ builder.setInsertionPoint(op);
+ auto callOp = cir::CallOp::create(
+ builder, op.getLoc(), mlir::FlatSymbolRefAttr::get(beginCatchFunc),
+ u8PtrType, mlir::ValueRange{exnPtr});
+ mlir::Value castResult = callOp.getResult();
+ mlir::Type expectedPtrType = op.getExnPtr().getType();
+ if (castResult.getType() != expectedPtrType)
+ castResult =
+ cir::CastOp::create(builder, op.getLoc(), expectedPtrType,
+ cir::CastKind::bitcast, callOp.getResult());
+ op.getExnPtr().replaceAllUsesWith(castResult);
+ op.erase();
+ } else if (auto op = mlir::dyn_cast<cir::EhDispatchOp>(user)) {
+ // Read catch types from the dispatch and set them on the inflight op.
+ mlir::ArrayAttr catchTypes = op.getCatchTypesAttr();
+ if (catchTypes && catchTypes.size() > 0) {
+ SmallVector<mlir::Attribute> typeSymbols;
+ for (mlir::Attribute attr : catchTypes)
+ typeSymbols.push_back(
+ mlir::cast<cir::GlobalViewAttr>(attr).getSymbol());
+ inflightOp.setCatchTypeListAttr(builder.getArrayAttr(typeSymbols));
+ }
+ // Only lower the dispatch once. A sibling initiate sharing the same
+ // dispatch will still read its catch types (above), but the comparison
+ // chain and branch replacement are only created the first time.
+ if (!llvm::is_contained(deadOps, op.getOperation())) {
+ auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
+ lowerDispatch(op, exnPtr, typeId, deadOps);
+ }
+ } else if (auto op = mlir::dyn_cast<cir::ResumeOp>(user)) {
+ auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
+ builder.setInsertionPoint(op);
+ cir::ResumeFlatOp::create(builder, op.getLoc(), exnPtr, typeId);
+ op.erase();
+ } else if (auto op = mlir::dyn_cast<cir::BrOp>(user)) {
+ // Replace eh_token operands with the (ptr, u32) pair.
+ SmallVector<mlir::Value> newOperands;
+ bool changed = false;
+ for (mlir::Value operand : op.getDestOperands()) {
+ auto it = ehTokenMap.find(operand);
+ if (it != ehTokenMap.end()) {
+ newOperands.push_back(it->second.first);
+ newOperands.push_back(it->second.second);
+ changed = true;
+ } else {
+ newOperands.push_back(operand);
+ }
+ }
+ if (changed) {
+ builder.setInsertionPoint(op);
+ cir::BrOp::create(builder, op.getLoc(), op.getDest(), newOperands);
+ op.erase();
+ }
+ }
+ }
+ }
+
+ initiateOp.erase();
+}
+
+/// Lower a cir.eh.dispatch by creating a comparison chain in new blocks.
+/// The dispatch itself is replaced with a branch to the first comparison
+/// block and added to deadOps for deferred removal.
+void ItaniumEHLowering::lowerDispatch(
+ cir::EhDispatchOp dispatch, mlir::Value exnPtr, mlir::Value typeId,
+ SmallVectorImpl<mlir::Operation *> &deadOps) {
+ mlir::Location dispLoc = dispatch.getLoc();
+ mlir::Block *defaultDest = dispatch.getDefaultDestination();
+ mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr();
+ mlir::SuccessorRange catchDests = dispatch.getCatchDestinations();
+ mlir::Block *dispatchBlock = dispatch->getBlock();
+
+ // Build the comparison chain in new blocks inserted after the dispatch's
+ // block. The dispatch itself is replaced with a branch to the first
+ // comparison block and scheduled for deferred removal.
+ if (!catchTypes || catchTypes.empty()) {
+ // No typed catches: replace dispatch with a direct branch.
+ builder.setInsertionPoint(dispatch);
+ cir::BrOp::create(builder, dispLoc, defaultDest,
+ mlir::ValueRange{exnPtr, typeId});
+ } else {
+ unsigned numCatches = catchTypes.size();
+
+ // Create and populate comparison blocks in reverse order so that each
+ // block's false destination (the next comparison block, or defaultDest
+ // for the last one) is already available. Each createBlock inserts
+ // before the previous one, so the blocks end up in forward order.
+ mlir::Block *insertBefore = dispatchBlock->getNextNode();
+ mlir::Block *falseDest = defaultDest;
+ mlir::Block *firstCmpBlock = nullptr;
+ for (int i = numCatches - 1; i >= 0; --i) {
+ auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type},
+ {dispLoc, dispLoc});
+
+ mlir::Value cmpExnPtr = cmpBlock->getArgument(0);
+ mlir::Value cmpTypeId = cmpBlock->getArgument(1);
+
+ auto globalView = mlir::cast<cir::GlobalViewAttr>(catchTypes[i]);
+ auto ehTypeIdOp =
+ cir::EhTypeIdOp::create(builder, dispLoc, globalView.getSymbol());
+ auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::eq,
+ cmpTypeId, ehTypeIdOp.getTypeId());
+
+ cir::BrCondOp::create(builder, dispLoc, cmpOp, catchDests[i], falseDest,
+ mlir::ValueRange{cmpExnPtr, cmpTypeId},
+ mlir::ValueRange{cmpExnPtr, cmpTypeId});
+
+ insertBefore = cmpBlock;
+ falseDest = cmpBlock;
+ firstCmpBlock = cmpBlock;
+ }
+
+ // Replace the dispatch with a branch to the first comparison block.
+ builder.setInsertionPoint(dispatch);
+ cir::BrOp::create(builder, dispLoc, firstCmpBlock,
+ mlir::ValueRange{exnPtr, typeId});
+ }
+
+ // Schedule the dispatch for deferred removal. We cannot erase it now because
+ // a sibling initiate that shares this dispatch may still need to read its
+ // catch types.
+ deadOps.push_back(dispatch);
+}
+
+//===----------------------------------------------------------------------===//
+// The Pass
+//===----------------------------------------------------------------------===//
+
+struct CIREHABILoweringPass
+ : public impl::CIREHABILoweringBase<CIREHABILoweringPass> {
+ CIREHABILoweringPass() = default;
+ void runOnOperation() override;
+};
+
+void CIREHABILoweringPass::runOnOperation() {
+ auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
+
+ // The target triple is attached to the module as the "cir.triple" attribute.
+ // If it is absent (e.g. a CIR module parsed from text without a triple) we
+ // cannot determine the ABI and must skip the pass.
+ auto tripleAttr = mlir::dyn_cast_if_present<mlir::StringAttr>(
+ mod->getAttr(cir::CIRDialect::getTripleAttrName()));
+ if (!tripleAttr) {
+ mod.emitError("Module has no target triple");
+ return;
+ }
+
+ // Select the ABI-specific lowering handler from the triple. The Microsoft
+ // C++ ABI targets a Windows MSVC environment; everything else uses Itanium.
+ // Extend this when Microsoft ABI lowering is added.
+ llvm::Triple triple(tripleAttr.getValue());
+ std::unique_ptr<EHABILowering> lowering;
+ if (triple.isWindowsMSVCEnvironment()) {
+ mod.emitError(
+ "EH ABI lowering is not yet implemented for the Microsoft ABI");
+ return signalPassFailure();
+ } else {
+ lowering = std::make_unique<ItaniumEHLowering>(mod);
+ }
+
+ if (mlir::failed(lowering->run()))
+ return signalPassFailure();
+}
+
+} // namespace
+
+std::unique_ptr<Pass> mlir::createCIREHABILoweringPass() {
+ return std::make_unique<CIREHABILoweringPass>();
+}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index c0dcc4ba5e90a..7b93356a34c38 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -49,6 +49,7 @@ namespace mlir {
void populateCIRPreLoweringPasses(OpPassManager &pm) {
pm.addPass(createHoistAllocasPass());
pm.addPass(createCIRFlattenCFGPass());
+ pm.addPass(createCIREHABILoweringPass());
pm.addPass(createGotoSolverPass());
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 03085ad29ab78..4f71c588b5018 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1837,7 +1837,9 @@ static mlir::LogicalResult
rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
mlir::ConversionPatternRewriter &rewriter,
const mlir::TypeConverter *converter,
- mlir::FlatSymbolRefAttr calleeAttr) {
+ mlir::FlatSymbolRefAttr calleeAttr,
+ mlir::Block *continueBlock = nullptr,
+ mlir::Block *landingPadBlock = nullptr) {
llvm::SmallVector<mlir::Type, 8> llvmResults;
mlir::ValueTypeRange<mlir::ResultRange> cirResults = op->getResultTypes();
auto call = cast<cir::CIRCallOpInterface>(op);
@@ -1908,18 +1910,23 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
converter->convertType(calleeFuncTy));
}
- assert(!cir::MissingFeatures::opCallLandingPad());
- assert(!cir::MissingFeatures::opCallContinueBlock());
assert(!cir::MissingFeatures::opCallCallConv());
- auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
- op, llvmFnTy, calleeAttr, callOperands);
- newOp->setAttrs(attributes);
- if (memoryEffects)
- newOp.setMemoryEffectsAttr(memoryEffects);
- newOp.setNoUnwind(noUnwind);
- newOp.setWillReturn(willReturn);
- newOp.setNoreturn(noReturn);
+ if (landingPadBlock) {
+ auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::InvokeOp>(
+ op, llvmFnTy, calleeAttr, callOperands, continueBlock,
+ mlir::ValueRange{}, landingPadBlock, mlir::ValueRange{});
+ newOp->setAttrs(attributes);
+ } else {
+ auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
+ op, llvmFnTy, calleeAttr, callOperands);
+ newOp->setAttrs(attributes);
+ if (memoryEffects)
+ newOp.setMemoryEffectsAttr(memoryEffects);
+ newOp.setNoUnwind(noUnwind);
+ newOp.setWillReturn(willReturn);
+ newOp.setNoreturn(noReturn);
+ }
return mlir::success();
}
@@ -1931,6 +1938,15 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
getTypeConverter(), op.getCalleeAttr());
}
+mlir::LogicalResult CIRToLLVMTryCallOpLowering::matchAndRewrite(
+ cir::TryCallOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ assert(!cir::MissingFeatures::opCallCallConv());
+ return rewriteCallOrInvoke(op.getOperation(), adaptor.getOperands(), rewriter,
+ getTypeConverter(), op.getCalleeAttr(),
+ op.getNormalDest(), op.getUnwindDest());
+}
+
mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
cir::ReturnAddrOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
index 5cb60aaf6b2cc..f7951e229c06f 100644
--- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp
+++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
@@ -1,8 +1,12 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %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
+// TODO(cir): Merge this with try-catch.cpp.
+
int division();
void call_function_inside_try_catch_all() {
@@ -29,6 +33,43 @@ void call_function_inside_try_catch_all() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z34call_function_inside_try_catch_allv() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: catch ptr null
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: br label %[[BEGIN_CATCH:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI1]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
// OGCG: define {{.*}} void @_Z34call_function_inside_try_catch_allv() #0 personality ptr @__gxx_personality_v0
// OGCG: %[[EXN_OBJ_ADDR:.*]] = alloca ptr, align 8
@@ -82,6 +123,58 @@ void call_function_inside_try_catch_with_exception_type() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z50call_function_inside_try_catch_with_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: catch ptr @_ZTIi
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: br label %[[DISPATCH:.*]]
+// LLVM: [[DISPATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
+// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
+// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: %[[LOAD:.*]] = load i32, ptr %[[TOKEN]], align 4
+// LLVM: store i32 %[[LOAD]], ptr {{.*}}, align 4
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[RESUME]]:
+// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
+// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
+// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
+
// OGCG: define {{.*}} void @_Z50call_function_inside_try_catch_with_exception_typev() #0 personality ptr @__gxx_personality_v0
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
@@ -148,6 +241,58 @@ void call_function_inside_try_catch_with_complex_exception_type() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z58call_function_inside_try_catch_with_complex_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: catch ptr @_ZTICi
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: br label %[[DISPATCH:.*]]
+// LLVM: [[DISPATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTICi)
+// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
+// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: %[[LOAD:.*]] = load { i32, i32 }, ptr %[[TOKEN]], align 4
+// LLVM: store { i32, i32 } %[[LOAD]], ptr {{.*}}, align 4
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[RESUME]]:
+// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
+// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
+// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
+
// OGCG: define {{.*}} void @_Z58call_function_inside_try_catch_with_complex_exception_typev() #0 personality ptr @__gxx_personality_v0
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
@@ -219,6 +364,57 @@ void call_function_inside_try_catch_with_array_exception_type() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z56call_function_inside_try_catch_with_array_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: catch ptr @_ZTIPi
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: br label %[[DISPATCH:.*]]
+// LLVM: [[DISPATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIPi)
+// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
+// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: store ptr %[[TOKEN]], ptr {{.*}}, align 8
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[RESUME]]:
+// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
+// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
+// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
+
// OGCG: define {{.*}} void @_Z56call_function_inside_try_catch_with_array_exception_typev() #0 personality ptr @__gxx_personality_v0
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
@@ -292,6 +488,66 @@ void call_function_inside_try_catch_with_exception_type_and_catch_all() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z64call_function_inside_try_catch_with_exception_type_and_catch_allv() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: catch ptr @_ZTIi
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: br label %[[DISPATCH:.*]]
+// LLVM: [[DISPATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
+// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
+// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[CATCH_ALL:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: %[[LOAD:.*]] = load i32, ptr %[[TOKEN]], align 4
+// LLVM: store i32 %[[LOAD]], ptr {{.*}}, align 4
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[CATCH_ALL]]:
+// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
+// LLVM: %[[TOKEN2:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI3]])
+// LLVM: br label %[[CATCH_ALL_BODY:.*]]
+// LLVM: [[CATCH_ALL_BODY]]:
+// LLVM: br label %[[END_CATCH2:.*]]
+// LLVM: [[END_CATCH2]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH2:.*]]
+// LLVM: [[END_DISPATCH2]]:
+// LLVM: br label %[[END_TRY2:.*]]
+// LLVM: [[END_TRY2]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
+
// OGCG: define {{.*}} void @_Z64call_function_inside_try_catch_with_exception_type_and_catch_allv() #0 personality ptr @__gxx_personality_v0
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
@@ -364,6 +620,56 @@ void cleanup_inside_try_body() {
// CIR: }
// CIR: }
+// LLVM: define {{.*}} void @_Z23cleanup_inside_try_bodyv() {{.*}} personality ptr @__gxx_personality_v0
+// LLVM: br label %[[TRY_SCOPE:.*]]
+// LLVM: [[TRY_SCOPE]]:
+// LLVM: br label %[[TRY_BEGIN:.*]]
+// LLVM: [[TRY_BEGIN]]:
+// LLVM: br label %[[CLEANUP_SCOPE:.*]]
+// LLVM: [[CLEANUP_SCOPE]]:
+// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM: br label %[[CLEANUP:.*]]
+// LLVM: [[CLEANUP]]:
+// LLVM: call void @_ZN1SD1Ev(ptr {{.*}})
+// LLVM: br label %[[END_CLEANUP:.*]]
+// LLVM: [[END_CLEANUP]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[LANDING_PAD]]:
+// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
+// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
+// LLVM: br label %[[CLEANUP_LANDING:.*]]
+// LLVM: [[CLEANUP_LANDING]]:
+// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
+// LLVM: call void @_ZN1SD1Ev(ptr {{.*}})
+// LLVM: br label %[[CATCH:.*]]
+// LLVM: [[CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CLEANUP_LANDING:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CLEANUP_LANDING:.*]] ]
+// LLVM: br label %[[BEGIN_CATCH:.*]]
+// LLVM: [[BEGIN_CATCH]]:
+// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[CATCH:.*]] ]
+// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
+// LLVM: br label %[[CATCH_BODY:.*]]
+// LLVM: [[CATCH_BODY]]:
+// LLVM: br label %[[END_CATCH:.*]]
+// LLVM: [[END_CATCH]]:
+// LLVM: call void @__cxa_end_catch()
+// LLVM: br label %[[END_DISPATCH:.*]]
+// LLVM: [[END_DISPATCH]]:
+// LLVM: br label %[[END_TRY:.*]]
+// LLVM: [[END_TRY]]:
+// LLVM: br label %[[TRY_CONT:.*]]
+// LLVM: [[TRY_CONT]]:
+// LLVM: br label %[[DONE:.*]]
+// LLVM: [[DONE]]:
+// LLVM: ret void
+
// OGCG: define {{.*}} void @_Z23cleanup_inside_try_bodyv() {{.*}} personality ptr @__gxx_personality_v0 {
// OGCG: %[[S:.*]] = alloca %struct.S
// OGCG: %[[EXN_SLOT:.*]] = alloca ptr
diff --git a/clang/test/CIR/CodeGen/try-catch.cpp b/clang/test/CIR/CodeGen/try-catch.cpp
index 81ac15eeb7016..27e3d8ef41115 100644
--- a/clang/test/CIR/CodeGen/try-catch.cpp
+++ b/clang/test/CIR/CodeGen/try-catch.cpp
@@ -1,10 +1,10 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
// RUN: %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
-// TODO(cir): Reenable lowering to LLVM after new try-catch lowering is implemented.
-
void empty_try_block_with_catch_all() {
try {} catch (...) {}
}
diff --git a/clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir b/clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir
new file mode 100644
index 0000000000000..42e2722a43455
--- /dev/null
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir
@@ -0,0 +1,514 @@
+// RUN: cir-opt %s -cir-eh-abi-lowering -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s
+
+// Test Itanium ABI lowering of flattened CIR exception handling operations.
+// The input is the form produced by cir-flatten-cfg, and the output should
+// have all EH-specific tokens replaced with concrete values and ABI calls.
+
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+!rec_SomeClass = !cir.record<struct "SomeClass" {!s32i}>
+!rec_exception = !cir.record<struct "std::exception" {!s32i}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+
+// Test: EH cleanup only (no catch handlers).
+// cir.eh.initiate cleanup → cir.eh.inflight_exception cleanup
+// cir.begin_cleanup / cir.end_cleanup → removed
+// cir.resume → cir.resume.flat
+cir.func @test_cleanup_only() {
+ %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
+ cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.try_call @doSomething(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_SomeClass>) -> ()
+^bb1:
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.br ^bb5
+^bb2:
+ %1 = cir.eh.initiate cleanup : !cir.eh_token
+ cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token : !cir.eh_token):
+ %2 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.end_cleanup %2 : !cir.cleanup_token
+ cir.resume %eh_token : !cir.eh_token
+^bb5:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_cleanup_only()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
+//
+// Normal path unchanged.
+// CHECK: ^[[NORMAL]]:
+// CHECK: cir.call @dtor
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Unwind: eh.initiate replaced with eh.inflight_exception.
+// CHECK: ^[[UNWIND]]:
+// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception cleanup
+// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
+//
+// Cleanup: begin_cleanup/end_cleanup removed, resume becomes resume.flat.
+// CHECK: ^[[CLEANUP]](%[[C_EXN:.*]]: !cir.ptr<!void>, %[[C_TID:.*]]: !u32i):
+// CHECK-NOT: cir.begin_cleanup
+// CHECK: cir.call @dtor
+// CHECK-NOT: cir.end_cleanup
+// CHECK: cir.resume.flat %[[C_EXN]], %[[C_TID]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: Try-catch with catch-all handler.
+// cir.eh.dispatch [catch_all] → cir.br to catch-all block
+// cir.begin_catch → __cxa_begin_catch + bitcast
+// cir.end_catch → __cxa_end_catch
+cir.func @test_catch_all() {
+ cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
+^bb1:
+ cir.br ^bb5
+^bb2:
+ %1 = cir.eh.initiate : !cir.eh_token
+ cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token : !cir.eh_token):
+ cir.eh.dispatch %eh_token : !cir.eh_token [
+ catch_all : ^bb4
+ ]
+^bb4(%eh_token.1 : !cir.eh_token):
+ %ct, %exn = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+ cir.end_catch %ct : !cir.catch_token
+ cir.br ^bb5
+^bb5:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_catch_all()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
+//
+// CHECK: ^[[NORMAL]]:
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Landing pad with catch_all (no cleanup, no typed catches).
+// CHECK: ^[[UNWIND]]:
+// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception
+// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch replaced with direct branch to catch-all handler.
+// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
+// CHECK: cir.br ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Catch-all: begin_catch → __cxa_begin_catch, end_catch → __cxa_end_catch.
+// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: %{{.*}} = cir.call @__cxa_begin_catch(%[[CA_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: Try-catch with typed handler and unwind.
+// cir.eh.dispatch with typed catch → typeid + cmp + brcond chain
+// unwind handler → cir.resume.flat
+cir.func @test_typed_catch_with_unwind() {
+ cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
+^bb1:
+ cir.br ^bb6
+^bb2:
+ %1 = cir.eh.initiate : !cir.eh_token
+ cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token : !cir.eh_token):
+ cir.eh.dispatch %eh_token : !cir.eh_token [
+ catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb4,
+ unwind : ^bb5
+ ]
+^bb4(%eh_token.1 : !cir.eh_token):
+ %ct, %exn = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
+ cir.end_catch %ct : !cir.catch_token
+ cir.br ^bb6
+^bb5(%eh_token.2 : !cir.eh_token):
+ cir.resume %eh_token.2 : !cir.eh_token
+^bb6:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_typed_catch_with_unwind()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
+//
+// CHECK: ^[[NORMAL]]:
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Landing pad with typed catch list.
+// CHECK: ^[[UNWIND]]:
+// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception [@_ZTISt9exception]
+// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch: branch to comparison chain.
+// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
+//
+// Comparison: typeid comparison + brcond.
+// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
+// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
+// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
+// CHECK: cir.brcond %[[CMP]] ^[[CATCH:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[UNWIND_HANDLER:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Typed catch handler.
+// CHECK: ^[[CATCH]](%[[C_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: %{{.*}} = cir.call @__cxa_begin_catch(%[[C_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// Unwind handler: resume.flat.
+// CHECK: ^[[UNWIND_HANDLER]](%[[UW_EXN:.*]]: !cir.ptr<!void>, %[[UW_TID:.*]]: !u32i):
+// CHECK: cir.resume.flat %[[UW_EXN]], %[[UW_TID]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: Try-catch with multiple typed handlers and catch-all.
+// Dispatch → chain of typeid comparisons, catch-all as final default.
+cir.func @test_multiple_typed_and_catch_all() {
+ cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
+^bb1:
+ cir.br ^bb7
+^bb2:
+ %1 = cir.eh.initiate : !cir.eh_token
+ cir.br ^bb3(%1 : !cir.eh_token)
+^bb3(%eh_token : !cir.eh_token):
+ cir.eh.dispatch %eh_token : !cir.eh_token [
+ catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb4,
+ catch(#cir.global_view<@_ZTIi> : !cir.ptr<!u8i>) : ^bb5,
+ catch_all : ^bb6
+ ]
+^bb4(%eh_token.1 : !cir.eh_token):
+ %ct1, %exn1 = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
+ cir.end_catch %ct1 : !cir.catch_token
+ cir.br ^bb7
+^bb5(%eh_token.2 : !cir.eh_token):
+ %ct2, %exn2 = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!s32i>)
+ cir.end_catch %ct2 : !cir.catch_token
+ cir.br ^bb7
+^bb6(%eh_token.3 : !cir.eh_token):
+ %ct3, %exn3 = cir.begin_catch %eh_token.3 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+ cir.end_catch %ct3 : !cir.catch_token
+ cir.br ^bb7
+^bb7:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_multiple_typed_and_catch_all()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
+//
+// CHECK: ^[[NORMAL]]:
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Landing pad with typed catches AND catch-all.
+// CHECK: ^[[UNWIND]]:
+// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception [@_ZTISt9exception, @_ZTIi]
+// CHECK: cir.br ^[[CMP1_BLOCK:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch: branch to comparison chain.
+// CHECK: ^[[CMP1_BLOCK]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.br ^[[CMP1:bb[0-9]+]]
+//
+// First type comparison (exception type).
+// CHECK: ^[[CMP1]](%[[C1_EXN:.*]]: !cir.ptr<!void>, %[[C1_TID:.*]]: !u32i):
+// CHECK: %[[TID1:.*]] = cir.eh.typeid @_ZTISt9exception
+// CHECK: %[[CMP1V:.*]] = cir.cmp(eq, %[[C1_TID]], %[[TID1]])
+// CHECK: cir.brcond %[[CMP1V]] ^[[CATCH_EXN:bb[0-9]+]](%[[C1_EXN]], %[[C1_TID]] : !cir.ptr<!void>, !u32i), ^[[CMP2_BLOCK:bb[0-9]+]](%[[C1_EXN]], %[[C1_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Second type comparison (int type).
+// CHECK: ^[[CMP2_BLOCK]](%[[C2_EXN:.*]]: !cir.ptr<!void>, %[[C2_TID:.*]]: !u32i):
+// CHECK: %[[TID2:.*]] = cir.eh.typeid @_ZTIi
+// CHECK: %[[CMP2:.*]] = cir.cmp(eq, %[[C2_TID]], %[[TID2]])
+// CHECK: cir.brcond %[[CMP2]] ^[[CATCH_INT:bb[0-9]+]](%[[C2_EXN]], %[[C2_TID]] : !cir.ptr<!void>, !u32i), ^[[CATCH_ALL:bb[0-9]+]](%[[C2_EXN]], %[[C2_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Exception catch handler.
+// CHECK: ^[[CATCH_EXN]](%[[CE_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[CE_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// Int catch handler.
+// CHECK: ^[[CATCH_INT]](%[[CI_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[CI_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// Catch-all handler.
+// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: Try-catch with cleanup + catch handler.
+// Two landing pads: one without cleanup (ctor), one with cleanup (doSomething).
+// cleanup ops are removed, cleanup code stays.
+cir.func @test_catch_with_cleanup() {
+ %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
+ cir.try_call @ctor(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+^bb1:
+ cir.try_call @doSomething(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+^bb2:
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.br ^bb8
+^bb3:
+ %1 = cir.eh.initiate : !cir.eh_token
+ cir.br ^bb6(%1 : !cir.eh_token)
+^bb4:
+ %2 = cir.eh.initiate cleanup : !cir.eh_token
+ cir.br ^bb5(%2 : !cir.eh_token)
+^bb5(%eh_token : !cir.eh_token):
+ %3 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.end_cleanup %3 : !cir.cleanup_token
+ cir.br ^bb6(%eh_token : !cir.eh_token)
+^bb6(%eh_token.1 : !cir.eh_token):
+ cir.eh.dispatch %eh_token.1 : !cir.eh_token [
+ catch_all : ^bb7
+ ]
+^bb7(%eh_token.2 : !cir.eh_token):
+ %ct, %exn = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+ cir.end_catch %ct : !cir.catch_token
+ cir.br ^bb8
+^bb8:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_catch_with_cleanup()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+//
+// Entry: ctor call.
+// CHECK: cir.try_call @ctor(%{{.*}}) ^[[AFTER_CTOR:bb[0-9]+]], ^[[CTOR_UNWIND:bb[0-9]+]]
+//
+// After ctor: doSomething call.
+// CHECK: ^[[AFTER_CTOR]]:
+// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL_CLEANUP:bb[0-9]+]], ^[[DO_UNWIND:bb[0-9]+]]
+//
+// Normal cleanup.
+// CHECK: ^[[NORMAL_CLEANUP]]:
+// CHECK: cir.call @dtor
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Ctor unwind: landing pad without cleanup.
+// CHECK: ^[[CTOR_UNWIND]]:
+// CHECK: %[[E1:.*]], %[[T1:.*]] = cir.eh.inflight_exception
+// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[E1]], %[[T1]] : !cir.ptr<!void>, !u32i)
+//
+// doSomething unwind: landing pad with cleanup.
+// CHECK: ^[[DO_UNWIND]]:
+// CHECK: %[[E2:.*]], %[[T2:.*]] = cir.eh.inflight_exception cleanup
+// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[E2]], %[[T2]] : !cir.ptr<!void>, !u32i)
+//
+// EH cleanup: begin_cleanup and end_cleanup are REMOVED, dtor call stays.
+// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
+// CHECK-NOT: cir.begin_cleanup
+// CHECK: cir.call @dtor
+// CHECK-NOT: cir.end_cleanup
+// CHECK: cir.br ^[[DISPATCH]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch: catch-all handler (direct branch).
+// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
+// CHECK: cir.br ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Catch-all handler.
+// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: Try-catch with cleanup + typed catch handler.
+// Like test_catch_with_cleanup but with a typed catch instead of catch-all.
+// Having a typed catch in the dispatch causes both eh.initiate sites to
+// include @_ZTISt9exception in their catch_type_list.
+cir.func @test_typed_catch_with_cleanup() {
+ %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
+ cir.try_call @ctor(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+^bb1:
+ cir.try_call @doSomething(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+^bb2:
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.br ^bb9
+^bb3:
+ %1 = cir.eh.initiate : !cir.eh_token
+ cir.br ^bb6(%1 : !cir.eh_token)
+^bb4:
+ %2 = cir.eh.initiate cleanup : !cir.eh_token
+ cir.br ^bb5(%2 : !cir.eh_token)
+^bb5(%eh_token : !cir.eh_token):
+ %3 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.end_cleanup %3 : !cir.cleanup_token
+ cir.br ^bb6(%eh_token : !cir.eh_token)
+^bb6(%eh_token.1 : !cir.eh_token):
+ cir.eh.dispatch %eh_token.1 : !cir.eh_token [
+ catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb7,
+ unwind : ^bb8
+ ]
+^bb7(%eh_token.2 : !cir.eh_token):
+ %ct, %exn = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
+ cir.end_catch %ct : !cir.catch_token
+ cir.br ^bb9
+^bb8(%eh_token.3 : !cir.eh_token):
+ cir.resume %eh_token.3 : !cir.eh_token
+^bb9:
+ cir.return
+}
+
+// CHECK-LABEL: cir.func @test_typed_catch_with_cleanup()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+//
+// Entry: ctor call.
+// CHECK: cir.try_call @ctor(%{{.*}}) ^[[AFTER_CTOR:bb[0-9]+]], ^[[CTOR_UNWIND:bb[0-9]+]]
+//
+// After ctor: doSomething call.
+// CHECK: ^[[AFTER_CTOR]]:
+// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL_CLEANUP:bb[0-9]+]], ^[[DO_UNWIND:bb[0-9]+]]
+//
+// Normal cleanup.
+// CHECK: ^[[NORMAL_CLEANUP]]:
+// CHECK: cir.call @dtor
+// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
+//
+// Ctor unwind: landing pad without cleanup; typed catch list from shared dispatch.
+// CHECK: ^[[CTOR_UNWIND]]:
+// CHECK: %[[E1:.*]], %[[T1:.*]] = cir.eh.inflight_exception [@_ZTISt9exception]
+// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[E1]], %[[T1]] : !cir.ptr<!void>, !u32i)
+//
+// doSomething unwind: landing pad with cleanup and same typed catch list.
+// CHECK: ^[[DO_UNWIND]]:
+// CHECK: %[[E2:.*]], %[[T2:.*]] = cir.eh.inflight_exception cleanup [@_ZTISt9exception]
+// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[E2]], %[[T2]] : !cir.ptr<!void>, !u32i)
+//
+// EH cleanup: begin_cleanup and end_cleanup are REMOVED, dtor call stays.
+// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
+// CHECK-NOT: cir.begin_cleanup
+// CHECK: cir.call @dtor
+// CHECK-NOT: cir.end_cleanup
+// CHECK: cir.br ^[[DISPATCH]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch: branch to comparison chain.
+// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
+//
+// Comparison: typeid comparison for std::exception, with unwind fallthrough.
+// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
+// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
+// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
+// CHECK: cir.brcond %[[CMP]] ^[[CATCH:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[UNWIND_HANDLER:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Typed catch handler.
+// CHECK: ^[[CATCH]](%[[C_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[C_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+// CHECK: cir.br ^[[RETURN]]
+//
+// Unwind handler: resume.flat.
+// CHECK: ^[[UNWIND_HANDLER]](%[[UW_EXN:.*]]: !cir.ptr<!void>, %[[UW_TID:.*]]: !u32i):
+// CHECK: cir.resume.flat %[[UW_EXN]], %[[UW_TID]]
+//
+// CHECK: ^[[RETURN]]:
+// CHECK: cir.return
+
+// Test: try-catch with a local variable that has a non-trivial destructor.
+// The cleanup path calls ~S() then falls through to the catch-all dispatch.
+// begin_cleanup/end_cleanup are removed; typed catch + catch-all dispatch is lowered.
+cir.func no_inline dso_local @test_catch_with_cleanup_no_ctor() personality(@__gxx_personality_v0) {
+ cir.br ^bb1
+^bb1: // pred: ^bb0
+ %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["s"] {alignment = 1 : i64}
+ cir.br ^bb2
+^bb2: // pred: ^bb1
+ cir.try_call @mayThrow() ^bb3, ^bb4 : () -> ()
+^bb3: // pred: ^bb2
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.br ^bb9
+^bb4: // pred: ^bb2
+ %1 = cir.eh.initiate cleanup : !cir.eh_token
+ cir.br ^bb5(%1 : !cir.eh_token)
+^bb5(%2: !cir.eh_token): // pred: ^bb4
+ %3 = cir.begin_cleanup %2 : !cir.eh_token -> !cir.cleanup_token
+ cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
+ cir.end_cleanup %3 : !cir.cleanup_token
+ cir.br ^bb6(%2 : !cir.eh_token)
+^bb6(%4: !cir.eh_token): // pred: ^bb5
+ cir.eh.dispatch %4 : !cir.eh_token [
+ catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb7,
+ catch_all : ^bb8
+ ]
+^bb7(%6: !cir.eh_token): // pred: ^bb6
+ %catch_token2, %exn_ptr2 = cir.begin_catch %6 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
+ cir.end_catch %catch_token2 : !cir.catch_token
+ cir.br ^bb9
+^bb8(%5: !cir.eh_token): // pred: ^bb6
+ %catch_token, %exn_ptr = cir.begin_catch %5 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+ cir.end_catch %catch_token : !cir.catch_token
+ cir.br ^bb9
+^bb9: // preds: ^bb3, ^bb7, ^bb8
+ cir.return
+}
+
+// CHECK-LABEL: cir.func {{.*}} @test_catch_with_cleanup_no_ctor()
+// CHECK-SAME: personality(@__gxx_personality_v0)
+// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
+//
+// Normal path: destructor called on success.
+// CHECK: ^[[NORMAL]]:
+// CHECK: cir.call @dtor
+//
+// Unwind: initiate cleanup → inflight_exception cleanup with std::exception type.
+// CHECK: ^[[UNWIND]]:
+// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception cleanup [@_ZTISt9exception]
+// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
+//
+// Cleanup: begin_cleanup/end_cleanup removed, destructor call stays.
+// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
+// CHECK-NOT: cir.begin_cleanup
+// CHECK: cir.call @dtor
+// CHECK-NOT: cir.end_cleanup
+// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Dispatch: branch to comparison chain.
+// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
+//
+// Comparison: typeid comparison for std::exception, with catch-all fallthrough.
+// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
+// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
+// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
+// CHECK: cir.brcond %[[CMP]] ^[[TYPED:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
+//
+// Typed catch for std::exception (^bb7 in source).
+// CHECK: ^[[TYPED]](%[[T_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[T_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+//
+// Catch-all handler (^bb8 in source).
+// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
+// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
+// CHECK: cir.call @__cxa_end_catch()
+
+// Verify that runtime function declarations are added.
+// CHECK: cir.func private @__gxx_personality_v0(...)
+// CHECK: cir.func private @__cxa_begin_catch(!cir.ptr<!void>)
+// CHECK: cir.func private @__cxa_end_catch()
+
+cir.func private @mayThrow()
+cir.func private @ctor(!cir.ptr<!rec_SomeClass>)
+cir.func private @dtor(!cir.ptr<!rec_SomeClass>) attributes {nothrow}
+cir.func private @doSomething(!cir.ptr<!rec_SomeClass>)
+cir.global "private" constant external @_ZTISt9exception : !cir.ptr<!u8i>
+cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
+
+} // end module
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index edadfeec09a2a..83832a100debb 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -55,6 +55,10 @@ int main(int argc, char **argv) {
return mlir::createCIRFlattenCFGPass();
});
+ ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
+ return mlir::createCIREHABILoweringPass();
+ });
+
::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
return mlir::createHoistAllocasPass();
});
More information about the cfe-commits
mailing list