[clang] [CIR][docs] Migrate existing Markdown documents to reStructuredText format (PR #192066)

Sirui Mu via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 14 07:16:56 PDT 2026


https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/192066

This patch migrates the existing ClangIR documents that are written in Markdown format to reStructuredText format to align CIR's documents with clang's documentation policy.

Closes #191850 .

>From b04c61e1bca4bcaacbdcd30613020f709d9f6a8f Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Tue, 14 Apr 2026 22:12:55 +0800
Subject: [PATCH] [CIR][docs] Migrate existing Markdown documents to
 reStructuredText format

This patch migrates the existing ClangIR documents that are written in Markdown
format to reStructuredText format to align CIR's documents with clang's
documentation policy.

Closes #191850 .
---
 clang/docs/CIR/ABILowering.md         |  556 ---------
 clang/docs/CIR/ABILowering.rst        |  589 +++++++++
 clang/docs/CIR/CleanupAndEHDesign.md  | 1587 ------------------------
 clang/docs/CIR/CleanupAndEHDesign.rst | 1631 +++++++++++++++++++++++++
 4 files changed, 2220 insertions(+), 2143 deletions(-)
 delete mode 100644 clang/docs/CIR/ABILowering.md
 create mode 100644 clang/docs/CIR/ABILowering.rst
 delete mode 100644 clang/docs/CIR/CleanupAndEHDesign.md
 create mode 100644 clang/docs/CIR/CleanupAndEHDesign.rst

diff --git a/clang/docs/CIR/ABILowering.md b/clang/docs/CIR/ABILowering.md
deleted file mode 100644
index bcc29dc8544ca..0000000000000
--- a/clang/docs/CIR/ABILowering.md
+++ /dev/null
@@ -1,556 +0,0 @@
-# ClangIR ABI Lowering - Design Document
-
-## 1. Introduction
-
-This design describes calling convention lowering that builds on the LLVM ABI
-Lowering Library in `llvm/lib/ABI/`: we use its `abi::Type*` and target ABI
-logic and add an MLIR integration layer (ABITypeMapper, ABI lowering pass, and
-dialect rewriters).  The framework relies on the LLVM ABI library as the single
-source of truth for ABI classification.  MLIR dialects use it via an adapter
-layer.  The design provides a way to perform ABI-compliant calling convention
-lowering that can be used by any MLIR dialect that implements the necessary
-interfaces.  Inputs are high-level function signatures in CIR, FIR, or other
-MLIR dialect.  Outputs are ABI-lowered signatures and call sites.  Lowering
-runs as an MLIR pass in the compilation pipeline.
-
-### 1.1 Design Goals
-
-Building on the LLVM ABI library and adding an MLIR integration layer avoids
-duplicating complex ABI logic across MLIR dialects, reduces maintenance, and
-keeps a single source of ABI compliance in `llvm/lib/ABI/`.  The separation
-between the ABI library (classification) and dialect-specific ABIRewriteContext
-(rewriting) enables clearer testing and a straightforward migration path from
-the CIR incubator by porting useful algorithms into the ABI library where
-appropriate.
-
-A central goal is that generated code be call-compatible with Classic Clang
-CodeGen and other compilers.  Parity is with Classic Clang CodeGen output,
-not only with the incubator.  Success means CIR correctly lowers x86_64 and
-AArch64 calling conventions with full ABI compliance using the LLVM ABI library
-and MLIR integration layer; FIR can adopt the same infrastructure with minimal
-dialect-specific adaptation (e.g.  cdecl when calling C from Fortran).  ABI
-compliance will be validated through differential testing against Classic Clang
-CodeGen, and performance overhead should remain under 5% compared to a direct,
-dialect-specific implementation.  Initial scope focuses on fixed-argument
-functions; variadic support (varargs) is deferred.
-
-## 2. Background and Context
-
-### 2.1 What is Calling Convention Lowering?
-
-Calling convention lowering transforms high-level function signatures to match
-target ABI (Application Binary Interface) requirements.  When a function is
-declared at the source level with convenient, language-level types, these types
-must be translated into the specific register assignments, memory layouts, and
-calling sequences that the target architecture expects.  For example, on x86_64
-System V ABI, a struct containing two 64-bit integers might be "expanded" into
-two separate arguments passed in registers, rather than being passed as a single
-aggregate:
-
-```
-// High-level CIR
-func @foo(i32, struct<i64, i64>) -> i32
-
-// After ABI lowering
-func @foo(i32 %arg0, i64 %arg1, i64 %arg2) -> i32
-//        ^       ^            ^        ^
-//        |       |            +--------+ struct expanded into fields
-//        |       +---- first field passed in register
-//        +---- small integer passed in register
-```
-
-Calling convention lowering is complex for several reasons: it is highly
-target-specific (each architecture has different rules for registers vs.
-memory), type-dependent (rules differ for integers, floats, structs, unions,
-arrays), and context-sensitive (varargs, virtual calls, conventions like
-vectorcall or preserve_most).  The same target may have multiple ABI variants
-(e.g.  x86_64 System V vs.  Windows x64), adding further complexity.
-
-### 2.2 Existing Implementations
-
-#### Classic Clang CodeGen
-
-Classic Clang CodeGen (located in `clang/lib/CodeGen/`) transforms calling
-conventions during the AST-to-LLVM-IR lowering process.  This implementation is
-mature and well-tested, handling all supported targets with comprehensive ABI
-coverage.  However, it's tightly coupled to both Clang's AST representation and
-LLVM IR, making it difficult to reuse for MLIR-based frontends.
-
-#### CIR Incubator
-
-The CIR incubator includes a calling convention lowering pass in
-`clang/lib/CIR/Dialect/Transforms/TargetLowering/` that transforms CIR
-operations into ABI-lowered CIR operations as an MLIR pass.  This implementation
-successfully adapted logic from Classic Clang CodeGen to work within the MLIR
-framework.  However, it relies on CIR-specific types and operations, preventing
-reuse by other MLIR dialects.
-
-#### LLVM ABI Lowering Library
-
-A 2025 Google Summer of Code project produced [PR
-#140112](https://github.com/llvm/llvm-project/pull/140112), which proposes
-extracting Clang's ABI logic into a reusable library in `llvm/lib/ABI/`.  The
-design centers on a shadow type system (`abi::Type*`) separate from both Clang's
-AST types and LLVM IR types, enabling the ABI classification algorithms to work
-independently of any specific frontend representation.  The library includes
-abstract `ABIInfo` base classes and target-specific implementations (e.g.
-x86_64, BPF) and provides QualTypeMapper for Clang to map `QualType` to
-`abi::Type*`.
-
-Our approach is to complete and extend this library and use it as the single
-source of truth for ABI classification.  One implementation in one place reduces
-duplication, simplifies bug fixes, and creates a path for Classic Clang CodeGen
-to use the same logic in the future.  MLIR dialects (CIR, FIR, and others) will
-use the library via an adapter layer rather than reimplementing ABI logic.
-
-**Current state.** The x86_64 implementation is largely complete and under
-review.  AArch64 and some other targets are not yet implemented; there is no
-MLIR integration today.  The work is being upstreamed in smaller parts (e.g.
-[PR 158329](https://github.com/llvm/llvm-project/pull/158329)); progress is
-limited by reviewer bandwidth.  The overhead of the shadow type system
-(converting to and from `abi::Type*`) has been measured at under 0.1% for clang
--O0, so it is negligible for CIR.  Our approach therefore depends on the ABI
-library being merged upstream or our contributions to it being accepted.
-
-**Our approach.** The approach is to complete and extend the ABI library (e.g.
-AArch64, review feedback, tests) and add an **MLIR integration layer** so that
-MLIR dialects can use it:
-
-- **ABITypeMapper**: maps `mlir::Type` to `abi::Type*`, analogous to
-  QualTypeMapper for Clang.
-
-- **MLIR ABI lowering pass**: uses the library's `ABIInfo` for classification,
-  then performs dialect-specific rewriting via `ABIRewriteContext` for CIR, FIR,
-  and other dialects.
-
-The CIR incubator serves as a **reference only** (e.g. for AArch64 algorithms).
-We do not upstream the incubator's CIR-specific ABI implementation as the
-long-term solution; we port useful algorithms into the ABI library where
-appropriate.
-
-### 2.3 Requirements for MLIR Dialects
-
-CIR needs to lower C/C++ calling conventions correctly, with initial support for
-x86_64 and AArch64 targets.  It must handle structs, unions, and complex types,
-as well as support instance methods and virtual calls.  FIR's initial need is
-**cdecl for calling C from Fortran** (C interop); that is in scope.
-Fortran-specific ABI semantics (e.g.  CHARACTER hidden length parameters, array
-descriptors) are out of initial scope; full Fortran ABI lowering is a broader
-goal.  Both dialects share common requirements: strict target ABI compliance,
-efficient lowering with minimal overhead, extensibility for adding new target
-architectures, and comprehensive testability and validation capabilities.
-
-## 3. Proposed Solution
-
-**Core.** The LLVM ABI library in `llvm/lib/ABI/` performs ABI classification on
-`abi::Type*`.  It provides `ABIInfo` and target-specific implementations
-(x86_64, BPF, and eventually AArch64 and others).  This is the single place
-where ABI rules are implemented.
-
-**MLIR side.** To use this library from MLIR dialects we add an integration
-layer: (1) **ABITypeMapper** maps `mlir::Type` to `abi::Type*` (analogous to
-QualTypeMapper for Clang).  (2) A **generic ABI lowering pass** invokes the
-library's `ABIInfo` for classification, then (3) performs **dialect-specific
-rewriting** via the `ABIRewriteContext` interface—each dialect (CIR, FIR, etc.)
-implements only the glue to create its own operations (e.g. `cir.call`,
-`fir.call`).  Classification logic is shared; operation creation is
-dialect-specific.
-
-The following diagram shows the layering.  At the top, the ABI library holds
-the ABI logic.  In the middle, adapters connect frontends to it: Classic Clang
-CodeGen uses QualTypeMapper; MLIR uses ABITypeMapper and the ABI lowering pass.
-At the bottom, each dialect implements `ABIRewriteContext` only; FIR is shown as
-a consumer for cdecl/C interop (e.g. calling C from Fortran).
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│  LLVM ABI Library (llvm/lib/ABI/)                               │
-│  ABIInfo, abi::Type*, target implementations (X86, AArch64,…)   │
-└─────────────────────────────────────────────────────────────────┘
-                              │
-            ┌─────────────────┴─────────────────┐
-            │                                   │
-            ▼                                   ▼
-┌───────────────────────┐         ┌───────────────────────────────┐
-│  Classic CodeGen      │         │  MLIR adapter                 │
-│  QualTypeMapper       │         │  ABITypeMapper + ABI pass     │
-└───────────────────────┘         └───────────────────────────────┘
-                                                │
-                               ┌────────────────┼────────────────┐
-                               │                │                │
-                               ▼                ▼                ▼
-                         ┌────────────┐   ┌────────────┐   ┌────────────┐
-                         │ CIR        │   │ FIR        │   │ Future     │
-                         │ ABIRewrite │   │ (cdecl/C   │   │ Dialects   │
-                         │ Context    │   │  interop)  │   │            │
-                         └────────────┘   └────────────┘   └────────────┘
-```
-
-## 4. Design Overview
-
-### 4.1 Architecture Diagram
-
-The following diagram shows how the design builds on the ABI library (Section
-3).  At the top, the ABI library holds the classification logic.  The middle
-layer adapts MLIR to the ABI library: ABITypeMapper converts `mlir::Type` to
-`abi::Type*`, and the MLIR ABI lowering pass invokes the library's `ABIInfo` and
-uses the classification
-to drive rewriting.  At the bottom, each dialect implements only
-`ABIRewriteContext` for operation creation; there is no separate type
-abstraction layer in MLIR for classification—that lives in the ABI library.
-
-```
-┌─────────────────────────────────────────────────────────────────────────┐
-│  LLVM ABI Library (llvm/lib/ABI/) — single source of truth              │
-│  abi::Type*, ABIInfo, target implementations (X86_64, AArch64, …)       │
-│  Input: abi::Type*  →  Output: classification (ABIArgInfo, etc.)        │
-└─────────────────────────────────────────────────────────────────────────┘
-                                      │
-                                      ▼
-┌─────────────────────────────────────────────────────────────────────────┐
-│  MLIR adapter                                                           │
-│  ABITypeMapper (mlir::Type → abi::Type*)  +  MLIR ABI lowering pass     │
-│  (1) Map types  (2) Call ABIInfo  (3) Drive rewriting from              │
-│  classification result                                                  │
-└─────────────────────────────────────────────────────────────────────────┘
-                                      │
-                    ┌─────────────────┼─────────────────┐
-                    ▼                 ▼                 ▼
-              ┌────────────┐    ┌────────────┐    ┌────────────┐
-              │ CIR        │    │ FIR        │    │ Future     │
-              │ ABIRewrite │    │ ABIRewrite │    │ Dialects   │
-              │ Context    │    │ Context    │    │            │
-              └────────────┘    └────────────┘    └────────────┘
-              Dialect-specific operation creation only (no type
-              abstraction for classification in MLIR)
-```
-
-### 4.2 ABI Library, Adapter, and Dialect Layers
-
-The architecture has three parts.  **The ABI library** (`llvm/lib/ABI/`) is the
-single source of truth for ABI classification: it operates on `abi::Type*` and
-produces classification results (e.g.  ABIArgInfo, ABIFunctionInfo).
-Target-specific `ABIInfo` implementations (X86_64, AArch64, etc.) live there.
-The **adapter layer** is MLIR-specific: ABITypeMapper maps `mlir::Type` to
-`abi::Type*`, and the MLIR ABI lowering pass (1) maps types, (2) calls the
-library's ABIInfo, and (3) uses the classification to drive rewriting.  The
-**dialect layer** is only ABIRewriteContext: each dialect (CIR, FIR) implements
-operation creation (createFunction, createCall, createExtractValue, etc.).
-There is no type abstraction layer in MLIR for classification; type queries for
-ABI are performed on `abi::Type*` inside the ABI library.
-
-### 4.3 Key Components
-
-The framework is built from the following components.  **The ABI library**
-(`llvm/lib/ABI/`) provides the single source of truth for ABI classification:
-the `abi::Type*` type system, the `ABIInfo` base and target-specific
-implementations (e.g.  X86_64, AArch64), and the classification result types
-(e.g.  ABIArgInfo, ABIFunctionInfo).  **ABITypeMapper** maps `mlir::Type` to
-`abi::Type*` so that MLIR dialect types can be classified by the ABI library.
-The generic mapper relies on existing MLIR type interfaces (e.g.
-`DataLayoutTypeInterface`) for size and alignment, and pattern-matches on
-standard type categories (integers, floats, pointers, structs, arrays,
-vectors) to build `abi::Type*`.  Dialects whose types do not conform to
-standard MLIR type categories (e.g.  CIR's `cir::IntType` is not
-`mlir::IntegerType`) may need dialect-aware mapping alongside the generic
-mapper to preserve semantics such as signedness, pointer identity, and
-record field structure.
-The **MLIR ABI lowering pass** orchestrates the flow: it uses ABITypeMapper,
-calls the library's ABIInfo, and drives rewriting from the classification
-result.  **ABIRewriteContext** is the dialect-specific interface for operation
-creation (each dialect implements it to produce e.g.  cir.call, fir.call).  A
-**target registry** (or equivalent) is used to select the appropriate ABIInfo
-for the compilation target.  There is no ABITypeInterface or separate "ABIInfo
-in MLIR"; classification lives entirely in the ABI library.
-
-### 4.4 ABI Lowering Flow: How the Pieces Fit Together
-
-This section describes the end-to-end flow of ABI lowering, showing how all
-interfaces and components work together.
-
-#### Step 1: Function Signature Analysis
-
-The ABI lowering pass begins by analyzing the function signature.  Function
-operations are identified via MLIR's `FunctionOpInterface`, which provides
-access to the function type, argument types, and return types.  The pass
-extracts the parameter types and return type to prepare them for
-classification.  At this stage, the types are still in their
-high-level, dialect-specific form (e.g., `!cir.struct` for CIR, or `!fir.type`
-for FIR).  The pass collects these types into a list that will be fed to the
-classification logic in the next step.
-
-```
-Input: func @foo(%arg0: !cir.int<u, 32>,
-       %arg1: !cir.struct<{!cir.int<u, 64>,
-                            !cir.int<u, 64>}>) -> !cir.int<u, 32>
-```
-
-#### Step 2: Type Mapping via ABITypeMapper
-
-For each argument and the return type, the pass maps `mlir::Type` to
-`abi::Type*` using ABITypeMapper.  The mapper produces the representation that
-the library's ABIInfo expects; optionally, it can map back to MLIR types for coercion
-types when needed.
-
-```cpp
-// Map dialect types to the library's type system
-ABITypeMapper abiTypeMapper(module.getDataLayout());
-abi::Type *arg0Abi = abiTypeMapper.map(arg0Type);   // i32 -> IntegerType
-abi::Type *arg1Abi = abiTypeMapper.map(arg1Type);   // struct -> RecordType
-abi::Type *retAbi = abiTypeMapper.map(returnType);
-```
-
-**Key Point**: Classification runs in the ABI library on `abi::Type*`; ABITypeMapper is
-the only bridge from dialect types to that representation.
-
-#### Step 3: ABI Classification
-
-The library's target-specific `ABIInfo` (e.g.  X86_64) performs classification on
-`abi::Type*` and produces the library's classification result (e.g.  ABIFunctionInfo
-and ABIArgInfo as defined in `llvm/lib/ABI/`):
-
-```cpp
-// The MLIR ABI lowering pass obtains the ABIInfo from the target
-// registry based on the module's target triple (see Section 5.2).
-llvm::abi::ABIInfo *abiInfo = getABIInfo();  // e.g. X86_64
-llvm::abi::ABIFunctionInfo abiFI;
-abiInfo->computeInfo(abiFI, arg0Abi, arg1Abi, retAbi);
-// For struct<i64,i64> on x86_64: produces Expand (two i64 args)
-```
-
-Output: the library's classification (e.g.  ABIFunctionInfo) for all arguments and
-return:
-- `%arg0 (i32)` → Direct (pass as-is)
-- `%arg1 (struct)` → Expand (split into two i64 fields)
-- Return type → Direct
-
-#### Step 4: Function Signature Rewriting
-
-After the library's classification is complete, the pass rewrites the function to match
-the ABI requirements using the dialect's `ABIRewriteContext`.  The
-classification result (from the ABI library) describes the lowered signature; the rewrite
-context creates the actual dialect operations.  For example, if a struct is
-classified as "Expand", the new function signature will have multiple scalar
-parameters instead of the single struct parameter.
-
-```cpp
-ABIRewriteContext &ctx = getDialectRewriteContext();
-
-// Create new function with lowered signature
-FunctionType newType = ...; // (i32, i64, i64) -> i32
-Operation *newFunc = ctx.createFunction(loc, "foo", newType);
-```
-
-**Key Point**: The original function had signature `(i32, struct) -> i32`, but
-the ABI-lowered function has signature `(i32, i64, i64) -> i32` with the struct
-expanded into its constituent fields.
-
-#### Step 5: Argument Expansion
-
-With the function signature rewritten, the pass updates all call sites to match
-the new signature, using the classification from the ABI library to drive rewriting via
-`ABIRewriteContext`.  For arguments classified as "Expand", the pass breaks down
-the aggregate into its constituent parts (e.g.  struct into two i64 values).
-The rewrite context provides operations to extract fields and construct the new
-call with the expanded argument list.
-
-```cpp
-// Original call: call @foo(%val0, %structVal)
-// Need to extract struct fields:
-
-Value field0 = ctx.createExtractValue(loc, structVal, {0}); // extract 1st i64
-Value field1 = ctx.createExtractValue(loc, structVal, {1}); // extract 2nd i64
-
-// New call with expanded arguments
-ctx.createCall(loc, newFunc, {resultType}, {val0, field0, field1});
-```
-
-**Key Point**: `ABIRewriteContext` abstracts the dialect-specific operation
-creation, so the lowering logic doesn't need to know about CIR operations.
-
-#### Step 6: Return Value Handling
-
-For functions returning large structs (indirect return):
-
-```cpp
-// If return type is classified as Indirect:
-Value sretPtr = ctx.createAlloca(loc, retType, alignment);
-ctx.createCall(loc, func, {}, {sretPtr, ...otherArgs});
-Value result = ctx.createLoad(loc, sretPtr);
-```
-
-#### Complete Flow Diagram
-
-The diagram below combines the three-layer architecture (Section
-4.1) with the step-by-step flow, showing which layer owns each
-step.
-
-```
-   ┌─────────────────────────────────────────────────────────┐
-   │ Input: High-Level Function (CIR/FIR/other dialect)      │
-   │   func @foo(%arg0: i32, %arg1: struct<i64,i64>) -> i32  │
-   └──────────────────────────┬──────────────────────────────┘
-                              │
-  ╔═══════════════════════════╪═══════════════════════════════╗
-  ║  MLIR Adapter Layer       │                               ║
-  ║                           ▼                               ║
-  ║  Step 1: Extract types from FunctionOpInterface           ║
-  ║            arg0: mlir::Type, arg1: mlir::Type, ret: …     ║
-  ║                           │                               ║
-  ║                           ▼                               ║
-  ║  Step 2: ABITypeMapper    │                               ║
-  ║            mlir::Type ──> abi::Type*                      ║
-  ║            (uses DataLayoutTypeInterface for size/align)  ║
-  ╚═══════════════════════════╪═══════════════════════════════╝
-                              │
-  ╔═══════════════════════════╪═══════════════════════════════╗
-  ║  LLVM ABI Library         │  (llvm/lib/ABI/)              ║
-  ║                           ▼                               ║
-  ║  Step 3: ABIInfo::computeInfo() on abi::Type*             ║
-  ║            Applies target rules (e.g. x86_64 System V)    ║
-  ║            Produces: ABIArgInfo per arg/return            ║
-  ║              arg0 (i32)   → Direct                        ║
-  ║              arg1 (struct)→ Expand (two i64 fields)       ║
-  ║              return (i32) → Direct                        ║
-  ╚═══════════════════════════╪═══════════════════════════════╝
-                              │
-  ╔═══════════════════════════╪═══════════════════════════════╗
-  ║  Dialect-Specific Layer   │  (ABIRewriteContext)          ║
-  ║                           ▼                               ║
-  ║  Step 4: Rewrite function signature                       ║
-  ║            (i32, struct) -> i32                           ║
-  ║            becomes (i32, i64, i64) -> i32                 ║
-  ║                           │                               ║
-  ║                           ▼                               ║
-  ║  Step 5: Rewrite call sites                               ║
-  ║            createExtractValue() to expand struct args     ║
-  ║            createCall() with lowered arg list             ║
-  ║                           │                               ║
-  ║                           ▼                               ║
-  ║  Step 6: Handle return values                             ║
-  ║            Indirect: createAlloca + sret pointer          ║
-  ║            Coerced: memory-based reinterpretation         ║
-  ╚═══════════════════════════╪═══════════════════════════════╝
-                              │
-                              ▼
-   ┌─────────────────────────────────────────────────────────┐
-   │ Output: ABI-Lowered Function                            │
-   │   func @foo(%arg0: i32, %arg1: i64, %arg2: i64) -> i32  │
-   └─────────────────────────────────────────────────────────┘
-```
-
-#### Key Interactions Between Components
-
-Classification lives in the ABI library: `ABIInfo` operates on `abi::Type*` and produces
-classification results (e.g.  ABIArgInfo, ABIFunctionInfo).  MLIR types reach
-the ABI library only via ABITypeMapper, which converts `mlir::Type` to `abi::Type*`.  The
-lowering pass (1) maps types with ABITypeMapper, (2) calls the library's ABIInfo to
-get classification, and (3) uses that result to drive rewriting through the
-dialect's ABIRewriteContext.
-
-ABIRewriteContext consumes the classification (e.g.  "Expand" for a struct) and
-performs the actual IR changes: createFunction with the lowered signature,
-createExtractValue and createCall at call sites.  Each dialect implements
-ABIRewriteContext to produce its own operations (e.g.  cir.call, fir.call).
-This keeps classification in one place (the ABI library) and limits dialect code to
-operation creation.
-
-## 5. ABIRewriteContext and Target Registry
-
-### 5.1 ABIRewriteContext Interface
-
-ABIRewriteContext is the only dialect-specific layer: CIR and FIR each
-implement it to create their own dialect operations (e.g.  cir.call, fir.call).
-In a module with mixed dialect content, the pass selects the appropriate
-ABIRewriteContext for each function based on the dialect of its operations.  Classification is
-performed by the library's ABIInfo and produces the library's result (e.g.  ABIFunctionInfo,
-ABIArgInfo); ABIRewriteContext consumes that classification to perform the
-actual IR rewriting.  ABIRewriteContext is also responsible for updating
-ABI-related attributes (e.g.  sret, byval, signext, zeroext, inreg) on the
-rewritten function signatures and call sites as indicated by the classification
-result.
-
-The interface defines two high-level methods:
-`rewriteFunctionDefinition(funcOp, classification, builder)` rewrites a
-function's signature and body (coercing return values, adapting arguments,
-handling sret), and `rewriteCallSite(callOp, classification, builder)` rewrites
-a call to match the lowered callee (coercing arguments, handling coerced
-returns).  Each method encapsulates the full rewriting logic for its scope,
-using the dialect's own builder operations internally (e.g.  `cir::CastOp`,
-`cir::AllocaOp`, `cir::StoreOp`).  Each dialect handles operation creation
-using its own builder internally.
-
-Each dialect implementing ABI lowering must provide a concrete
-`ABIRewriteContext` subclass.  This is a significant but one-time cost:
-CIR implements `CIRABIRewriteContext`, FIR implements `FIRABIRewriteContext`,
-and any future dialect reuses the shared classification infrastructure by
-providing its own context implementation.  The alternative—reimplementing the
-entire ABI classification logic per dialect—would require 8,000-15,000 lines per
-dialect (the combined size of x86_64 and AArch64 classification code plus all
-supporting infrastructure), introduce divergent behavior across dialects, and
-create a maintenance burden where ABI bug fixes must be propagated to every
-dialect independently.
-
-### 5.2 Target Registry
-
-We use the library's target selection or registry to obtain the appropriate ABIInfo for
-the compilation target (e.g.  X86_64, AArch64).  We do not introduce a separate
-MLIR TargetRegistry unless the MLIR ABI pass needs it for pass options or
-configuration.  The dependency direction is: the MLIR ABI pass depends on
-`llvm/lib/ABI`; there is no reverse dependency from the ABI library to MLIR dialects.
-
-## 6. Open Questions
-
-The following items are open for discussion.  This section may be revised,
-shortened, or removed before final merge.
-
-### 6.1 How to Handle clang::TargetInfo Dependency in MLIR?
-
-The CIR incubator currently uses `clang::TargetInfo` to query target-specific
-properties needed for ABI decisions, such as pointer width, alignment,
-endianness, and calling convention availability.  Moving this functionality to
-MLIR dialect-agnostic infrastructure raises an architectural question: should
-MLIR code depend on a Clang library, or should it use MLIR-based mechanisms?
-
-Three approaches are under consideration.
-
-1.  Continue using `clang::TargetInfo` directly, accepting an MLIR→Clang
-   dependency for this target-specific infrastructure.  This approach requires
-   no additional implementation since it already works in the CIR incubator,
-   and `clang::TargetInfo` provides comprehensive, battle-tested coverage of
-   all target properties.  However, it creates a dependency relationship that
-   may violate MLIR's architectural principle of being a peer to Clang rather
-   than dependent on it.
-
-2.  Combine `llvm::Triple` with MLIR's `DataLayoutInterface`, supplemented by
-   module-level attributes for ABI-specific properties not covered by the data
-   layout.  This approach maintains clean layering with no Clang dependency and
-   follows MLIR patterns, but requires defining approximately 10-15 additional
-   attributes and some upfront design work.
-
-3.  Create a new `mlir::target::TargetInfo` abstraction with minimal methods
-   tailored specifically for ABI needs (approximately 15-20 methods).  This
-   provides clean layering without Clang dependency but requires implementing
-   and maintaining target-specific code that duplicates some knowledge from
-   `clang::TargetInfo`.
-
-Option 2 is recommended as the preferred approach.  It maintains MLIR's
-independence from Clang, which is important for MLIR's mission to be reusable by
-non-Clang frontends like Rust, Julia, and Swift.  Target information is input
-metadata rather than an output format, so it should be expressible through
-MLIR's existing mechanisms rather than requiring external dependencies.  Option
-3 serves as an acceptable fallback if Option 2 proves insufficient during
-prototyping, while Option 1 is not recommended due to the architectural concerns
-around MLIR depending on Clang.
-
-### 6.2 Scope: C Calling Convention vs.  Arbitrary Calling Conventions
-
-This design focuses on the **C calling convention layer** (e.g. cdecl, System V,
-AAPCS).  C++ ABI concerns such as non-trivial copy constructors or destructors
-are largely handled elsewhere in the compilation pipeline; the ABI library and
-MLIR integration layer address how arguments and return values are passed at the
-C ABI boundary.  An open question is whether the design should remain explicitly
-scoped to C calling conventions only, or be general enough to support arbitrary
-calling conventions (e.g. vectorcall, preserve_most) via extensible interfaces.
-Clarifying this scope will guide the design of the LLVM ABI library integration
-and the MLIR pass.
diff --git a/clang/docs/CIR/ABILowering.rst b/clang/docs/CIR/ABILowering.rst
new file mode 100644
index 0000000000000..59f0bb4f9a646
--- /dev/null
+++ b/clang/docs/CIR/ABILowering.rst
@@ -0,0 +1,589 @@
+====================================
+ClangIR ABI Lowering Design Document
+====================================
+
+.. contents::
+   :local:
+
+Introduction
+============
+
+This design describes calling convention lowering that builds on the LLVM ABI
+Lowering Library in ``llvm/lib/ABI/``: we use its ``abi::Type*`` and target ABI
+logic and add an MLIR integration layer (ABITypeMapper, ABI lowering pass, and
+dialect rewriters).  The framework relies on the LLVM ABI library as the single
+source of truth for ABI classification.  MLIR dialects use it via an adapter
+layer.  The design provides a way to perform ABI-compliant calling convention
+lowering that can be used by any MLIR dialect that implements the necessary
+interfaces.  Inputs are high-level function signatures in CIR, FIR, or other
+MLIR dialect.  Outputs are ABI-lowered signatures and call sites.  Lowering
+runs as an MLIR pass in the compilation pipeline.
+
+Design Goals
+------------
+
+Building on the LLVM ABI library and adding an MLIR integration layer avoids
+duplicating complex ABI logic across MLIR dialects, reduces maintenance, and
+keeps a single source of ABI compliance in ``llvm/lib/ABI/``.  The separation
+between the ABI library (classification) and dialect-specific ABIRewriteContext
+(rewriting) enables clearer testing and a straightforward migration path from
+the CIR incubator by porting useful algorithms into the ABI library where
+appropriate.
+
+A central goal is that generated code be call-compatible with Classic Clang
+CodeGen and other compilers.  Parity is with Classic Clang CodeGen output,
+not only with the incubator.  Success means CIR correctly lowers x86_64 and
+AArch64 calling conventions with full ABI compliance using the LLVM ABI library
+and MLIR integration layer; FIR can adopt the same infrastructure with minimal
+dialect-specific adaptation (e.g.  cdecl when calling C from Fortran).  ABI
+compliance will be validated through differential testing against Classic Clang
+CodeGen, and performance overhead should remain under 5% compared to a direct,
+dialect-specific implementation.  Initial scope focuses on fixed-argument
+functions; variadic support (varargs) is deferred.
+
+Background and Context
+======================
+
+What is Calling Convention Lowering?
+------------------------------------
+
+Calling convention lowering transforms high-level function signatures to match
+target ABI (Application Binary Interface) requirements.  When a function is
+declared at the source level with convenient, language-level types, these types
+must be translated into the specific register assignments, memory layouts, and
+calling sequences that the target architecture expects.  For example, on x86_64
+System V ABI, a struct containing two 64-bit integers might be "expanded" into
+two separate arguments passed in registers, rather than being passed as a single
+aggregate:
+
+.. code-block::
+    
+    // High-level CIR
+    func @foo(i32, struct<i64, i64>) -> i32
+
+    // After ABI lowering
+    func @foo(i32 %arg0, i64 %arg1, i64 %arg2) -> i32
+    //        ^       ^            ^        ^
+    //        |       |            +--------+ struct expanded into fields
+    //        |       +---- first field passed in register
+    //        +---- small integer passed in register
+
+Calling convention lowering is complex for several reasons: it is highly
+target-specific (each architecture has different rules for registers vs.
+memory), type-dependent (rules differ for integers, floats, structs, unions,
+arrays), and context-sensitive (varargs, virtual calls, conventions like
+vectorcall or preserve_most).  The same target may have multiple ABI variants
+(e.g.  x86_64 System V vs.  Windows x64), adding further complexity.
+
+Existing Implementations
+------------------------
+
+Classic Clang CodeGen
+^^^^^^^^^^^^^^^^^^^^^
+
+Classic Clang CodeGen (located in ``clang/lib/CodeGen/``) transforms calling
+conventions during the AST-to-LLVM-IR lowering process.  This implementation is
+mature and well-tested, handling all supported targets with comprehensive ABI
+coverage.  However, it's tightly coupled to both Clang's AST representation and
+LLVM IR, making it difficult to reuse for MLIR-based frontends.
+
+CIR Incubator
+^^^^^^^^^^^^^
+
+The CIR incubator includes a calling convention lowering pass in
+``clang/lib/CIR/Dialect/Transforms/TargetLowering/`` that transforms CIR
+operations into ABI-lowered CIR operations as an MLIR pass.  This implementation
+successfully adapted logic from Classic Clang CodeGen to work within the MLIR
+framework.  However, it relies on CIR-specific types and operations, preventing
+reuse by other MLIR dialects.
+
+LLVM ABI Lowering Library
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A 2025 Google Summer of Code project produced `PR
+#140112 <https://github.com/llvm/llvm-project/pull/140112>`__, which proposes
+extracting Clang's ABI logic into a reusable library in ``llvm/lib/ABI/``.  The
+design centers on a shadow type system (``abi::Type*``) separate from both
+Clang's AST types and LLVM IR types, enabling the ABI classification algorithms
+to work independently of any specific frontend representation.  The library
+includes abstract ``ABIInfo`` base classes and target-specific implementations
+(e.g. x86_64, BPF) and provides QualTypeMapper for Clang to map ``QualType`` to
+``abi::Type*``.
+
+Our approach is to complete and extend this library and use it as the single
+source of truth for ABI classification.  One implementation in one place reduces
+duplication, simplifies bug fixes, and creates a path for Classic Clang CodeGen
+to use the same logic in the future.  MLIR dialects (CIR, FIR, and others) will
+use the library via an adapter layer rather than reimplementing ABI logic.
+
+**Current state.** The x86_64 implementation is largely complete and under
+review.  AArch64 and some other targets are not yet implemented; there is no
+MLIR integration today.  The work is being upstreamed in smaller parts (e.g.
+`PR 158329 <https://github.com/llvm/llvm-project/pull/158329>`__); progress is
+limited by reviewer bandwidth.  The overhead of the shadow type system
+(converting to and from ``abi::Type*``) has been measured at under 0.1% for clang
+-O0, so it is negligible for CIR.  Our approach therefore depends on the ABI
+library being merged upstream or our contributions to it being accepted.
+
+**Our approach.** The approach is to complete and extend the ABI library (e.g.
+AArch64, review feedback, tests) and add an **MLIR integration layer** so that
+MLIR dialects can use it:
+
+* **ABITypeMapper**: maps ``mlir::Type`` to ``abi::Type*``, analogous to
+  QualTypeMapper for Clang.
+* **MLIR ABI lowering pass**: uses the library's ``ABIInfo`` for classification,
+  then performs dialect-specific rewriting via ``ABIRewriteContext`` for CIR,
+  FIR, and other dialects.
+
+The CIR incubator serves as a **reference only** (e.g. for AArch64 algorithms).
+We do not upstream the incubator's CIR-specific ABI implementation as the
+long-term solution; we port useful algorithms into the ABI library where
+appropriate.
+
+Requirements for MLIR Dialects
+------------------------------
+
+CIR needs to lower C/C++ calling conventions correctly, with initial support for
+x86_64 and AArch64 targets.  It must handle structs, unions, and complex types,
+as well as support instance methods and virtual calls.  FIR's initial need is
+**cdecl for calling C from Fortran** (C interop); that is in scope.
+Fortran-specific ABI semantics (e.g.  CHARACTER hidden length parameters, array
+descriptors) are out of initial scope; full Fortran ABI lowering is a broader
+goal.  Both dialects share common requirements: strict target ABI compliance,
+efficient lowering with minimal overhead, extensibility for adding new target
+architectures, and comprehensive testability and validation capabilities.
+
+Proposed Solution
+=================
+
+**Core.** The LLVM ABI library in ``llvm/lib/ABI/`` performs ABI classification
+on ``abi::Type*``.  It provides ``ABIInfo`` and target-specific implementations
+(x86_64, BPF, and eventually AArch64 and others).  This is the single place
+where ABI rules are implemented.
+
+**MLIR side.** To use this library from MLIR dialects we add an integration
+layer: (1) **ABITypeMapper** maps ``mlir::Type`` to ``abi::Type*`` (analogous to
+QualTypeMapper for Clang).  (2) A **generic ABI lowering pass** invokes the
+library's ``ABIInfo`` for classification, then (3) performs **dialect-specific
+rewriting** via the ``ABIRewriteContext`` interface—each dialect (CIR, FIR,
+etc.) implements only the glue to create its own operations (e.g. ``cir.call``,
+``fir.call``).  Classification logic is shared; operation creation is
+dialect-specific.
+
+The following diagram shows the layering.  At the top, the ABI library holds
+the ABI logic.  In the middle, adapters connect frontends to it: Classic Clang
+CodeGen uses QualTypeMapper; MLIR uses ABITypeMapper and the ABI lowering pass.
+At the bottom, each dialect implements ``ABIRewriteContext`` only; FIR is shown
+as a consumer for cdecl/C interop (e.g. calling C from Fortran).
+
+.. code-block::
+
+    ┌─────────────────────────────────────────────────────────────────┐
+    │  LLVM ABI Library (llvm/lib/ABI/)                               │
+    │  ABIInfo, abi::Type*, target implementations (X86, AArch64,…)   │
+    └─────────────────────────────────────────────────────────────────┘
+                                  │
+                ┌─────────────────┴─────────────────┐
+                │                                   │
+                ▼                                   ▼
+    ┌───────────────────────┐         ┌───────────────────────────────┐
+    │  Classic CodeGen      │         │  MLIR adapter                 │
+    │  QualTypeMapper       │         │  ABITypeMapper + ABI pass     │
+    └───────────────────────┘         └───────────────────────────────┘
+                                                    │
+                                   ┌────────────────┼────────────────┐
+                                   │                │                │
+                                   ▼                ▼                ▼
+                             ┌────────────┐   ┌────────────┐   ┌────────────┐
+                             │ CIR        │   │ FIR        │   │ Future     │
+                             │ ABIRewrite │   │ (cdecl/C   │   │ Dialects   │
+                             │ Context    │   │  interop)  │   │            │
+                             └────────────┘   └────────────┘   └────────────┘
+
+Design Overview
+===============
+
+Architecture Diagram
+--------------------
+
+The following diagram shows how the design builds on the ABI library (Section
+3).  At the top, the ABI library holds the classification logic.  The middle
+layer adapts MLIR to the ABI library: ABITypeMapper converts ``mlir::Type`` to
+``abi::Type*``, and the MLIR ABI lowering pass invokes the library's ``ABIInfo``
+and uses the classification to drive rewriting.  At the bottom, each dialect
+implements only ``ABIRewriteContext`` for operation creation; there is no
+separate type abstraction layer in MLIR for classification—that lives in the ABI
+library.
+
+.. code-block::
+
+    ┌─────────────────────────────────────────────────────────────────────────┐
+    │  LLVM ABI Library (llvm/lib/ABI/) — single source of truth              │
+    │  abi::Type*, ABIInfo, target implementations (X86_64, AArch64, …)       │
+    │  Input: abi::Type*  →  Output: classification (ABIArgInfo, etc.)        │
+    └─────────────────────────────────────────────────────────────────────────┘
+                                          │
+                                          ▼
+    ┌─────────────────────────────────────────────────────────────────────────┐
+    │  MLIR adapter                                                           │
+    │  ABITypeMapper (mlir::Type → abi::Type*)  +  MLIR ABI lowering pass     │
+    │  (1) Map types  (2) Call ABIInfo  (3) Drive rewriting from              │
+    │  classification result                                                  │
+    └─────────────────────────────────────────────────────────────────────────┘
+                                          │
+                        ┌─────────────────┼─────────────────┐
+                        ▼                 ▼                 ▼
+                  ┌────────────┐    ┌────────────┐    ┌────────────┐
+                  │ CIR        │    │ FIR        │    │ Future     │
+                  │ ABIRewrite │    │ ABIRewrite │    │ Dialects   │
+                  │ Context    │    │ Context    │    │            │
+                  └────────────┘    └────────────┘    └────────────┘
+                  Dialect-specific operation creation only (no type
+                  abstraction for classification in MLIR)
+
+ABI Library, Adapter, and Dialect Layers
+----------------------------------------
+
+The architecture has three parts.  **The ABI library** (``llvm/lib/ABI/``) is
+the single source of truth for ABI classification: it operates on ``abi::Type*``
+and produces classification results (e.g.  ABIArgInfo, ABIFunctionInfo).
+Target-specific ``ABIInfo`` implementations (X86_64, AArch64, etc.) live there.
+The **adapter layer** is MLIR-specific: ABITypeMapper maps ``mlir::Type`` to
+``abi::Type*``, and the MLIR ABI lowering pass (1) maps types, (2) calls the
+library's ABIInfo, and (3) uses the classification to drive rewriting.  The
+**dialect layer** is only ABIRewriteContext: each dialect (CIR, FIR) implements
+operation creation (createFunction, createCall, createExtractValue, etc.).
+There is no type abstraction layer in MLIR for classification; type queries for
+ABI are performed on ``abi::Type*`` inside the ABI library.
+
+Key Components
+--------------
+
+The framework is built from the following components.  **The ABI library**
+(``llvm/lib/ABI/``) provides the single source of truth for ABI classification:
+the ``abi::Type*`` type system, the ``ABIInfo`` base and target-specific
+implementations (e.g.  X86_64, AArch64), and the classification result types
+(e.g.  ABIArgInfo, ABIFunctionInfo).  **ABITypeMapper** maps ``mlir::Type`` to
+``abi::Type*`` so that MLIR dialect types can be classified by the ABI library.
+The generic mapper relies on existing MLIR type interfaces (e.g.
+``DataLayoutTypeInterface``) for size and alignment, and pattern-matches on
+standard type categories (integers, floats, pointers, structs, arrays,
+vectors) to build ``abi::Type*``.  Dialects whose types do not conform to
+standard MLIR type categories (e.g.  CIR's ``cir::IntType`` is not
+``mlir::IntegerType``) may need dialect-aware mapping alongside the generic
+mapper to preserve semantics such as signedness, pointer identity, and
+record field structure.
+
+The **MLIR ABI lowering pass** orchestrates the flow: it uses ABITypeMapper,
+calls the library's ABIInfo, and drives rewriting from the classification
+result.  **ABIRewriteContext** is the dialect-specific interface for operation
+creation (each dialect implements it to produce e.g.  cir.call, fir.call).  A
+**target registry** (or equivalent) is used to select the appropriate ABIInfo
+for the compilation target.  There is no ABITypeInterface or separate "ABIInfo
+in MLIR"; classification lives entirely in the ABI library.
+
+ABI Lowering Flow: How the Pieces Fit Together
+----------------------------------------------
+
+This section describes the end-to-end flow of ABI lowering, showing how all
+interfaces and components work together.
+
+Step 1: Function Signature Analysis
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ABI lowering pass begins by analyzing the function signature.  Function
+operations are identified via MLIR's ``FunctionOpInterface``, which provides
+access to the function type, argument types, and return types.  The pass
+extracts the parameter types and return type to prepare them for classification.
+At this stage, the types are still in their high-level, dialect-specific form
+(e.g., ``!cir.struct`` for CIR, or ``!fir.type`` for FIR).  The pass collect
+these types into a list that will be fed to the classification logic in the next
+step.
+
+.. code-block::
+
+    Input: func @foo(%arg0: !cir.int<u, 32>,
+           %arg1: !cir.struct<{!cir.int<u, 64>,
+                                !cir.int<u, 64>}>) -> !cir.int<u, 32>
+
+Step 2: Type Mapping via ABITypeMapper
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For each argument and the return type, the pass maps ``mlir::Type`` to
+``abi::Type*`` using ABITypeMapper.  The mapper produces the representation that
+the library's ABIInfo expects; optionally, it can map back to MLIR types for
+coercion types when needed.
+
+.. code-block:: c++
+
+  // Map dialect types to the library's type system
+  ABITypeMapper abiTypeMapper(module.getDataLayout());
+  abi::Type *arg0Abi = abiTypeMapper.map(arg0Type);   // i32 -> IntegerType
+  abi::Type *arg1Abi = abiTypeMapper.map(arg1Type);   // struct -> RecordType
+  abi::Type *retAbi = abiTypeMapper.map(returnType);
+
+**Key Point**: Classification runs in the ABI library on ``abi::Type*``;
+ABITypeMapper is the only bridge from dialect types to that representation.
+
+Step 3: ABI Classification
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The library's target-specific ``ABIInfo`` (e.g.  X86_64) performs classification
+on ``abi::Type*`` and produces the library's classification result
+(e.g.  ABIFunctionInfo and ABIArgInfo as defined in ``llvm/lib/ABI/``):
+
+.. code-block:: c++
+
+    // The MLIR ABI lowering pass obtains the ABIInfo from the target
+    // registry based on the module's target triple (see Section 5.2).
+    llvm::abi::ABIInfo *abiInfo = getABIInfo();  // e.g. X86_64
+    llvm::abi::ABIFunctionInfo abiFI;
+    abiInfo->computeInfo(abiFI, arg0Abi, arg1Abi, retAbi);
+    // For struct<i64,i64> on x86_64: produces Expand (two i64 args)
+
+
+Output: the library's classification (e.g.  ABIFunctionInfo) for all arguments
+and return:
+* ``%arg0 (i32)`` → Direct (pass as-is)
+* ``%arg1 (struct)`` → Expand (split into two i64 fields)
+* Return type → Direct
+
+Step 4: Function Signature Rewriting
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+After the library's classification is complete, the pass rewrites the function
+to match the ABI requirements using the dialect's `ABIRewriteContext`.  The
+classification result (from the ABI library) describes the lowered signature;
+the rewrite context creates the actual dialect operations.  For example, if a
+struct is classified as "Expand", the new function signature will have multiple
+scalar parameters instead of the single struct parameter.
+
+.. code-block:: c++
+
+    ABIRewriteContext &ctx = getDialectRewriteContext();
+
+    // Create new function with lowered signature
+    FunctionType newType = ...; // (i32, i64, i64) -> i32
+    Operation *newFunc = ctx.createFunction(loc, "foo", newType);
+
+**Key Point**: The original function had signature ``(i32, struct) -> i32``, but
+the ABI-lowered function has signature ``(i32, i64, i64) -> i32`` with the
+struct expanded into its constituent fields.
+
+Step 5: Argument Expansion
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+With the function signature rewritten, the pass updates all call sites to match
+the new signature, using the classification from the ABI library to drive
+rewriting via ``ABIRewriteContext``.  For arguments classified as "Expand", the
+pass breaks down the aggregate into its constituent parts (e.g.  struct into two
+i64 values). The rewrite context provides operations to extract fields and
+construct the new call with the expanded argument list.
+
+.. code-block:: c++
+
+    // Original call: call @foo(%val0, %structVal)
+    // Need to extract struct fields:
+
+    Value field0 = ctx.createExtractValue(loc, structVal, {0}); // extract 1st i64
+    Value field1 = ctx.createExtractValue(loc, structVal, {1}); // extract 2nd i64
+
+    // New call with expanded arguments
+    ctx.createCall(loc, newFunc, {resultType}, {val0, field0, field1});
+
+**Key Point**: ``ABIRewriteContext`` abstracts the dialect-specific operation
+creation, so the lowering logic doesn't need to know about CIR operations.
+
+Step 6: Return Value Handling
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For functions returning large structs (indirect return):
+
+.. code-block:: c++
+
+    // If return type is classified as Indirect:
+    Value sretPtr = ctx.createAlloca(loc, retType, alignment);
+    ctx.createCall(loc, func, {}, {sretPtr, ...otherArgs});
+    Value result = ctx.createLoad(loc, sretPtr);
+
+Complete Flow Diagram
+^^^^^^^^^^^^^^^^^^^^^
+
+The diagram below combines the three-layer architecture (Section 4.1) with the
+step-by-step flow, showing which layer owns each step.
+
+.. code-block::
+
+     ┌─────────────────────────────────────────────────────────┐
+     │ Input: High-Level Function (CIR/FIR/other dialect)      │
+     │   func @foo(%arg0: i32, %arg1: struct<i64,i64>) -> i32  │
+     └──────────────────────────┬──────────────────────────────┘
+                                │
+    ╔═══════════════════════════╪═══════════════════════════════╗
+    ║  MLIR Adapter Layer       │                               ║
+    ║                           ▼                               ║
+    ║  Step 1: Extract types from FunctionOpInterface           ║
+    ║            arg0: mlir::Type, arg1: mlir::Type, ret: …     ║
+    ║                           │                               ║
+    ║                           ▼                               ║
+    ║  Step 2: ABITypeMapper    │                               ║
+    ║            mlir::Type ──> abi::Type*                      ║
+    ║            (uses DataLayoutTypeInterface for size/align)  ║
+    ╚═══════════════════════════╪═══════════════════════════════╝
+                                │
+    ╔═══════════════════════════╪═══════════════════════════════╗
+    ║  LLVM ABI Library         │  (llvm/lib/ABI/)              ║
+    ║                           ▼                               ║
+    ║  Step 3: ABIInfo::computeInfo() on abi::Type*             ║
+    ║            Applies target rules (e.g. x86_64 System V)    ║
+    ║            Produces: ABIArgInfo per arg/return            ║
+    ║              arg0 (i32)   → Direct                        ║
+    ║              arg1 (struct)→ Expand (two i64 fields)       ║
+    ║              return (i32) → Direct                        ║
+    ╚═══════════════════════════╪═══════════════════════════════╝
+                                │
+    ╔═══════════════════════════╪═══════════════════════════════╗
+    ║  Dialect-Specific Layer   │  (ABIRewriteContext)          ║
+    ║                           ▼                               ║
+    ║  Step 4: Rewrite function signature                       ║
+    ║            (i32, struct) -> i32                           ║
+    ║            becomes (i32, i64, i64) -> i32                 ║
+    ║                           │                               ║
+    ║                           ▼                               ║
+    ║  Step 5: Rewrite call sites                               ║
+    ║            createExtractValue() to expand struct args     ║
+    ║            createCall() with lowered arg list             ║
+    ║                           │                               ║
+    ║                           ▼                               ║
+    ║  Step 6: Handle return values                             ║
+    ║            Indirect: createAlloca + sret pointer          ║
+    ║            Coerced: memory-based reinterpretation         ║
+    ╚═══════════════════════════╪═══════════════════════════════╝
+                                │
+                                ▼
+     ┌─────────────────────────────────────────────────────────┐
+     │ Output: ABI-Lowered Function                            │
+     │   func @foo(%arg0: i32, %arg1: i64, %arg2: i64) -> i32  │
+     └─────────────────────────────────────────────────────────┘
+
+Key Interactions Between Components
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Classification lives in the ABI library: ``ABIInfo`` operates on ``abi::Type*``
+and produces classification results (e.g.  ABIArgInfo, ABIFunctionInfo).  MLIR
+types reach the ABI library only via ABITypeMapper, which converts
+``mlir::Type`` to ``abi::Type*``.  The lowering pass (1) maps types with
+ABITypeMapper, (2) calls the library's ABIInfo to get classification, and (3)
+uses that result to drive rewriting through the dialect's ABIRewriteContext.
+
+ABIRewriteContext consumes the classification (e.g.  "Expand" for a struct) and
+performs the actual IR changes: createFunction with the lowered signature,
+createExtractValue and createCall at call sites.  Each dialect implements
+ABIRewriteContext to produce its own operations (e.g.  cir.call, fir.call).
+This keeps classification in one place (the ABI library) and limits dialect code
+to operation creation.
+
+ABIRewriteContext and Target Registry
+=====================================
+
+ABIRewriteContext Interface
+---------------------------
+
+ABIRewriteContext is the only dialect-specific layer: CIR and FIR each
+implement it to create their own dialect operations (e.g.  cir.call, fir.call).
+In a module with mixed dialect content, the pass selects the appropriate
+ABIRewriteContext for each function based on the dialect of its operations.
+Classification is performed by the library's ABIInfo and produces the library's
+result (e.g.  ABIFunctionInfo, ABIArgInfo); ABIRewriteContext consumes that
+classification to perform the actual IR rewriting.  ABIRewriteContext is also
+responsible for updating ABI-related attributes (e.g.  sret, byval, signext,
+zeroext, inreg) on the rewritten function signatures and call sites as indicated
+by the classification result.
+
+The interface defines two high-level methods:
+``rewriteFunctionDefinition(funcOp, classification, builder)`` rewrites a
+function's signature and body (coercing return values, adapting arguments,
+handling sret), and ``rewriteCallSite(callOp, classification, builder)``
+rewrites a call to match the lowered callee (coercing arguments, handling
+coerced returns).  Each method encapsulates the full rewriting logic for its
+scope, using the dialect's own builder operations internally
+(e.g.  ``cir::CastOp``, ``cir::AllocaOp``, ``cir::StoreOp``).  Each dialect
+handles operation creation using its own builder internally.
+
+Each dialect implementing ABI lowering must provide a concrete
+``ABIRewriteContext`` subclass.  This is a significant but one-time cost:
+CIR implements ``CIRABIRewriteContext``, FIR implements ``FIRABIRewriteContext``,
+and any future dialect reuses the shared classification infrastructure by
+providing its own context implementation.  The alternative—reimplementing the
+entire ABI classification logic per dialect—would require 8,000-15,000 lines per
+dialect (the combined size of x86_64 and AArch64 classification code plus all
+supporting infrastructure), introduce divergent behavior across dialects, and
+create a maintenance burden where ABI bug fixes must be propagated to every
+dialect independently.
+
+Target Registry
+---------------
+
+We use the library's target selection or registry to obtain the appropriate
+ABIInfo for the compilation target (e.g.  X86_64, AArch64).  We do not introduce
+a separate MLIR TargetRegistry unless the MLIR ABI pass needs it for pass
+options or configuration.  The dependency direction is: the MLIR ABI pass
+depends on ``llvm/lib/ABI``; there is no reverse dependency from the ABI library
+to MLIR dialects.
+
+Open Questions
+==============
+
+The following items are open for discussion.  This section may be revised,
+shortened, or removed before final merge.
+
+How to Handle clang::TargetInfo Dependency in MLIR?
+---------------------------------------------------
+
+The CIR incubator currently uses ``clang::TargetInfo`` to query target-specific
+properties needed for ABI decisions, such as pointer width, alignment,
+endianness, and calling convention availability.  Moving this functionality to
+MLIR dialect-agnostic infrastructure raises an architectural question: should
+MLIR code depend on a Clang library, or should it use MLIR-based mechanisms?
+
+Three approaches are under consideration.
+
+1. Continue using ``clang::TargetInfo`` directly, accepting an MLIR→Clang
+   dependency for this target-specific infrastructure.  This approach requires
+   no additional implementation since it already works in the CIR incubator,
+   and ``clang::TargetInfo`` provides comprehensive, battle-tested coverage of
+   all target properties.  However, it creates a dependency relationship that
+   may violate MLIR's architectural principle of being a peer to Clang rather
+   than dependent on it.
+2. Combine ``llvm::Triple`` with MLIR's ``DataLayoutInterface``, supplemented by
+   module-level attributes for ABI-specific properties not covered by the data
+   layout.  This approach maintains clean layering with no Clang dependency and
+   follows MLIR patterns, but requires defining approximately 10-15 additional
+   attributes and some upfront design work.
+3. Create a new ``mlir::target::TargetInfo`` abstraction with minimal methods
+   tailored specifically for ABI needs (approximately 15-20 methods).  This
+   provides clean layering without Clang dependency but requires implementing
+   and maintaining target-specific code that duplicates some knowledge from
+   ``clang::TargetInfo``.
+
+Option 2 is recommended as the preferred approach.  It maintains MLIR's
+independence from Clang, which is important for MLIR's mission to be reusable by
+non-Clang frontends like Rust, Julia, and Swift.  Target information is input
+metadata rather than an output format, so it should be expressible through
+MLIR's existing mechanisms rather than requiring external dependencies.  Option
+3 serves as an acceptable fallback if Option 2 proves insufficient during
+prototyping, while Option 1 is not recommended due to the architectural concerns
+around MLIR depending on Clang.
+
+Scope: C Calling Convention vs.  Arbitrary Calling Conventions
+--------------------------------------------------------------
+
+This design focuses on the **C calling convention layer** (e.g. cdecl, System V,
+AAPCS).  C++ ABI concerns such as non-trivial copy constructors or destructors
+are largely handled elsewhere in the compilation pipeline; the ABI library and
+MLIR integration layer address how arguments and return values are passed at the
+C ABI boundary.  An open question is whether the design should remain explicitly
+scoped to C calling conventions only, or be general enough to support arbitrary
+calling conventions (e.g. vectorcall, preserve_most) via extensible interfaces.
+Clarifying this scope will guide the design of the LLVM ABI library integration
+and the MLIR pass.
diff --git a/clang/docs/CIR/CleanupAndEHDesign.md b/clang/docs/CIR/CleanupAndEHDesign.md
deleted file mode 100644
index afe259c3524ee..0000000000000
--- a/clang/docs/CIR/CleanupAndEHDesign.md
+++ /dev/null
@@ -1,1587 +0,0 @@
-# ClangIR Cleanup and Exception Handling Design
-
-```{contents}
----
-local:
----
-```
-
-## Overview
-
-This document describes the design for C++ cleanups and exception
-handling representation and lowering in the CIR dialect. The initial CIR
-generation will follow the general structure of the cleanup and
-exception handling code in Clang's LLVM IR generation. In particular,
-we will continue to use the `EHScopeStack` with pushing and popping of
-`EHScopeStack::Cleanup` objects to drive the creation of cleanup scopes
-within CIR.
-
-However, the LLVM IR generated by Clang is fundamentally unstructured
-and therefore isn't well suited to the goals of CIR. Therefore, we are
-proposing a high-level representation that follows MLIR's structured
-control flow model.
-
-The `cir::LowerCFG` pass will lower this high-level representation to a
-different form where control flow is block-based and explicit. This form
-will more closely resemble the LLVM IR used when Clang is generating
-LLVM IR directly. However, this form will still be ABI-agnostic.
-
-An additional pass will be introduced to lower the flattened form to an
-ABI-specific representation. This ABI-specific form will have a direct
-correspondence to the LLVM IR exception handling representation for a
-given target.
-
-## High-level CIR representation
-
-### Normal and EH cleanups
-
-Scopes that require normal or EH cleanup will be represented using a new
-operation, `cir.cleanup.scope`.
-
-```
-cir.cleanup.scope {
-  // body region
-} cleanup [normal|eh|all] {
-  // cleanup instructions
-}
-```
-
-Execution begins with the first operation in the body region and
-continues according to normal control flow semantics until a terminating
-operation (`cir.yield`, `cir.break`, `cir.return`, `cir.continue`) is
-encountered or an exception is thrown.
-
-If the cleanup region is marked as `eh_only`, normal control flow exits
-from the body region skip the cleanup region and continue to their
-normal destination according to the semantics of the operation. If the
-cleanup region is not marked as `eh_only`, normal control flow exits
-from the body region must execute the cleanup region before control is
-transferred to the destination implied by the operation.
-
-If a `cir.goto` operation occurs within a cleanup scope, the behavior
-depends on the target of the operation. If the target is within the
-same cleanup scope, control is transferred to the target block directly.
-If the target is not within the cleanup scope, control is transferred to
-the cleanup region according to the rules described above for normal
-exits before branching to the destination of the goto operation.
-
-While we do not expect to encounter `cir.br` or `cir.brcond` operations
-that exit a cleanup scope, if such a thing did happen, it would follow
-the rules described above for `cir.goto` operations.
-
-The `cir.indirect_br` operation is not permitted within a cleanup scope.
-
-When an exception is thrown from within a cleanup scope and not caught
-within the scope, the cleanup region must be executed before handling of
-the exception continues. If the cleanup scope is nested within another
-cleanup scope, the cleanup region of the inner scope is executed,
-followed by the cleanup region of the outer scope, and handling
-continues according to these rules. If the cleanup scope is nested
-within a try operation, the cleanup region is executed before control is
-transferred to the catch handlers. If an exception is thrown from within
-a cleanup region that is not nested within either another cleanup region
-or a try operation, the cleanup region is executed and then exception
-unwinding continues as if a `cir.resume` operation had been executed.
-
-If a `cir.resume` operation occurs within a cleanup scope, for example,
-if the scope contains a try operation with uncaught exception types, the
-`cir.resume` operation will unwind to the cleanup region of the enclosing
-cleanup scope.
-
-Note that this design eliminates the need for synthetic try operations,
-such as were used to represent calls within a cleanup scope in the
-ClangIR incubator project.
-
-#### Implementation notes
-
-The `cir.cleanup.scope` must be created when we call `pushCleanup`. We
-will need to set the insertion point at that time. When each cleanup
-block is popped, we will need to set the insertion point to immediately
-following the cleanup scope operation. If `forceCleanups()` is called,
-it will pop cleanup blocks, which is good.
-
-#### Example: Automatic storage object cleanup
-
-**C++**
-
-``` c++
-void someFunc() {
-  SomeClass c;
-  c.doSomething();
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc() {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.cleanup.scope {
-    cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  } cleanup normal {
-    cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-In this example, we create an instance of `SomeClass` which has a
-constructor and a destructor. If an exception occurs within the
-constructor call, it unwinds without any handling in this function. The
-cleanup scope is not entered in that case. Once the object has been
-constructed, we enter a cleanup scope which continues until the object
-goes out of scope, in this case for the remainder of the function.
-
-If an exception is thrown from within the `doSomething()` function, we
-execute the cleanup region, calling the `SomeClass` destructor before
-continuing to unwind the exception. If the call to `doSomething()`
-completes successfully, the object goes out of scope and we execute the
-cleanup region, calling the destructor, before continuing to the return
-operation.
-
-#### Example: Multiple automatic objects
-
-**C++**
-
-``` c++
-void someFunc() {
-  SomeClass c;
-  SomeClass c2;
-  c.doSomething();
-  SomeClass c3;
-  c3.doSomething();
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc() {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  %1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
-  %2 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c3", init]
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.cleanup.scope {
-    cir.call @_ZN9SomeClassC1Ev(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.cleanup.scope {
-      cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.call @_ZN9SomeClassC1Ev(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.cleanup.scope {
-        cir.call @_ZN9SomeClass11doSomethingEv(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      } cleanup normal {
-        cir.call @_ZN9SomeClassD1Ev(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      }
-      cir.yield
-    } cleanup normal {
-      cir.call @_ZN9SomeClassD1Ev(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.yield
-    }
-    cir.yield
-  } cleanup normal {
-    cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-In this example, we have three objects with automatic storage duration.
-The destructor must be called for each object that has been constructed,
-and the destructors must be called in reverse order of object creation.
-We guarantee that by creating nested cleanup scopes as each object is
-constructed.
-
-Normal execution control flows through the body region of each of the
-nested cleanup scopes until the body of the innermost scope. Next, the
-cleanup scopes are visited, calling the destructor once in each cleanup
-scope, in reverse order of the object construction.
-
-#### Implementation notes
-
-Branch through cleanups will be handled during flattening. In the
-structured CIR representation, an operation like `cir.break`,
-`cir.return`, or `cir.continue` has well-defined behavior. We will need
-to define the semantics such that they include visiting the cleanup
-region before continuing to their currently defined destination.
-
-#### Example: Branch through cleanup
-
-**C++**
-
-``` c++
-int someFunc() {
-  int i = 0;
-  while (true) {
-    SomeClass c;
-    if (i == 3)
-      continue;
-    if (i == 7)
-      break;
-    i = c.get();
-  }
-  return i;
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc() -> !s32i {
-  %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
-  %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
-  %2 = cir.const #cir.int<0> : !s32i
-  cir.store align(4) %2, %1 : !s32i, !cir.ptr<!s32i>
-  cir.scope {
-    cir.while {
-      %5 = cir.const #true
-      cir.condition(%5)
-    } do {
-      cir.scope {
-        %5 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-        cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.cleanup.scope {
-          cir.scope { // This is a scope for the `if`, unrelated to cleanups
-            %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-            %8 = cir.const #cir.int<3> : !s32i
-            %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
-            cir.if %9 {
-              cir.continue // This implicitly branches through the cleanup region
-            }
-          }
-          cir.scope { // This is a scope for the `if`, unrelated to cleanups
-            %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-            %8 = cir.const #cir.int<7> : !s32i
-            %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
-            cir.if %9 {
-              cir.break // This implicitly branches through the cleanup region
-            }
-          }
-          %6 = cir.call @_ZN9SomeClass3getEv(%5) : (!cir.ptr<!rec_SomeClass>) -> !s32i
-          cir.store align(4) %6, %1 : !s32i, !cir.ptr<!s32i>
-          cir.yield
-        } cleanup normal {
-          cir.call @_ZN9SomeClassD1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
-          cir.yield
-        }
-      }
-      cir.yield
-    }
-  }
-  %3 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-  cir.store %3, %0 : !s32i, !cir.ptr<!s32i>
-  %4 = cir.load %0 : !cir.ptr<!s32i>, !s32i
-  cir.return %4 : !s32i
-}
-```
-
-In this example we have a cleanup scope inside the body of a
-`while-loop`, and multiple instructions that may exit the loop body with
-different destinations. When the `cir.continue` operation is executed,
-it will transfer control to the cleanup region, which calls the object
-destructor before transferring control to the while condition region
-according to the semantics of the `cir.continue` operation.
-
-When the `cir.break` operation is executed, it will transfer control to
-the cleanup region, which calls the object destructor before
-transferring control to the operation following the while loop according
-to the semantics of the `cir.break` operation.
-
-If neither the `cir.continue` or `cir.break` operations are executed
-during an iteration of the loop, when the end of the cleanup scope's
-body region is reached, control will be transferred to the cleanup
-region, which calls the object destructor before transferring control to
-the next operation following the cleanup scope, in this case falling
-through to the `cir.yield` operation to complete the loop iteration.
-
-This control flow is implicit in the semantics of the CIR operations at
-this point. When this CIR is flattened, explicit branches and a switch
-on destination slots will be created, matching the LLVM IR control flow
-for cleanup block sharing.
-
-#### Example: EH-only cleanup
-
-**C++**
-
-``` c++
-class Base {
-public:
-  Base();
-  ~Base();
-};
-
-class Derived : public Base {
-public:
-  Derived() : Base() { f(); }
-  ~Derived();
-};
-```
-
-**CIR**
-
-```
-cir.func @_ZN7DerivedC2Ev(%arg0: !cir.ptr<!rec_Derived>) {
-  %0 = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["this", init]
-  cir.store %arg0, %0 : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>
-  %1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived>
-  %2 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
-  cir.call @_ZN4BaseC2Ev(%2) : (!cir.ptr<!rec_Base>) -> ()
-  cir.cleanup.scope {
-    cir.call exception @_Z1fv() : () -> ()
-    cir.yield
-  } cleanup eh {
-    %3 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
-    cir.call @_ZN4BaseD2Ev(%3) : (!cir.ptr<!rec_Base>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-In this example, the `Derived` constructor calls the `Base` constructor
-and then calls a function which may throw an exception. If an exception
-is thrown, we must call the `Base` destructor before continuing to
-unwind the exception. However, if no exception is thrown, we do not call
-the destructor. Therefore, this cleanup handler is marked as eh_only.
-
-### Try Operations and Exception Handling
-
-Try-catch blocks will be represented, as they are in the ClangIR
-incubator project, using a `cir.try` operation.
-
-Each catch handler region and unwind region in a `cir.try` operation
-receives a `!cir.eh_token` argument representing the inflight exception.
-
-The `cir.begin_catch` operation takes a `!cir.eh_token` as an argument
-and returns two values: a `!cir.catch_token` that uniquely identifies
-this catch handler, and a pointer to the exception object. When the
-catch handler includes a source variable representing the exception
-object, the pointer returned by `cir.begin_catch` will be stored to an
-alloca object for the source variable. If the handler is a catch-all,
-the `cir.begin_catch` operation will return a pointer to void, but this
-cannot be captured by a source variable.
-
-The `cir.end_catch` operation takes a `!cir.catch_token` as an argument,
-marking the end of the catch handler. All paths through the catch
-handler must converge on a single `cir.end_catch` operation.
-
-The first operation in a catch handler region must be a `cir.begin_catch`
-operation. This must be followed by a `cir.cleanup.scope` operation,
-with the `cir.end_catch` operation in its cleanup region.
-
-```
-cir.try {
-  cir.call exception @function() : () -> ()
-  cir.yield
-} catch [type #cir.global_view<@_ZTIPf> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
-  %catch_token, %exn_ptr = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.float>)
-  cir.cleanup.scope {
-    ...
-    cir.yield
-  } cleanup eh {
-    cir.end_catch %catch_token
-    cir.yield
-  }
-  cir.yield
-} unwind (%eh_token : !cir.eh_token) {
-  cir.resume %eh_token : !cir.eh_token
-}
-```
-
-The operation consists of a try region, which contains the operations to
-be executed during normal execution, and one or more handler regions,
-which represent catch handlers or the fallback unwind for uncaught
-exceptions.
-
-#### Example: Simple try-catch
-
-**C++**
-
-``` c++
-void someFunc() {
-  try {
-    f();
-  } catch (std::exception &e) {
-    // Do nothing
-  }
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
-  cir.scope {
-    cir.try {
-      cir.call exception @_Z1fv() : () -> ()
-      cir.yield
-    } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
-      %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
-      cir.cleanup.scope {
-        %2 = cir.load align(8) %1 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
-        cir.store align(8) %2, %0 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
-        cir.yield
-      } cleanup eh {
-        cir.end_catch %catch_token
-        cir.yield
-      }
-      cir.yield
-    } unwind (%eh_token : !cir.eh_token) {
-      cir.resume %eh_token : !cir.eh_token
-    }
-  }
-  cir.return
-}
-```
-
-If the call to `f()` throws an exception that matches the handled type
-(`std::exception&`), control will be transferred to the catch handler
-for that type, which simply yields, continuing execution immediately
-after the try operation.
-
-If the call to `f()` throws any other type of exception, control will be
-transferred to the unwind region, which simply continues unwinding the
-exception at the next level, in this case, the handlers (if any) for the
-function that called `someFunc()`.
-
-#### Example: Try-catch with catch all
-
-**C++**
-
-``` c++
-void someFunc() {
-  try {
-    f();
-  } catch (std::exception &e) {
-    // Do nothing
-  } catch (...) {
-    // Do nothing
-  }
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
-  cir.scope {
-    cir.try {
-      cir.call exception @_Z1fv() : () -> ()
-      cir.yield
-    } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
-      %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
-      cir.cleanup.scope {
-        %2 = cir.load align(8) %1 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
-        cir.store align(8) %2, %0 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
-        cir.yield
-      } cleanup eh {
-        cir.end_catch %catch_token
-        cir.yield
-      }
-      cir.yield
-    } catch all (%eh_token : !cir.eh_token) {
-      %catch_token.1, %3 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
-      cir.cleanup.scope {
-        cir.yield
-      } cleanup eh {
-        cir.end_catch %catch_token.1
-        cir.yield
-      }
-      cir.yield
-    }
-  }
-  cir.return
-}
-```
-
-In this case, if the call to `f()` throws an exception that matches the
-handled type (`std::exception&`), everything works exactly as in the
-previous example. Control will be transferred to the catch handler for
-that type, which simply yields, continuing execution immediately after
-the try operation.
-
-If the call to `f()` throws any other type of exception, control will be
-transferred to the catch all region, which also yields, continuing
-execution immediately after the try operation.
-
-#### Example: Try-catch with cleanup
-
-**C++**
-
-``` c++
-void someFunc() {
-  try {
-    SomeClass c;
-    c.doSomething();
-  } catch (...) {
-    // Do nothing
-  }
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc(){
-  cir.scope {
-    %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-    cir.try {
-      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.cleanup.scope {
-        cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      } cleanup all {
-        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      }
-    } catch all (%eh_token : !cir.eh_token) {
-      %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
-      cir.cleanup.scope {
-        cir.yield
-      } cleanup eh {
-        cir.end_catch %catch_token
-        cir.yield
-      }
-      cir.yield
-    }
-  }
-  cir.return
-}
-```
-
-In this case, an object that requires cleanup is instantiated inside the
-try block scope. If the call to `doSomething()` throws an exception, the
-cleanup region will be executed before control is transferred to the
-catch handler.
-
-#### Example: Try-catch within a cleanup region
-
-**C++**
-
-``` c++
-void someFunc() {
-  SomeClass c;
-  try {
-    c.doSomething();
-  } catch (std::exception& e) {
-    // Do nothing
-  }
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  %1 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.cleanup.scope {
-    cir.scope {
-      cir.try {
-        cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
-        %catch_token, %2 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
-        cir.cleanup.scope {
-          %3 = cir.load align(8) %2 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
-          cir.store align(8) %3, %1 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
-          cir.yield
-        } cleanup eh {
-          cir.end_catch %catch_token
-          cir.yield
-        }
-        cir.yield
-      } unwind (%eh_token : !cir.eh_token) {
-        cir.resume %eh_token : !cir.eh_token
-      }
-    }
-    cir.yield
-  } cleanup all {
-    cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-In this case, the object that requires cleanup is instantiated outside
-the try block scope, and not all exception types have catch handlers.
-
-If the call to `doSomething()` throws an exception of type
-`std::exception&`, control will be transferred to the catch handler,
-which will simply continue execution at the point immediately following
-the try operation, and the cleanup handler will be executed when the
-cleanup scope is exited normally.
-
-If the call to `doSomething()` throws any other exception of type,
-control will be transferred to the unwind region, which executes
-`cir.resume` to continue unwinding the exception. However, the cleanup
-region of the cleanup scope will be executed before exception unwinding
-continues because we are exiting the scope via the `cir.resume`
-operation.
-
-### Partial Array Cleanup
-
-Partial array cleanup is a special case because the details of array
-construction and deletion are already encapsulated within high-level CIR
-operations. When an array of objects is constructed, the constructor for
-each object is called sequentially. If one of the constructors throws an
-exception, we must call the destructor for each object that was
-previously constructed in reverse order of their construction. In the
-high-level CIR representation, we have a single operation,
-`cir.array.ctor` to represent the array construction. Because the
-cleanup needed is entirely within the scope of this operation, we can
-represent the cleanup by adding a cleanup region to this operation.
-
-```
-cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_SomeClass x 16>>) {
-^bb0(%arg0: !cir.ptr<!rec_SomeClass>):
-  cir.call @_ZN9SomeClassC1Ev(%arg0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.yield
-} cleanup {
-^bb0(%arg0: !cir.ptr<!rec_SomeClass>):
-  cir.call @_ZN9SomeClassD1Ev(%arg0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.yield
-}
-```
-
-This representation shows how a single instance of the object is
-initialized and cleaned up. When the operation is transformed to a
-low-level form (during `cir::LoweringPrepare`), these two regions will
-be expanded to a loop within a `cir.cleanup.scope` for the
-initialization, and a loop within the cleanup scope's cleanup region to
-perform the partial array cleanup, as follows
-
-```
-cir.scope {
-  %1 = cir.const #cir.int<16> : !u64i
-  %2 = cir.cast array_to_ptrdecay %0 : !cir.ptr<!cir.array<!rec_SomeClass x 16>> -> !cir.ptr<!rec_SomeClass>
-  %3 = cir.ptr_stride %2, %1 : (!cir.ptr<!rec_SomeClass>, !u64i) -> !cir.ptr<!rec_SomeClass>
-  %4 = cir.alloca !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>, ["__array_idx"]
-  cir.store %2, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
-  cir.cleanup.scope {
-    cir.do {
-      %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
-      cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
-      %6 = cir.const #cir.int<1> : !u64i
-      %7 = cir.ptr_stride %5, %6 : (!cir.ptr<!rec_SomeClass>, !u64i) -> !cir.ptr<!rec_SomeClass>
-      cir.store %7, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
-      cir.yield
-    } while {
-      %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
-      %6 = cir.cmp(ne, %5, %3) : !cir.ptr<!rec_SomeClass>, !cir.bool
-      cir.condition(%6)
-    }
-  } cleanup eh {
-    cir.while {
-      %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
-      %6 = cir.cmp(ne, %5, %2) : !cir.ptr<!rec_SomeClass>, !cir.bool
-      cir.condition(%6)
-    } cir.do {
-      %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
-      %6 = cir.const #cir.int<-1> : !s64i
-      %7 = cir.ptr_stride %5, %6 : (!cir.ptr<!rec_SomeClass>, !s64i) -> !cir.ptr<!rec_SomeClass>
-      cir.call @_ZN9SomeClassD1Ev(%7) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.store %7, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
-      cir.yield
-    }
-  }
-}
-```
-
-Here, both the construction and cleanup loops use the same temporary
-pointer variable to track their location. If an exception is thrown by
-one of the constructor, the `__array_idx` variable will point to the
-object that was being constructed when the exception was thrown. If the
-exception was thrown during construction of the first object,
-`__array_idx` will point to the start of the array, and so no destructor
-will be called. If an exception is thrown during the constructor call
-for any other object, `__array_idx` will not point to the start of the
-array, and so the cleanup region will decrement the pointer, call the
-destructor for the previous object, and so on until we reach the
-beginning of the array. This corresponds to the way that partial array
-destruction is handled in Clang's LLVM IR codegen.
-
-## CFG Flattening
-
-Before CIR can be lowered to the LLVM dialect, the CFG must be
-flattened. That is, functions must not contain nested regions, and all
-blocks in the function must belong to the parent region. This state is
-formed by the `cir::FlattenCFG` pass. This pass will need to transform
-the high-level CIR representation described above to a flat form where
-cleanups and exception handling are explicitly routed through blocks,
-which are shared as needed.
-
-The CIR representation will remain ABI agnostic after the flattening
-pass. The flattening pass will implement the semantics for branching
-through cleanup regions using the same slot and dispatch mechanism used
-in Clang's LLVM IR codegen.
-
-### Exception Handling
-
-Flattening the CIR for exception handling, including any cleanups that
-must be performed during exception unwinding, requires some specialized
-CIR operations. The operations that were used in the ClangIR incubator
-project were closely matched to the Itanium exception handling ABI. In
-order to achieve a representation that also works well for other ABIs,
-the following new operations are being proposed: `cir.eh.initiate`,
-`cir.eh.dispatch`, `cir.eh.terminate`, `cir.begin_cleanup`, and
-`cir.end_cleanup`. The `cir.begin_catch` and `cir.end_catch` operations,
-described above, are also used in the flattened form.
-
-Any time a cir.call operation that may throw and exception appears
-within the try region of a `cir.try` operation or within the body region
-of a `cir.cleanup.scope` with a cleanup region marked as an exception
-cleanup, the call will be converted to a `cir.try_call` operation, with
-normal and unwind destinations. The first operation in the unwind
-destination block must be a `cir.eh.initiate` operation.
-
- `%eh_token = cir.eh.initiate [cleanup]`
-
-If this destination includes cleanup code, the cleanup keyword will be
-present, and the cleanup code will be executed before the exception is
-dispatched to any handlers. The `cir.eh.initiate` operation returns a
-value of type `!cir.eh_token`. This is an opaque value that will be used
-during ABI-lowering. At this phase, it conceptually represents the
-exception that was thrown and is passed as the argument to the
-`cir.begin_cleanup`, `cir.begin_catch`, and `cir.eh.dispatch`
-operations.
-
-```
-cir.eh.dispatch %eh_token : !cir.eh_token [
-  catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb6
-  catch_all : ^bb7
-]
-
-cir.eh.dispatch %eh_token : !cir.eh_token [
-  catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb6
-  unwind : ^bb7
-]
-```
-
-The `cir.eh.dispatch` operation behaves similarly to the LLVM IR switch
-instruction. It takes as an argument a token that was returned by a
-previous `cir.eh.initiate` operation. It then has a list of key-value
-pairs, where the key is either a type identifier, the keyword catch_all,
-or the keyword unwind and the value is a block to which execution should
-be transferred if the key is matched. Although the example above shows
-both the catch_all and unwind keyword, in practice only one or the other
-will be present, but the operation is required to have one of these
-values.
-
-When we are unwinding an exception with cleanups, the `cir.eh.initiate`
-operation will be marked with the cleanup attribute and will be followed
-by a branch to the cleanup block, passing the EH token as an operand to
-the block. The cleanup block will begin with a call to
-`cir.begin_cleanup` which returns a cleanup token.
-
-```
-^bb4 (%eh_token : !cir.eh_token): 
-  %cleanup_token = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
-```
-
-This is followed by the operations to perform the cleanup and then a
-cir.end_cleanup operation.
-
-  `cir.end_cleanup(%cleanup_token : !cir.cleanup_token)`
-
-Finally, the cleanup block either branches to a catch dispatch block or
-executes a `cir.resume` operation to continue unwinding the exception.
-
-When an exception is caught, the catch block will receive the eh token
-for the exception being caught as an argument. The `cir.begin_catch`
-and `cir.end_catch` operations, described above in the high-level
-representation, continue to be used in the flattened form. In the
-flattened form, the `eh_token` argument to `cir.begin_catch` comes
-from the block argument rather than a region argument, and the
-`cir.end_catch` operation appears directly in the catch block rather
-than within a `cir.cleanup.scope` cleanup region.
-
-#### Example: Try-catch with cleanup
-
-**C++**
-
-``` c++
-void someFunc() {
-  try {
-    SomeClass c;
-    c.doSomething();
-  } catch (...) {
-    // Do nothing
-  }
-}
-```
-
-**High-level CIR**
-
-```
-cir.func @someFunc(){
-  cir.scope {
-    %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-    cir.try {
-      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-      cir.cleanup.scope {
-        cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      } cleanup all {
-        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.yield
-      }
-    } catch all (%eh_token : !cir.eh_token) {
-      %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
-      cir.cleanup.scope {
-        cir.yield
-      } cleanup eh {
-        cir.end_catch %catch_token
-        cir.yield
-      }
-      cir.yield
-    }
-  }
-  cir.return
-}
-```
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb2 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb8
-^bb3 // EH catch (from entry block)
-  %1 = cir.eh.initiate : !cir.eh_token
-  cir.br ^bb6(%1 : !cir.eh_token)
-^bb4 // EH cleanup (from ^bb1)
-  %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
-  cir.eh.dispatch %eh_token.1 : !cir.eh_token [
-    catch_all : ^bb7
-  ]
-^bb7(%eh_token.2 : !cir.eh_token)
-  %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb8
-^bb8 // Normal continue (from ^bb2 or ^bb6)
-  cir.return
-}
-```
-
-In this example, the normal cleanup is performed in a different block
-than the EH cleanup. This follows the pattern established by Clang's
-LLVM IR codegen. Only the EH cleanup requires `cir.begin_cleanup` and
-`cir.end_cleanup` operations.
-
-If the `SomeClass` constructor throws an exception, it unwinds to an EH
-catch block (`^bb3`), which has excecutes a `cir.eh.initiate` operation
-before branching to a shared catch dispatch block (`^bb6`).
-
-If the `doSomething()` function throws an exception, it unwinds to an EH
-block `^bb4` that performs cleanup before branching to the shared catch
-dispatch block (`^bb5`).
-
-#### Example: Cleanup with unhandled exception
-
-**C++**
-
-``` c++
-void someFunc() {
-  SomeClass c;
-  c.doSomething();
-}
-```
-
-**High-level CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.cleanup.scope {
-    cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  } cleanup all {
-    cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb4
-^bb2 // EH cleanup (from entry block)
-  %1 = cir.eh.initiate cleanup : !cir.eh_token
-  cir.br ^bb3(%1 : !cir.eh_token)
-^bb3(%eh_token : !cir.eh_token) // Perform cleanup
-  %2 = cir.begin_cleanup(%eh_token : !cir.eh_token) : !cir.cleanup_token
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.end_cleanup(%2 : !cir.cleanup_token)
-  cir.resume %eh_token : !cir.eh_token // Unwind to caller
-^bb4 // Normal continue (from ^bb1)
-  cir.return
-}
-```
-
-In this example, if `doSomething()` throws an exception, it unwinds to
-the EH cleanup block (`^bb2`), which branches to `^bb3` to perform the
-cleanup, but because we have no catch handler, we execute `cir.resume`
-after the cleanup to unwind to the function that called `someFunc()`.
-
-#### Throwing Calls in Cleanup Regions
-
-When a call in an EH cleanup region may throw an exception, it requires
-special handling. The C++ standard requires that if an exception is
-thrown during exception cleanup (i.e., while unwinding a previous
-exception), the program must call `std::terminate()`. In the flattened
-CIR, such calls are replaced with `cir.try_call` operations whose
-unwind destination contains a `cir.eh.initiate` followed by a
-`cir.eh.terminate` operation.
-
-The `cir.eh.terminate` operation is a terminator that signals the need
-for program termination due to an exception thrown during cleanup. It
-takes the `!cir.eh_token` returned by `cir.eh.initiate` and is further
-processed during EH ABI lowering, where it is replaced with target-specific
-termination code.
-
-#### Example: Cleanup with throwing destructor
-
-**C++**
-
-``` c++
-struct ThrowingDtor {
-  ~ThrowingDtor() noexcept(false);
-};
-
-void someFunc() {
-  ThrowingDtor c;
-  c.doSomething();
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_ThrowingDtor, !cir.ptr<!rec_ThrowingDtor>, ["c", init]
-  cir.call @_ZN12ThrowingDtorC1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-  cir.cleanup.scope {
-    cir.call @_ZN12ThrowingDtor11doSomethingEv(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-    cir.yield
-  } cleanup all {
-    cir.call @_ZN12ThrowingDtorD1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-    cir.yield
-  }
-  cir.return
-}
-```
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_ThrowingDtor, !cir.ptr<!rec_ThrowingDtor>, ["c", init]
-  cir.call @_ZN12ThrowingDtorC1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-  cir.try_call @_ZN12ThrowingDtor11doSomethingEv(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-^bb1 // Normal cleanup
-  cir.call @_ZN12ThrowingDtorD1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-  cir.br ^bb6
-^bb2 // EH cleanup (from entry block)
-  %1 = cir.eh.initiate cleanup : !cir.eh_token
-  cir.br ^bb3(%1 : !cir.eh_token)
-^bb3(%eh_token : !cir.eh_token) // Perform cleanup
-  %2 = cir.begin_cleanup(%eh_token : !cir.eh_token) : !cir.cleanup_token
-  cir.try_call @_ZN12ThrowingDtorD1Ev(%0) ^bb4, ^bb5 : (!cir.ptr<!rec_ThrowingDtor>) -> ()
-^bb4 // Destructor completed: continue unwinding
-  cir.end_cleanup(%2 : !cir.cleanup_token)
-  cir.resume %eh_token : !cir.eh_token
-^bb5 // Destructor threw: terminate
-  %3 = cir.eh.initiate : !cir.eh_token
-  cir.eh.terminate %3 : !cir.eh_token
-^bb6 // Normal continue (from ^bb1)
-  cir.return
-}
-```
-
-In this example, the destructor for `ThrowingDtor` may throw. In the
-normal cleanup path (`^bb1`), the destructor is a regular `cir.call`
-since the exception would propagate normally. In the EH cleanup path
-(`^bb3`), the destructor call is a `cir.try_call` because if the
-destructor throws during exception unwinding, the program must
-terminate. If the destructor completes normally, the exception
-continues unwinding via `cir.resume`. If the destructor throws, control
-transfers to `^bb5`, which initiates exception handling and immediately
-terminates.
-
-#### Example: Shared cleanups
-
-**C++**
-
-``` c++
-int someFunc() {
-  int i = 0;
-  while (true) {
-    SomeClass c;
-    if (i == 3)
-      continue;
-    if (i == 7)
-      break;
-    i = c.get();
-  }
-  return i;
-}
-```
-
-**CIR**
-
-```
-cir.func @someFunc() -> !s32i {
-  %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
-  %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
-  %2 = cir.const #cir.int<0> : !s32i
-  cir.store align(4) %2, %1 : !s32i, !cir.ptr<!s32i>
-  cir.scope {
-    cir.while {
-      %5 = cir.const #true
-      cir.condition(%5)
-    } do {
-      cir.scope {
-        %5 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-        cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
-        cir.cleanup.scope {
-          cir.scope {
-            %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-            %8 = cir.const #cir.int<3> : !s32i
-            %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
-            cir.if %9 {
-              cir.continue
-            }
-          }
-          cir.scope {
-            %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-            %8 = cir.const #cir.int<7> : !s32i
-            %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
-            cir.if %9 {
-              cir.break
-            }
-          }
-          %6 = cir.call @_ZN9SomeClass3getEv(%5) : (!cir.ptr<!rec_SomeClass>) -> !s32i
-          cir.store align(4) %6, %1 : !s32i, !cir.ptr<!s32i>
-          cir.yield
-        } cleanup all {
-          cir.call @_ZN9SomeClassD1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
-          cir.yield
-        }
-      }
-      cir.yield
-    }
-  }
-  %3 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-  cir.store %3, %0 : !s32i, !cir.ptr<!s32i>
-  %4 = cir.load %0 : !cir.ptr<!s32i>, !s32i
-  cir.return %4 : !s32i
-}
-```
-
-**Flattened CIR**
-
-```
-cir.func @someFunc() -> !s32i {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot "]
-  %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
-  %3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
-  %4 = cir.const #cir.int<0> : !s32i
-  cir.store align(4) %4, %3 : !s32i, !cir.ptr<!s32i>
-  cir.br ^bb1
-^bb1:  // 3 preds: ^bb0, ^bb9, ^bb11
-  %5 = cir.const #true
-  cir.brcond %5 ^bb2, ^bb12
-^bb2:  // pred: ^bb1
-  cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb3
-^bb3:  // pred: ^bb2
-  %6 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
-  %7 = cir.const #cir.int<3> : !s32i
-  %8 = cir.cmp(eq, %6, %7) : !s32i, !cir.bool
-  cir.brcond %8 ^bb4, ^bb5
-^bb4:  // pred: ^bb3
-  // Set the destination slot and branch through cleanup
-  %9 = cir.const #cir.int<0> : !s32i
-  cir.store %9, %1 : !s32i, !cir.ptr<!s32i>
-  cir.br ^bb9
-^bb5:  // pred: ^bb3
-  %10 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
-  %11 = cir.const #cir.int<7> : !s32i
-  %12 = cir.cmp(eq, %10, %11) : !s32i, !cir.bool
-  cir.brcond %12 ^bb6, ^bb7
-^bb6:  // pred: ^bb5
-  // Set the destination slot and branch through cleanup
-  %13 = cir.const #cir.int<1> : !s32i
-  cir.store %13, %1 : !s32i, !cir.ptr<!s32i>
-  cir.br ^bb9
-^bb7:  // pred: ^bb5
-  %14 = cir.call @_ZN9SomeClass3getEv(%0) : (!cir.ptr<!rec_SomeClass>) -> !s32i
-  cir.store align(4) %14, %3 : !s32i, !cir.ptr<!s32i>
-  cir.br ^bb8
-^bb8: // pred: ^bb7
-  // Set the destination slot and branch through cleanup
-  %15 = cir.const #cir.int<2> : !s32i
-  cir.store %15, %1 : !s32i, !cir.ptr<!s32i>
-  cir.br ^bb9
-^bb9: // pred
-  // Shared cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  %16 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
-  cir.switch.flat %16 : !s32i, ^bb10 [
-    0: ^bb1  // continue
-    1: ^bb12 // break
-    2: ^bb11 // end of loop
-  ]
-^bb10:  // preds: ^bb9
-  cir.unreachable
-^bb11:  // pred: ^bb9
-  cir.br ^bb1
-^bb12:  // pred: ^bb1
-  %17 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
-  cir.store align(4) %17, %2 : !s32i, !cir.ptr<!s32i>
-  %18 = cir.load align(4) %2 : !cir.ptr<!s32i>, !s32i
-  cir.return %18 : !s32i
-}
-```
-
-In this example we have a cleanup scope inside the body of a while loop,
-and multiple instructions that may exit the loop body with different
-destinations. For simplicity, the example is shown without exception
-handling.
-
-When any of the conditions that exit a loop iteration occur (continue,
-break, or completion of an iteration), we set a cleanup destination slot
-to a unique value and branch to a shared normal cleanup block. That
-block performs the cleanup and then compares the cleanup destination
-slot value to the set of expected constants and branches to the
-corresponding destination.
-
-For example, when the continue instruction is reached, we set the
-cleanup destination slot (`%1`) to zero, branch to the shared cleanup
-block (`^bb9`), which calls the `SomeClass` destructor, then uses
-`cir.switch.flat` to switch on the cleanup destination slot value and,
-finding it to be zero, branches to the loop condition block (`^bb1`).
-
-If none of the expected values is matched, the `cir.switch.flat`
-branches to a block with a `cir.unreachable` operation. This corresponds
-to the behavior of Clang's LLVM IR codegen.
-
-## ABI Lowering
-
-A new pass will be introduced to lower the flattened representation to
-lower the ABI-agnostic flattened CIR representation to an ABI-specific
-form. This will be a separate pass from the main CXXABI lowering pass,
-which runs before CFG flattening. The ABI lowering pass will introduce
-personality functions and ABI-specific exception handling operations.
-
-This new pass will make use of the `cir::CXXABI` interface class and
-ABI-specific subclasses, but it will introduce a new set of interface
-methods for use with the exception handling ABI.
-
-For each supported exception handling ABI, the operations and function
-calls used will have a direct correspondence to the LLVM IR instructions
-and runtime library functions used for that ABI. The LLVM IR exception
-handling model is described in detail here: [LLVM Exception
-Handling](https://llvm.org/docs/ExceptionHandling.html).
-
-A personality function attribute will be added to functions that require
-it during the ABI lowering phase.
-
-### Itanium ABI Lowering
-
-The Itanium exception handling ABI representation replaces the
-`cir.eh.initiate` and `cir.eh.dispatch` operations with a
-`cir.eh.landingpad` operation and a series of `cir.compare` and
-`cir.brcond` operations to model the correct handling based on type IDs
-for the catch handlers. The `cir.begin_cleanup` and `cir.end_cleanup`
-operations are simply dropped. The `cir.begin_catch` operation becomes a
-call to `__cxa_begin_catch`. The `cir.end_catch` operation becomes a
-call to `__cxa_end_catch`. The `cir.eh.terminate` operation becomes a
-call to `__clang_call_terminate` (which calls `__cxa_begin_catch`
-followed by `std::terminate()`) and then an unreachable operation.
-
-The only operation that is specific to Itanium exception handling is
-`cir.eh.landingpad`.
-
-  `%exn_ptr_0, %type_id = cir.eh.landingpad [@_ZTISt9exception] : !cir.ptr<!void>, !u32i`
-
-This operation corresponds directly to the LLVM IR landingpad
-instruction. It may have a list of type IDs that the handler can catch
-(or null for \"catch all\") or it may have the cleanup attribute if the
-handler performs cleanup but does not catch any exceptions.
-
-#### Example: Try-catch with cleanup
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb2 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb8
-^bb3 // EH catch (from entry block)
-  %1 = cir.eh.initiate : !cir.eh_token
-  cir.br ^bb6(%1 : !cir.eh_token)
-^bb4 // EH cleanup (from ^bb1)
-  %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
-  cir.eh.dispatch %eh_token.1 : !cir.eh_token [
-    catch_all : ^bb7
-  ]
-^bb7(%eh_token.2 : !cir.eh_token)
-  %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb8
-^bb8 // Normal continue (from ^bb2 or ^bb6)
-  cir.return
-}
-```
-
-**ABI-lowered CIR**
-
-```
-cir.func @someFunc() #personality_fn = @__gxx_personality_v0 {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb2 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb8
-^bb3 // EH catch (from entry block)
-  %exn, %type_id = cir.eh.landingpad [null] : (!cir.ptr<!void>, !u32i)
-  cir.br ^bb6(%exn, &type_id : !cir.ptr<!void>, !u32i)
-^bb4 // EH cleanup (from ^bb1)
-  %exn.1, %type_id.1 = cir.eh.landingpad cleanup [null] : (!cir.ptr<!void>, !u32i)
-  cir.br ^bb5(%exn, %type_id : !cir.ptr<!void>, !u32i)
-^bb5(%1: !cir.ptr<!void>, %2: !u32i)
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb6(%1, %2 : !cir.ptr<!void>, !u32i)
-^bb6(%3: !cir.ptr<!void>, %4: !u32i) // Catch dispatch (from ^bb3 or ^bb4)
-  cir.br ^bb7(%3, %4 : !cir.ptr<!void>, !u32i)
-^bb7(%5: !cir.ptr<!void>, %6: !u32i) // Catch all handler
-  %7 = cir.call @__cxa_begin_catch(%5 : !cir.ptr<!void>)
-  cir.call @__cxa_end_catch()
-  cir.br ^bb8
-^bb8 // Normal continue (from ^bb2 or ^bb6)
-  cir.return
-}
-```
-
-In this example, if an exception is thrown by the `SomeClass`
-constructor, it unwinds to a landing pad block (`^bb3`), which branches
-to the shared catch dispatch block (`^bb6`), which branches to the catch
-all handler block (`^bb7`). The catch all handler calls
-`__cxa_begin_catch` and `__cxa_end_catch` and then continues to the
-normal continuation block (`^bb8`).
-
-#### Example: Try-catch with multiple catch handlers
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  cir.try_call @f() ^bb1, ^bb2
-^bb1
-  cir.br ^bb7
-^bb2 // EH catch (from entry block)
-  %1 = cir.eh.initiate : !cir.eh_token
-  cir.br ^bb3(%1 : !cir.eh_token)
-^bb3(%eh_token : !cir.eh_token) // Catch dispatch (from ^bb2)
-  cir.eh.dispatch %eh_token : !cir.eh_token [
-    catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb4
-    catch (#cir.global_view<@_ZTIf> : !u32i) : ^bb5
-    catch_all : ^bb6
-  ]
-^bb4(%eh_token.1 : !cir.eh_token) // Catch handler for int exception
-  %catch.token = cir.begin_catch(%eh_token.1 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb5(%eh_token.2 : !cir.eh_token) // Catch handler for float exception
-  %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb6(%eh_token.3 : !cir.eh_token) // Catch all handler
-  %catch.token = cir.begin_catch(%eh_token.3 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb7 // Normal continue (from ^bb1, ^bb4, ^bb5, or ^bb6)
-  cir.return
-}
-```
-
-**ABI-lowered CIR**
-
-```
-cir.func @someFunc() #personality_fn = @__gxx_personality_v0 {
-  cir.try_call @f() ^bb1, ^bb2
-^bb1
-  cir.br ^bb8
-^bb2 // EH catch (from entry block)
-  %exn, %type_id = cir.eh.landingpad [null] : (!cir.ptr<!void>, !u32i)
-  cir.br ^bb3(%exn, &type_id : !cir.ptr<!void>, !u32i)
-^bb3(%0: !cir.ptr<!void>, %1: !u32i) // Catch compare for int exception
-  %2 = cir.eh.typeid @_ZTIi : !u32i
-  %3 = cir.cmp(eq, %1, %2) : !u32i, !cir.bool
-  cir.brcond %3 ^bb4(%0 : !cir.ptr<!void>), ^bb5(%0, %1 : !cir.ptr<!void>, !u32i)
-^bb4(%4: !cir.ptr<!void>, %5: !u32i) // Catch all handler for int exception
-  %6 = cir.call @__cxa_begin_catch(%4 : !cir.ptr<!void>)
-  cir.call @__cxa_end_catch()
-  cir.br ^bb8
-^bb5(%7: !cir.ptr<!void>, %8: !u32i) // Catch compare for float exception
-  %9 = cir.eh.typeid @_ZTIf : !u32i
-  %10 = cir.cmp(eq, %8, %9) : !u32i, !cir.bool
-  cir.brcond %10 ^bb7(%7 : !cir.ptr<!void>), ^bb8(%7 : !cir.ptr<!void>)
-^bb6(%11: !cir.ptr<!void>, %12: !u32i) // Catch all handler for float exception
-  %13 = cir.call @__cxa_begin_catch(%11 : !cir.ptr<!void>)
-  cir.call @__cxa_end_catch()
-  cir.br ^bb8
-^bb7(%14: !cir.ptr<!void>) // Catch all handler
-  %15 = cir.call @__cxa_begin_catch(%14 : !cir.ptr<!void>)
-  cir.call @__cxa_end_catch()
-  cir.br ^bb8
-^bb8 // Normal continue (from ^bb1, ^bb4, ^bb6, or ^bb7)
-  cir.return
-}
-```
-
-In this example, if an exception is thrown by the `f()` call, it unwinds
-to a landing pad block (`^bb2`), which uses the `cir.eh.landingpad`
-operation to capture the exception pointer and its type id, then branches
-to `^bb3` to begin searching for a catch handler that handles the type id
-of the exception. Each catch handler simply consumes the exception by
-calling `__cxa_begin_catch` and `__cxa_end_catch` and then continues to
-the normal continuation block (`^bb8`).
-
-### Microsoft C++ ABI Lowering
-
-The Microsoft C++ exception handling ABI representation drops the
-`cir.eh.initiate` operation and replaces the `cir.eh.dispatch` operation
-with `cir.eh.catchswitch` operation. The `cir.begin_cleanup` and
-`cir.end_cleanup` operations are replaced with `cir.cleanuppad` and
-`cir.cleanupret` respectively, and the `cir.begin_catch` and
-`cir.end_catch` operations are replaced with `cir.catchpad` and
-`cir.catchret`.
-
-Each of these operations corresponds directly to a similarly named
-instruction in LLVM IR and have the same semantics. The first operation
-in the unwind destination of a `cir.try_call` must be either
-`cir.eh.catchswitch` or `cir.cleanuppad`.
-
-  `%4 = cir.eh.catchswitch within none [^bb2, ^bb3] unwind to caller`
-
-The `cir.eh.catchswitch` operation takes an operand which specifies the
-parent token, which may either be none or the token returned by a
-previous `cir.catchpad` operation. This is followed by a list of blocks
-which contain catch handlers. Each block in this list must begin with a
-`cir.catchpad` operation. Finally, the unwind destination is provided to
-specify where excution continues if the exception is not caught by any
-of the handlers, with unwind to caller indicating that the unwind is not
-handled further in the current function. This operation returns a token
-that is used as the operand for `cir.catchpad` operations associated
-with this switch.
-
-  `%5 = cir.cleanuppad within none []`
-
-The `cir.cleanuppad` operation takes an operand which specifies the
-parent token, which may either be none or the token returned by a
-previous `cir.catchpad` operation. This is followed by a arguments
-required by the personality function. In the case of C++ exception
-handlers, the personality function will be `__CxxFrameHandler3` and the
-argument list will be empty. This operation returns a token that is used
-as the operand for the associated `cir.cleanupret` operation.
-
-  `cir.cleanupret from %5 unwind to ^bb7`
-
-The `cir.cleanupret` operation takes an operand which specifies the
-`cir.cleanuppad` operation which is completed by this operation and a
-block at which unwinding of the current exception continues (or unwind
-to caller if there is no catch handling in the current function).
-
-  `%8 = cir.catchpad within %4 [ptr @"??_R0H at 8", i32 0, ptr %e]`
-
-The `cir.catchpad` operation takes an operand which specifies the parent
-token, which must have been return by a previous `cir.catchswitch`
-operation. This is followed by a list of arguments, beginning with the
-typeid for the type of exception being caught (or null for catch all),
-followed by a type info flag value, followed by a pointer to the
-in-flight exception. This operation returns a token that is used as the
-operand for the associated `cir.catchret` operation or as the parent for
-any `cir.catchswitch` or `cir.cleanuppad` operations that are nested
-within this catch handler.
-
-  `cir.catchret from %8 to ^bb8`
-
-The `cir.catchret` operation takes an operand which specifies the
-`cir.catchpad` operation which is completed by this operation and a
-block at which excution should be resumed.
-
-#### Example: Try-catch with cleanup
-
-**Flattened CIR**
-
-```
-cir.func @someFunc() {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb2 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb8
-^bb3 // EH catch (from entry block)
-  %1 = cir.eh.initiate : !cir.eh_token
-  cir.br ^bb6(%1 : !cir.eh_token)
-^bb4 // EH cleanup (from ^bb1)
-  %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
-  cir.eh.dispatch %eh_token.1 : !cir.eh_token [
-    catch_all : ^bb7
-  ]
-^bb7(%eh_token.2 : !cir.eh_token)
-  %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb8
-^bb8 // Normal continue (from ^bb2 or ^bb6)
-  cir.return
-}
-```
-
-**ABI-lowered CIR**
-
-```
-cir.func @someFunc() #personality_fn = @ __CxxFrameHandler3 {
-  %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
-  cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb1
-  cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
-^bb2 // Normal cleanup
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.br ^bb6
-^bb3 // EH cleanup (from ^bb1)
-  %1 = cir.cleanuppad within none : !cir.cleanup_token
-  cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
-  cir.cleanupret from %1 unwind to ^bb4
-^bb4 // Catch dispatch (from ^bb3 or ^bb4)
-  %2 = cir.catchswitch within none [^bb5] unwind to caller
-^bb5
-  %catch.token = cir.catchpad within %2 [null : !cir.ptr<!void>] : !cir.catch_token
-  cir.catchret within %catch.token to ^bb6
-^bb6 // Normal continue (from ^bb2 or ^bb6)
-  cir.return
-}
-```
-
-#### Example: Try-catch with multiple catch handlers
-
-**Flattened CIR**
-
-```
-cir.func @someFunc(){
-  cir.try_call @f() ^bb1, ^bb2
-^bb1
-  cir.br ^bb7
-^bb2 // EH catch (from entry block)
-  %1 = cir.eh.initiate : !cir.eh_token
-  cir.br ^bb3(%1 : !cir.eh_token)
-^bb3(%eh_token : !cir.eh_token) // Catch dispatch (from ^bb2)
-  cir.eh.dispatch %eh_token : !cir.eh_token [
-    catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb4
-    catch (#cir.global_view<@_ZTIf> : !u32i) : ^bb5
-    catch_all : ^bb6
-  ]
-^bb4(%eh_token.1 : !cir.eh_token) // Catch handler for int exception
-  %catch.token = cir.begin_catch(%eh_token.1 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb5(%eh_token.2 : !cir.eh_token) // Catch handler for float exception
-  %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb6(%eh_token.3 : !cir.eh_token) // Catch all handler
-  %catch.token = cir.begin_catch(%eh_token.3 : !cir.eh_token) : !cir.catch_token
-  cir.end_catch(%catch.token : !cir.catch_token)
-  cir.br ^bb7
-^bb7 // Normal continue (from ^bb1, ^bb4, ^bb5, or ^bb6)
-  cir.return
-}
-```
-
-**ABI-lowered CIR**
-
-```
-cir.func @someFunc() #personality_fn = @__CxxFrameHandler3 {
-  cir.try_call @f() ^bb1, ^bb2
-^bb1
-  cir.br ^bb6
-^bb2 // EH catch (from entry block)
-  %0 = cir.catchswitch within none [^bb3, ^bb4, ^bb5] unwind to caller
-^bb3(%0: !cir.ptr<!void>) // Catch handler for int exception
-  %1 = cir.catchpad within %0 [eh.typeid @"??_R0H at 8", 0, %0 : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
-  cir.catchret from %1 to ^bb6
-^bb4(%2: !cir.ptr<!void>) // Catch compare for float exception
-  %2 = cir.catchpad within %0 [eh.typeid @"??_R0M at 8", 0, %0 : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
-  cir.catchret from %2 to ^bb6
-^bb5(%3: !cir.ptr<!void>) // Catch all handler
-  %4 = cir.catchpad within %0 [null, 64, null : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
-  cir.catchret from %4 to ^bb6
-^bb6 // Normal continue (from ^bb1, ^bb3, ^bb4, or ^bb5)
-  cir.return
-}
-```
-
-In this example, if an exception is thrown by the `f()` call, it unwinds
-to a catch dispatch block (`^bb2`), which uses the `cir.catchswitch`
-operation to dispatch to a catch handler (`^bb3`, `^bb4`, or `^bb5`)
-based on the type id of the exception. The actual comparisons in this
-case will be handled by the personality function, using tables that are
-generated from the `cir.catchpad` operations. Each catch handler simply
-continues to the normal continuation block (`^bb6`) using the
-`cir.catchret` operation.
diff --git a/clang/docs/CIR/CleanupAndEHDesign.rst b/clang/docs/CIR/CleanupAndEHDesign.rst
new file mode 100644
index 0000000000000..c8e3dbf7d87cb
--- /dev/null
+++ b/clang/docs/CIR/CleanupAndEHDesign.rst
@@ -0,0 +1,1631 @@
+=============================================
+ClangIR Cleanup and Exception Handling Design
+=============================================
+
+.. contents::
+   :local:
+
+Overview
+========
+
+This document describes the design for C++ cleanups and exception
+handling representation and lowering in the CIR dialect. The initial CIR
+generation will follow the general structure of the cleanup and
+exception handling code in Clang's LLVM IR generation. In particular,
+we will continue to use the ``EHScopeStack`` with pushing and popping of
+``EHScopeStack::Cleanup`` objects to drive the creation of cleanup scopes
+within CIR.
+
+However, the LLVM IR generated by Clang is fundamentally unstructured
+and therefore isn't well suited to the goals of CIR. Therefore, we are
+proposing a high-level representation that follows MLIR's structured
+control flow model.
+
+The ``cir::LowerCFG`` pass will lower this high-level representation to a
+different form where control flow is block-based and explicit. This form
+will more closely resemble the LLVM IR used when Clang is generating
+LLVM IR directly. However, this form will still be ABI-agnostic.
+
+An additional pass will be introduced to lower the flattened form to an
+ABI-specific representation. This ABI-specific form will have a direct
+correspondence to the LLVM IR exception handling representation for a
+given target.
+
+High-level CIR representation
+=============================
+
+Normal and EH cleanups
+----------------------
+
+Scopes that require normal or EH cleanup will be represented using a new
+operation, ``cir.cleanup.scope``.
+
+.. code-block::
+
+    cir.cleanup.scope {
+      // body region
+    } cleanup [normal|eh|all] {
+      // cleanup instructions
+    }
+
+Execution begins with the first operation in the body region and
+continues according to normal control flow semantics until a terminating
+operation (``cir.yield``, ``cir.break``, ``cir.return``, ``cir.continue``) is
+encountered or an exception is thrown.
+
+If the cleanup region is marked as ``eh_only``, normal control flow exits
+from the body region skip the cleanup region and continue to their
+normal destination according to the semantics of the operation. If the
+cleanup region is not marked as ``eh_only``, normal control flow exits
+from the body region must execute the cleanup region before control is
+transferred to the destination implied by the operation.
+
+If a ``cir.goto`` operation occurs within a cleanup scope, the behavior
+depends on the target of the operation. If the target is within the
+same cleanup scope, control is transferred to the target block directly.
+If the target is not within the cleanup scope, control is transferred to
+the cleanup region according to the rules described above for normal
+exits before branching to the destination of the goto operation.
+
+While we do not expect to encounter ``cir.br`` or ``cir.brcond`` operations
+that exit a cleanup scope, if such a thing did happen, it would follow
+the rules described above for ``cir.goto`` operations.
+
+The ``cir.indirect_br`` operation is not permitted within a cleanup scope.
+
+When an exception is thrown from within a cleanup scope and not caught
+within the scope, the cleanup region must be executed before handling of
+the exception continues. If the cleanup scope is nested within another
+cleanup scope, the cleanup region of the inner scope is executed,
+followed by the cleanup region of the outer scope, and handling
+continues according to these rules. If the cleanup scope is nested
+within a try operation, the cleanup region is executed before control is
+transferred to the catch handlers. If an exception is thrown from within
+a cleanup region that is not nested within either another cleanup region
+or a try operation, the cleanup region is executed and then exception
+unwinding continues as if a ``cir.resume`` operation had been executed.
+
+If a ``cir.resume`` operation occurs within a cleanup scope, for example,
+if the scope contains a try operation with uncaught exception types, the
+``cir.resume`` operation will unwind to the cleanup region of the enclosing
+cleanup scope.
+
+Note that this design eliminates the need for synthetic try operations,
+such as were used to represent calls within a cleanup scope in the
+ClangIR incubator project.
+
+Implementation notes
+^^^^^^^^^^^^^^^^^^^^
+
+The ``cir.cleanup.scope`` must be created when we call ``pushCleanup``. We
+will need to set the insertion point at that time. When each cleanup
+block is popped, we will need to set the insertion point to immediately
+following the cleanup scope operation. If ``forceCleanups()`` is called,
+it will pop cleanup blocks, which is good.
+
+Example: Automatic storage object cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      SomeClass c;
+      c.doSomething();
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc() {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.cleanup.scope {
+        cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      } cleanup normal {
+        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+In this example, we create an instance of ``SomeClass`` which has a
+constructor and a destructor. If an exception occurs within the
+constructor call, it unwinds without any handling in this function. The
+cleanup scope is not entered in that case. Once the object has been
+constructed, we enter a cleanup scope which continues until the object
+goes out of scope, in this case for the remainder of the function.
+
+If an exception is thrown from within the ``doSomething()`` function, we
+execute the cleanup region, calling the ``SomeClass`` destructor before
+continuing to unwind the exception. If the call to ``doSomething()``
+completes successfully, the object goes out of scope and we execute the
+cleanup region, calling the destructor, before continuing to the return
+operation.
+
+Example: Multiple automatic objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+  void someFunc() {
+    SomeClass c;
+    SomeClass c2;
+    c.doSomething();
+    SomeClass c3;
+    c3.doSomething();
+  }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc() {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      %1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
+      %2 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c3", init]
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.cleanup.scope {
+        cir.call @_ZN9SomeClassC1Ev(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.cleanup.scope {
+          cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.call @_ZN9SomeClassC1Ev(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.cleanup.scope {
+            cir.call @_ZN9SomeClass11doSomethingEv(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          } cleanup normal {
+            cir.call @_ZN9SomeClassD1Ev(%2) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          }
+          cir.yield
+        } cleanup normal {
+          cir.call @_ZN9SomeClassD1Ev(%1) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.yield
+        }
+        cir.yield
+      } cleanup normal {
+        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+In this example, we have three objects with automatic storage duration.
+The destructor must be called for each object that has been constructed,
+and the destructors must be called in reverse order of object creation.
+We guarantee that by creating nested cleanup scopes as each object is
+constructed.
+
+Normal execution control flows through the body region of each of the
+nested cleanup scopes until the body of the innermost scope. Next, the
+cleanup scopes are visited, calling the destructor once in each cleanup
+scope, in reverse order of the object construction.
+
+Implementation notes
+^^^^^^^^^^^^^^^^^^^^
+
+Branch through cleanups will be handled during flattening. In the
+structured CIR representation, an operation like ``cir.break``,
+``cir.return``, or ``cir.continue`` has well-defined behavior. We will need
+to define the semantics such that they include visiting the cleanup
+region before continuing to their currently defined destination.
+
+Example: Branch through cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    int someFunc() {
+      int i = 0;
+      while (true) {
+        SomeClass c;
+        if (i == 3)
+          continue;
+        if (i == 7)
+          break;
+        i = c.get();
+      }
+      return i;
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc() -> !s32i {
+      %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+      %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
+      %2 = cir.const #cir.int<0> : !s32i
+      cir.store align(4) %2, %1 : !s32i, !cir.ptr<!s32i>
+      cir.scope {
+        cir.while {
+          %5 = cir.const #true
+          cir.condition(%5)
+        } do {
+          cir.scope {
+            %5 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+            cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.cleanup.scope {
+              cir.scope { // This is a scope for the `if`, unrelated to cleanups
+                %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+                %8 = cir.const #cir.int<3> : !s32i
+                %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
+                cir.if %9 {
+                  cir.continue // This implicitly branches through the cleanup region
+                }
+              }
+              cir.scope { // This is a scope for the `if`, unrelated to cleanups
+                %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+                %8 = cir.const #cir.int<7> : !s32i
+                %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
+                cir.if %9 {
+                  cir.break // This implicitly branches through the cleanup region
+                }
+              }
+              %6 = cir.call @_ZN9SomeClass3getEv(%5) : (!cir.ptr<!rec_SomeClass>) -> !s32i
+              cir.store align(4) %6, %1 : !s32i, !cir.ptr<!s32i>
+              cir.yield
+            } cleanup normal {
+              cir.call @_ZN9SomeClassD1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
+              cir.yield
+            }
+          }
+          cir.yield
+        }
+      }
+      %3 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+      cir.store %3, %0 : !s32i, !cir.ptr<!s32i>
+      %4 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+      cir.return %4 : !s32i
+    }
+
+In this example we have a cleanup scope inside the body of a
+``while-loop``, and multiple instructions that may exit the loop body with
+different destinations. When the ``cir.continue`` operation is executed,
+it will transfer control to the cleanup region, which calls the object
+destructor before transferring control to the while condition region
+according to the semantics of the ``cir.continue`` operation.
+
+When the ``cir.break`` operation is executed, it will transfer control to
+the cleanup region, which calls the object destructor before
+transferring control to the operation following the while loop according
+to the semantics of the ``cir.break`` operation.
+
+If neither the ``cir.continue`` or ``cir.break`` operations are executed
+during an iteration of the loop, when the end of the cleanup scope's
+body region is reached, control will be transferred to the cleanup
+region, which calls the object destructor before transferring control to
+the next operation following the cleanup scope, in this case falling
+through to the ``cir.yield`` operation to complete the loop iteration.
+
+This control flow is implicit in the semantics of the CIR operations at
+this point. When this CIR is flattened, explicit branches and a switch
+on destination slots will be created, matching the LLVM IR control flow
+for cleanup block sharing.
+
+Example: EH-only cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    class Base {
+    public:
+      Base();
+      ~Base();
+    };
+
+    class Derived : public Base {
+    public:
+      Derived() : Base() { f(); }
+      ~Derived();
+    };
+
+**CIR**
+
+.. code-block::
+
+    cir.func @_ZN7DerivedC2Ev(%arg0: !cir.ptr<!rec_Derived>) {
+      %0 = cir.alloca !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>, ["this", init]
+      cir.store %arg0, %0 : !cir.ptr<!rec_Derived>, !cir.ptr<!cir.ptr<!rec_Derived>>
+      %1 = cir.load %0 : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived>
+      %2 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
+      cir.call @_ZN4BaseC2Ev(%2) : (!cir.ptr<!rec_Base>) -> ()
+      cir.cleanup.scope {
+        cir.call exception @_Z1fv() : () -> ()
+        cir.yield
+      } cleanup eh {
+        %3 = cir.base_class_addr %1 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
+        cir.call @_ZN4BaseD2Ev(%3) : (!cir.ptr<!rec_Base>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+In this example, the ``Derived`` constructor calls the ``Base`` constructor
+and then calls a function which may throw an exception. If an exception
+is thrown, we must call the ``Base`` destructor before continuing to
+unwind the exception. However, if no exception is thrown, we do not call
+the destructor. Therefore, this cleanup handler is marked as eh_only.
+
+Try Operations and Exception Handling
+-------------------------------------
+
+Try-catch blocks will be represented, as they are in the ClangIR
+incubator project, using a ``cir.try`` operation.
+
+Each catch handler region and unwind region in a ``cir.try`` operation
+receives a ``!cir.eh_token`` argument representing the inflight exception.
+
+The ``cir.begin_catch`` operation takes a ``!cir.eh_token`` as an argument
+and returns two values: a ``!cir.catch_token`` that uniquely identifies
+this catch handler, and a pointer to the exception object. When the
+catch handler includes a source variable representing the exception
+object, the pointer returned by ``cir.begin_catch`` will be stored to an
+alloca object for the source variable. If the handler is a catch-all,
+the ``cir.begin_catch`` operation will return a pointer to void, but this
+cannot be captured by a source variable.
+
+The ``cir.end_catch`` operation takes a ``!cir.catch_token`` as an argument,
+marking the end of the catch handler. All paths through the catch
+handler must converge on a single ``cir.end_catch`` operation.
+
+The first operation in a catch handler region must be a ``cir.begin_catch``
+operation. This must be followed by a ``cir.cleanup.scope`` operation,
+with the ``cir.end_catch`` operation in its cleanup region.
+
+.. code-block::
+
+    cir.try {
+      cir.call exception @function() : () -> ()
+      cir.yield
+    } catch [type #cir.global_view<@_ZTIPf> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
+      %catch_token, %exn_ptr = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.float>)
+      cir.cleanup.scope {
+        ...
+        cir.yield
+      } cleanup eh {
+        cir.end_catch %catch_token
+        cir.yield
+      }
+      cir.yield
+    } unwind (%eh_token : !cir.eh_token) {
+      cir.resume %eh_token : !cir.eh_token
+    }
+
+The operation consists of a try region, which contains the operations to
+be executed during normal execution, and one or more handler regions,
+which represent catch handlers or the fallback unwind for uncaught
+exceptions.
+
+Example: Simple try-catch
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      try {
+        f();
+      } catch (std::exception &e) {
+        // Do nothing
+      }
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
+      cir.scope {
+        cir.try {
+          cir.call exception @_Z1fv() : () -> ()
+          cir.yield
+        } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
+          %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
+          cir.cleanup.scope {
+            %2 = cir.load align(8) %1 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
+            cir.store align(8) %2, %0 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
+            cir.yield
+          } cleanup eh {
+            cir.end_catch %catch_token
+            cir.yield
+          }
+          cir.yield
+        } unwind (%eh_token : !cir.eh_token) {
+          cir.resume %eh_token : !cir.eh_token
+        }
+      }
+      cir.return
+    }
+
+If the call to ``f()`` throws an exception that matches the handled type
+(``std::exception&``), control will be transferred to the catch handler
+for that type, which simply yields, continuing execution immediately
+after the try operation.
+
+If the call to ``f()`` throws any other type of exception, control will be
+transferred to the unwind region, which simply continues unwinding the
+exception at the next level, in this case, the handlers (if any) for the
+function that called ``someFunc()``.
+
+Example: Try-catch with catch all
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      try {
+        f();
+      } catch (std::exception &e) {
+        // Do nothing
+      } catch (...) {
+        // Do nothing
+      }
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
+      cir.scope {
+        cir.try {
+          cir.call exception @_Z1fv() : () -> ()
+          cir.yield
+        } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
+          %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
+          cir.cleanup.scope {
+            %2 = cir.load align(8) %1 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
+            cir.store align(8) %2, %0 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
+            cir.yield
+          } cleanup eh {
+            cir.end_catch %catch_token
+            cir.yield
+          }
+          cir.yield
+        } catch all (%eh_token : !cir.eh_token) {
+          %catch_token.1, %3 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+          cir.cleanup.scope {
+            cir.yield
+          } cleanup eh {
+            cir.end_catch %catch_token.1
+            cir.yield
+          }
+          cir.yield
+        }
+      }
+      cir.return
+    }
+
+In this case, if the call to ``f()`` throws an exception that matches the
+handled type (``std::exception&``), everything works exactly as in the
+previous example. Control will be transferred to the catch handler for
+that type, which simply yields, continuing execution immediately after
+the try operation.
+
+If the call to ``f()`` throws any other type of exception, control will be
+transferred to the catch all region, which also yields, continuing
+execution immediately after the try operation.
+
+Example: Try-catch with cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      try {
+        SomeClass c;
+        c.doSomething();
+      } catch (...) {
+        // Do nothing
+      }
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      cir.scope {
+        %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+        cir.try {
+          cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.cleanup.scope {
+            cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          } cleanup all {
+            cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          }
+        } catch all (%eh_token : !cir.eh_token) {
+          %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+          cir.cleanup.scope {
+            cir.yield
+          } cleanup eh {
+            cir.end_catch %catch_token
+            cir.yield
+          }
+          cir.yield
+        }
+      }
+      cir.return
+    }
+
+In this case, an object that requires cleanup is instantiated inside the
+try block scope. If the call to ``doSomething()`` throws an exception, the
+cleanup region will be executed before control is transferred to the
+catch handler.
+
+Example: Try-catch within a cleanup region
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      SomeClass c;
+      try {
+        c.doSomething();
+      } catch (std::exception& e) {
+        // Do nothing
+      }
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      %1 = cir.alloca !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, ["e"]
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.cleanup.scope {
+        cir.scope {
+          cir.try {
+            cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          } catch [type #cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>] (%eh_token : !cir.eh_token) {
+            %catch_token, %2 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>)
+            cir.cleanup.scope {
+              %3 = cir.load align(8) %2 : !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>, !cir.ptr<!rec_std3A3Aexception>
+              cir.store align(8) %3, %1 : !cir.ptr<!rec_std3A3Aexception>, !cir.ptr<!cir.ptr<!rec_std3A3Aexception>>
+              cir.yield
+            } cleanup eh {
+              cir.end_catch %catch_token
+              cir.yield
+            }
+            cir.yield
+          } unwind (%eh_token : !cir.eh_token) {
+            cir.resume %eh_token : !cir.eh_token
+          }
+        }
+        cir.yield
+      } cleanup all {
+        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+In this case, the object that requires cleanup is instantiated outside
+the try block scope, and not all exception types have catch handlers.
+
+If the call to ``doSomething()`` throws an exception of type
+``std::exception&``, control will be transferred to the catch handler,
+which will simply continue execution at the point immediately following
+the try operation, and the cleanup handler will be executed when the
+cleanup scope is exited normally.
+
+If the call to ``doSomething()`` throws any other exception of type,
+control will be transferred to the unwind region, which executes
+``cir.resume`` to continue unwinding the exception. However, the cleanup
+region of the cleanup scope will be executed before exception unwinding
+continues because we are exiting the scope via the ``cir.resume``
+operation.
+
+Partial Array Cleanup
+---------------------
+
+Partial array cleanup is a special case because the details of array
+construction and deletion are already encapsulated within high-level CIR
+operations. When an array of objects is constructed, the constructor for
+each object is called sequentially. If one of the constructors throws an
+exception, we must call the destructor for each object that was
+previously constructed in reverse order of their construction. In the
+high-level CIR representation, we have a single operation,
+``cir.array.ctor`` to represent the array construction. Because the
+cleanup needed is entirely within the scope of this operation, we can
+represent the cleanup by adding a cleanup region to this operation.
+
+.. code-block::
+
+    cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_SomeClass x 16>>) {
+    ^bb0(%arg0: !cir.ptr<!rec_SomeClass>):
+      cir.call @_ZN9SomeClassC1Ev(%arg0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.yield
+    } cleanup {
+    ^bb0(%arg0: !cir.ptr<!rec_SomeClass>):
+      cir.call @_ZN9SomeClassD1Ev(%arg0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.yield
+    }
+
+This representation shows how a single instance of the object is
+initialized and cleaned up. When the operation is transformed to a
+low-level form (during ``cir::LoweringPrepare``), these two regions will
+be expanded to a loop within a ``cir.cleanup.scope`` for the
+initialization, and a loop within the cleanup scope's cleanup region to
+perform the partial array cleanup, as follows
+
+.. code-block::
+
+    cir.scope {
+      %1 = cir.const #cir.int<16> : !u64i
+      %2 = cir.cast array_to_ptrdecay %0 : !cir.ptr<!cir.array<!rec_SomeClass x 16>> -> !cir.ptr<!rec_SomeClass>
+      %3 = cir.ptr_stride %2, %1 : (!cir.ptr<!rec_SomeClass>, !u64i) -> !cir.ptr<!rec_SomeClass>
+      %4 = cir.alloca !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>, ["__array_idx"]
+      cir.store %2, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
+      cir.cleanup.scope {
+        cir.do {
+          %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
+          cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
+          %6 = cir.const #cir.int<1> : !u64i
+          %7 = cir.ptr_stride %5, %6 : (!cir.ptr<!rec_SomeClass>, !u64i) -> !cir.ptr<!rec_SomeClass>
+          cir.store %7, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
+          cir.yield
+        } while {
+          %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
+          %6 = cir.cmp(ne, %5, %3) : !cir.ptr<!rec_SomeClass>, !cir.bool
+          cir.condition(%6)
+        }
+      } cleanup eh {
+        cir.while {
+          %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
+          %6 = cir.cmp(ne, %5, %2) : !cir.ptr<!rec_SomeClass>, !cir.bool
+          cir.condition(%6)
+        } cir.do {
+          %5 = cir.load %4 : !cir.ptr<!cir.ptr<!rec_SomeClass>>, !cir.ptr<!rec_SomeClass>
+          %6 = cir.const #cir.int<-1> : !s64i
+          %7 = cir.ptr_stride %5, %6 : (!cir.ptr<!rec_SomeClass>, !s64i) -> !cir.ptr<!rec_SomeClass>
+          cir.call @_ZN9SomeClassD1Ev(%7) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.store %7, %4 : !cir.ptr<!rec_SomeClass>, !cir.ptr<!cir.ptr<!rec_SomeClass>>
+          cir.yield
+        }
+      }
+    }
+
+Here, both the construction and cleanup loops use the same temporary
+pointer variable to track their location. If an exception is thrown by
+one of the constructor, the ``__array_idx`` variable will point to the
+object that was being constructed when the exception was thrown. If the
+exception was thrown during construction of the first object,
+``__array_idx`` will point to the start of the array, and so no destructor
+will be called. If an exception is thrown during the constructor call
+for any other object, ``__array_idx`` will not point to the start of the
+array, and so the cleanup region will decrement the pointer, call the
+destructor for the previous object, and so on until we reach the
+beginning of the array. This corresponds to the way that partial array
+destruction is handled in Clang's LLVM IR codegen.
+
+CFG Flattening
+==============
+
+Before CIR can be lowered to the LLVM dialect, the CFG must be
+flattened. That is, functions must not contain nested regions, and all
+blocks in the function must belong to the parent region. This state is
+formed by the ``cir::FlattenCFG`` pass. This pass will need to transform
+the high-level CIR representation described above to a flat form where
+cleanups and exception handling are explicitly routed through blocks,
+which are shared as needed.
+
+The CIR representation will remain ABI agnostic after the flattening
+pass. The flattening pass will implement the semantics for branching
+through cleanup regions using the same slot and dispatch mechanism used
+in Clang's LLVM IR codegen.
+
+Exception Handling
+------------------
+
+Flattening the CIR for exception handling, including any cleanups that
+must be performed during exception unwinding, requires some specialized
+CIR operations. The operations that were used in the ClangIR incubator
+project were closely matched to the Itanium exception handling ABI. In
+order to achieve a representation that also works well for other ABIs,
+the following new operations are being proposed: ``cir.eh.initiate``,
+``cir.eh.dispatch``, ``cir.eh.terminate``, ``cir.begin_cleanup``, and
+``cir.end_cleanup``. The ``cir.begin_catch`` and ``cir.end_catch`` operations,
+described above, are also used in the flattened form.
+
+Any time a cir.call operation that may throw and exception appears
+within the try region of a ``cir.try`` operation or within the body region
+of a ``cir.cleanup.scope`` with a cleanup region marked as an exception
+cleanup, the call will be converted to a ``cir.try_call`` operation, with
+normal and unwind destinations. The first operation in the unwind
+destination block must be a ``cir.eh.initiate`` operation.
+
+.. code-block::
+
+    %eh_token = cir.eh.initiate [cleanup]
+
+If this destination includes cleanup code, the cleanup keyword will be
+present, and the cleanup code will be executed before the exception is
+dispatched to any handlers. The ``cir.eh.initiate`` operation returns a
+value of type ``!cir.eh_token``. This is an opaque value that will be used
+during ABI-lowering. At this phase, it conceptually represents the
+exception that was thrown and is passed as the argument to the
+``cir.begin_cleanup``, ``cir.begin_catch``, and ``cir.eh.dispatch``
+operations.
+
+.. code-block::
+
+    cir.eh.dispatch %eh_token : !cir.eh_token [
+      catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb6
+      catch_all : ^bb7
+    ]
+
+    cir.eh.dispatch %eh_token : !cir.eh_token [
+      catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb6
+      unwind : ^bb7
+    ]
+
+The ``cir.eh.dispatch`` operation behaves similarly to the LLVM IR switch
+instruction. It takes as an argument a token that was returned by a
+previous ``cir.eh.initiate`` operation. It then has a list of key-value
+pairs, where the key is either a type identifier, the keyword catch_all,
+or the keyword unwind and the value is a block to which execution should
+be transferred if the key is matched. Although the example above shows
+both the catch_all and unwind keyword, in practice only one or the other
+will be present, but the operation is required to have one of these
+values.
+
+When we are unwinding an exception with cleanups, the ``cir.eh.initiate``
+operation will be marked with the cleanup attribute and will be followed
+by a branch to the cleanup block, passing the EH token as an operand to
+the block. The cleanup block will begin with a call to
+``cir.begin_cleanup`` which returns a cleanup token.
+
+.. code-block::
+
+    ^bb4 (%eh_token : !cir.eh_token): 
+      %cleanup_token = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
+
+This is followed by the operations to perform the cleanup and then a
+cir.end_cleanup operation.
+
+.. code-block::
+
+    cir.end_cleanup(%cleanup_token : !cir.cleanup_token)
+
+Finally, the cleanup block either branches to a catch dispatch block or
+executes a ``cir.resume`` operation to continue unwinding the exception.
+
+When an exception is caught, the catch block will receive the eh token
+for the exception being caught as an argument. The ``cir.begin_catch``
+and ``cir.end_catch`` operations, described above in the high-level
+representation, continue to be used in the flattened form. In the
+flattened form, the ``eh_token`` argument to ``cir.begin_catch`` comes
+from the block argument rather than a region argument, and the
+``cir.end_catch`` operation appears directly in the catch block rather
+than within a ``cir.cleanup.scope`` cleanup region.
+
+Example: Try-catch with cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      try {
+        SomeClass c;
+        c.doSomething();
+      } catch (...) {
+        // Do nothing
+      }
+    }
+
+**High-level CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      cir.scope {
+        %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+        cir.try {
+          cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+          cir.cleanup.scope {
+            cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          } cleanup all {
+            cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.yield
+          }
+        } catch all (%eh_token : !cir.eh_token) {
+          %catch_token, %1 = cir.begin_catch %eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+          cir.cleanup.scope {
+            cir.yield
+          } cleanup eh {
+            cir.end_catch %catch_token
+            cir.yield
+          }
+          cir.yield
+        }
+      }
+      cir.return
+    }
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb2 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb8
+    ^bb3 // EH catch (from entry block)
+      %1 = cir.eh.initiate : !cir.eh_token
+      cir.br ^bb6(%1 : !cir.eh_token)
+    ^bb4 // EH cleanup (from ^bb1)
+      %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
+      cir.eh.dispatch %eh_token.1 : !cir.eh_token [
+        catch_all : ^bb7
+      ]
+    ^bb7(%eh_token.2 : !cir.eh_token)
+      %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb8
+    ^bb8 // Normal continue (from ^bb2 or ^bb6)
+      cir.return
+    }
+
+In this example, the normal cleanup is performed in a different block
+than the EH cleanup. This follows the pattern established by Clang's
+LLVM IR codegen. Only the EH cleanup requires ``cir.begin_cleanup`` and
+``cir.end_cleanup`` operations.
+
+If the ``SomeClass`` constructor throws an exception, it unwinds to an EH
+catch block (``^bb3``), which has excecutes a ``cir.eh.initiate`` operation
+before branching to a shared catch dispatch block (``^bb6``).
+
+If the ``doSomething()`` function throws an exception, it unwinds to an EH
+block ``^bb4`` that performs cleanup before branching to the shared catch
+dispatch block (``^bb5``).
+
+Example: Cleanup with unhandled exception
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    void someFunc() {
+      SomeClass c;
+      c.doSomething();
+    }
+
+**High-level CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.cleanup.scope {
+        cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      } cleanup all {
+        cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb4
+    ^bb2 // EH cleanup (from entry block)
+      %1 = cir.eh.initiate cleanup : !cir.eh_token
+      cir.br ^bb3(%1 : !cir.eh_token)
+    ^bb3(%eh_token : !cir.eh_token) // Perform cleanup
+      %2 = cir.begin_cleanup(%eh_token : !cir.eh_token) : !cir.cleanup_token
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.end_cleanup(%2 : !cir.cleanup_token)
+      cir.resume %eh_token : !cir.eh_token // Unwind to caller
+    ^bb4 // Normal continue (from ^bb1)
+      cir.return
+    }
+
+In this example, if ``doSomething()`` throws an exception, it unwinds to
+the EH cleanup block (``^bb2``), which branches to ``^bb3`` to perform the
+cleanup, but because we have no catch handler, we execute ``cir.resume``
+after the cleanup to unwind to the function that called ``someFunc()``.
+
+Throwing Calls in Cleanup Regions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When a call in an EH cleanup region may throw an exception, it requires
+special handling. The C++ standard requires that if an exception is
+thrown during exception cleanup (i.e., while unwinding a previous
+exception), the program must call ``std::terminate()``. In the flattened
+CIR, such calls are replaced with ``cir.try_call`` operations whose
+unwind destination contains a ``cir.eh.initiate`` followed by a
+``cir.eh.terminate`` operation.
+
+The ``cir.eh.terminate`` operation is a terminator that signals the need
+for program termination due to an exception thrown during cleanup. It
+takes the ``!cir.eh_token`` returned by ``cir.eh.initiate`` and is further
+processed during EH ABI lowering, where it is replaced with target-specific
+termination code.
+
+Example: Cleanup with throwing destructor
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    struct ThrowingDtor {
+      ~ThrowingDtor() noexcept(false);
+    };
+
+    void someFunc() {
+      ThrowingDtor c;
+      c.doSomething();
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_ThrowingDtor, !cir.ptr<!rec_ThrowingDtor>, ["c", init]
+      cir.call @_ZN12ThrowingDtorC1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+      cir.cleanup.scope {
+        cir.call @_ZN12ThrowingDtor11doSomethingEv(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+        cir.yield
+      } cleanup all {
+        cir.call @_ZN12ThrowingDtorD1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+        cir.yield
+      }
+      cir.return
+    }
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_ThrowingDtor, !cir.ptr<!rec_ThrowingDtor>, ["c", init]
+      cir.call @_ZN12ThrowingDtorC1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+      cir.try_call @_ZN12ThrowingDtor11doSomethingEv(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+    ^bb1 // Normal cleanup
+      cir.call @_ZN12ThrowingDtorD1Ev(%0) : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+      cir.br ^bb6
+    ^bb2 // EH cleanup (from entry block)
+      %1 = cir.eh.initiate cleanup : !cir.eh_token
+      cir.br ^bb3(%1 : !cir.eh_token)
+    ^bb3(%eh_token : !cir.eh_token) // Perform cleanup
+      %2 = cir.begin_cleanup(%eh_token : !cir.eh_token) : !cir.cleanup_token
+      cir.try_call @_ZN12ThrowingDtorD1Ev(%0) ^bb4, ^bb5 : (!cir.ptr<!rec_ThrowingDtor>) -> ()
+    ^bb4 // Destructor completed: continue unwinding
+      cir.end_cleanup(%2 : !cir.cleanup_token)
+      cir.resume %eh_token : !cir.eh_token
+    ^bb5 // Destructor threw: terminate
+      %3 = cir.eh.initiate : !cir.eh_token
+      cir.eh.terminate %3 : !cir.eh_token
+    ^bb6 // Normal continue (from ^bb1)
+      cir.return
+    }
+
+In this example, the destructor for ``ThrowingDtor`` may throw. In the
+normal cleanup path (``^bb1``), the destructor is a regular ``cir.call``
+since the exception would propagate normally. In the EH cleanup path
+(``^bb3``), the destructor call is a ``cir.try_call`` because if the
+destructor throws during exception unwinding, the program must
+terminate. If the destructor completes normally, the exception
+continues unwinding via ``cir.resume``. If the destructor throws, control
+transfers to ``^bb5``, which initiates exception handling and immediately
+terminates.
+
+Example: Shared cleanups
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+**C++**
+
+.. code-block:: c++
+
+    int someFunc() {
+      int i = 0;
+      while (true) {
+        SomeClass c;
+        if (i == 3)
+          continue;
+        if (i == 7)
+          break;
+        i = c.get();
+      }
+      return i;
+    }
+
+**CIR**
+
+.. code-block::
+
+    cir.func @someFunc() -> !s32i {
+      %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+      %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
+      %2 = cir.const #cir.int<0> : !s32i
+      cir.store align(4) %2, %1 : !s32i, !cir.ptr<!s32i>
+      cir.scope {
+        cir.while {
+          %5 = cir.const #true
+          cir.condition(%5)
+        } do {
+          cir.scope {
+            %5 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+            cir.call @_ZN9SomeClassC1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
+            cir.cleanup.scope {
+              cir.scope {
+                %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+                %8 = cir.const #cir.int<3> : !s32i
+                %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
+                cir.if %9 {
+                  cir.continue
+                }
+              }
+              cir.scope {
+                %7 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+                %8 = cir.const #cir.int<7> : !s32i
+                %9 = cir.cmp(eq, %7, %8) : !s32i, !cir.bool
+                cir.if %9 {
+                  cir.break
+                }
+              }
+              %6 = cir.call @_ZN9SomeClass3getEv(%5) : (!cir.ptr<!rec_SomeClass>) -> !s32i
+              cir.store align(4) %6, %1 : !s32i, !cir.ptr<!s32i>
+              cir.yield
+            } cleanup all {
+              cir.call @_ZN9SomeClassD1Ev(%5) : (!cir.ptr<!rec_SomeClass>) -> ()
+              cir.yield
+            }
+          }
+          cir.yield
+        }
+      }
+      %3 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+      cir.store %3, %0 : !s32i, !cir.ptr<!s32i>
+      %4 = cir.load %0 : !cir.ptr<!s32i>, !s32i
+      cir.return %4 : !s32i
+    }
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc() -> !s32i {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      %1 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__cleanup_dest_slot "]
+      %2 = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+      %3 = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init]
+      %4 = cir.const #cir.int<0> : !s32i
+      cir.store align(4) %4, %3 : !s32i, !cir.ptr<!s32i>
+      cir.br ^bb1
+    ^bb1:  // 3 preds: ^bb0, ^bb9, ^bb11
+      %5 = cir.const #true
+      cir.brcond %5 ^bb2, ^bb12
+    ^bb2:  // pred: ^bb1
+      cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb3
+    ^bb3:  // pred: ^bb2
+      %6 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
+      %7 = cir.const #cir.int<3> : !s32i
+      %8 = cir.cmp(eq, %6, %7) : !s32i, !cir.bool
+      cir.brcond %8 ^bb4, ^bb5
+    ^bb4:  // pred: ^bb3
+      // Set the destination slot and branch through cleanup
+      %9 = cir.const #cir.int<0> : !s32i
+      cir.store %9, %1 : !s32i, !cir.ptr<!s32i>
+      cir.br ^bb9
+    ^bb5:  // pred: ^bb3
+      %10 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
+      %11 = cir.const #cir.int<7> : !s32i
+      %12 = cir.cmp(eq, %10, %11) : !s32i, !cir.bool
+      cir.brcond %12 ^bb6, ^bb7
+    ^bb6:  // pred: ^bb5
+      // Set the destination slot and branch through cleanup
+      %13 = cir.const #cir.int<1> : !s32i
+      cir.store %13, %1 : !s32i, !cir.ptr<!s32i>
+      cir.br ^bb9
+    ^bb7:  // pred: ^bb5
+      %14 = cir.call @_ZN9SomeClass3getEv(%0) : (!cir.ptr<!rec_SomeClass>) -> !s32i
+      cir.store align(4) %14, %3 : !s32i, !cir.ptr<!s32i>
+      cir.br ^bb8
+    ^bb8: // pred: ^bb7
+      // Set the destination slot and branch through cleanup
+      %15 = cir.const #cir.int<2> : !s32i
+      cir.store %15, %1 : !s32i, !cir.ptr<!s32i>
+      cir.br ^bb9
+    ^bb9: // pred
+      // Shared cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      %16 = cir.load align(4) %1 : !cir.ptr<!s32i>, !s32i
+      cir.switch.flat %16 : !s32i, ^bb10 [
+        0: ^bb1  // continue
+        1: ^bb12 // break
+        2: ^bb11 // end of loop
+      ]
+    ^bb10:  // preds: ^bb9
+      cir.unreachable
+    ^bb11:  // pred: ^bb9
+      cir.br ^bb1
+    ^bb12:  // pred: ^bb1
+      %17 = cir.load align(4) %3 : !cir.ptr<!s32i>, !s32i
+      cir.store align(4) %17, %2 : !s32i, !cir.ptr<!s32i>
+      %18 = cir.load align(4) %2 : !cir.ptr<!s32i>, !s32i
+      cir.return %18 : !s32i
+    }
+
+In this example we have a cleanup scope inside the body of a while loop,
+and multiple instructions that may exit the loop body with different
+destinations. For simplicity, the example is shown without exception
+handling.
+
+When any of the conditions that exit a loop iteration occur (continue,
+break, or completion of an iteration), we set a cleanup destination slot
+to a unique value and branch to a shared normal cleanup block. That
+block performs the cleanup and then compares the cleanup destination
+slot value to the set of expected constants and branches to the
+corresponding destination.
+
+For example, when the continue instruction is reached, we set the
+cleanup destination slot (``%1``) to zero, branch to the shared cleanup
+block (``^bb9``), which calls the ``SomeClass`` destructor, then uses
+``cir.switch.flat`` to switch on the cleanup destination slot value and,
+finding it to be zero, branches to the loop condition block (``^bb1``).
+
+If none of the expected values is matched, the ``cir.switch.flat``
+branches to a block with a ``cir.unreachable`` operation. This corresponds
+to the behavior of Clang's LLVM IR codegen.
+
+ABI Lowering
+============
+
+A new pass will be introduced to lower the flattened representation to
+lower the ABI-agnostic flattened CIR representation to an ABI-specific
+form. This will be a separate pass from the main CXXABI lowering pass,
+which runs before CFG flattening. The ABI lowering pass will introduce
+personality functions and ABI-specific exception handling operations.
+
+This new pass will make use of the ``cir::CXXABI`` interface class and
+ABI-specific subclasses, but it will introduce a new set of interface
+methods for use with the exception handling ABI.
+
+For each supported exception handling ABI, the operations and function
+calls used will have a direct correspondence to the LLVM IR instructions
+and runtime library functions used for that ABI. The LLVM IR exception
+handling model is described in detail here: `LLVM Exception
+Handling <https://llvm.org/docs/ExceptionHandling.html>`__.
+
+A personality function attribute will be added to functions that require
+it during the ABI lowering phase.
+
+Itanium ABI Lowering
+--------------------
+
+The Itanium exception handling ABI representation replaces the
+``cir.eh.initiate`` and ``cir.eh.dispatch`` operations with a
+``cir.eh.landingpad`` operation and a series of ``cir.compare`` and
+``cir.brcond`` operations to model the correct handling based on type IDs
+for the catch handlers. The ``cir.begin_cleanup`` and ``cir.end_cleanup``
+operations are simply dropped. The ``cir.begin_catch`` operation becomes a
+call to ``__cxa_begin_catch``. The ``cir.end_catch`` operation becomes a
+call to ``__cxa_end_catch``. The ``cir.eh.terminate`` operation becomes a
+call to ``__clang_call_terminate`` (which calls ``__cxa_begin_catch``
+followed by ``std::terminate()``) and then an unreachable operation.
+
+The only operation that is specific to Itanium exception handling is
+``cir.eh.landingpad``.
+
+.. code-block::
+
+    %exn_ptr_0, %type_id = cir.eh.landingpad [@_ZTISt9exception] : !cir.ptr<!void>, !u32i
+
+This operation corresponds directly to the LLVM IR landingpad
+instruction. It may have a list of type IDs that the handler can catch
+(or null for "catch all") or it may have the cleanup attribute if the
+handler performs cleanup but does not catch any exceptions.
+
+Example: Try-catch with cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb2 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb8
+    ^bb3 // EH catch (from entry block)
+      %1 = cir.eh.initiate : !cir.eh_token
+      cir.br ^bb6(%1 : !cir.eh_token)
+    ^bb4 // EH cleanup (from ^bb1)
+      %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
+      cir.eh.dispatch %eh_token.1 : !cir.eh_token [
+        catch_all : ^bb7
+      ]
+    ^bb7(%eh_token.2 : !cir.eh_token)
+      %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb8
+    ^bb8 // Normal continue (from ^bb2 or ^bb6)
+      cir.return
+    }
+
+**ABI-lowered CIR**
+
+.. code-block::
+
+    cir.func @someFunc() #personality_fn = @__gxx_personality_v0 {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb2 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb8
+    ^bb3 // EH catch (from entry block)
+      %exn, %type_id = cir.eh.landingpad [null] : (!cir.ptr<!void>, !u32i)
+      cir.br ^bb6(%exn, &type_id : !cir.ptr<!void>, !u32i)
+    ^bb4 // EH cleanup (from ^bb1)
+      %exn.1, %type_id.1 = cir.eh.landingpad cleanup [null] : (!cir.ptr<!void>, !u32i)
+      cir.br ^bb5(%exn, %type_id : !cir.ptr<!void>, !u32i)
+    ^bb5(%1: !cir.ptr<!void>, %2: !u32i)
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb6(%1, %2 : !cir.ptr<!void>, !u32i)
+    ^bb6(%3: !cir.ptr<!void>, %4: !u32i) // Catch dispatch (from ^bb3 or ^bb4)
+      cir.br ^bb7(%3, %4 : !cir.ptr<!void>, !u32i)
+    ^bb7(%5: !cir.ptr<!void>, %6: !u32i) // Catch all handler
+      %7 = cir.call @__cxa_begin_catch(%5 : !cir.ptr<!void>)
+      cir.call @__cxa_end_catch()
+      cir.br ^bb8
+    ^bb8 // Normal continue (from ^bb2 or ^bb6)
+      cir.return
+    }
+
+In this example, if an exception is thrown by the ``SomeClass``
+constructor, it unwinds to a landing pad block (``^bb3``), which branches
+to the shared catch dispatch block (``^bb6``), which branches to the catch
+all handler block (``^bb7``). The catch all handler calls
+``__cxa_begin_catch`` and ``__cxa_end_catch`` and then continues to the
+normal continuation block (``^bb8``).
+
+Example: Try-catch with multiple catch handlers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      cir.try_call @f() ^bb1, ^bb2
+    ^bb1
+      cir.br ^bb7
+    ^bb2 // EH catch (from entry block)
+      %1 = cir.eh.initiate : !cir.eh_token
+      cir.br ^bb3(%1 : !cir.eh_token)
+    ^bb3(%eh_token : !cir.eh_token) // Catch dispatch (from ^bb2)
+      cir.eh.dispatch %eh_token : !cir.eh_token [
+        catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb4
+        catch (#cir.global_view<@_ZTIf> : !u32i) : ^bb5
+        catch_all : ^bb6
+      ]
+    ^bb4(%eh_token.1 : !cir.eh_token) // Catch handler for int exception
+      %catch.token = cir.begin_catch(%eh_token.1 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb5(%eh_token.2 : !cir.eh_token) // Catch handler for float exception
+      %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb6(%eh_token.3 : !cir.eh_token) // Catch all handler
+      %catch.token = cir.begin_catch(%eh_token.3 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb7 // Normal continue (from ^bb1, ^bb4, ^bb5, or ^bb6)
+      cir.return
+    }
+
+**ABI-lowered CIR**
+
+.. code-block::
+
+    cir.func @someFunc() #personality_fn = @__gxx_personality_v0 {
+      cir.try_call @f() ^bb1, ^bb2
+    ^bb1
+      cir.br ^bb8
+    ^bb2 // EH catch (from entry block)
+      %exn, %type_id = cir.eh.landingpad [null] : (!cir.ptr<!void>, !u32i)
+      cir.br ^bb3(%exn, &type_id : !cir.ptr<!void>, !u32i)
+    ^bb3(%0: !cir.ptr<!void>, %1: !u32i) // Catch compare for int exception
+      %2 = cir.eh.typeid @_ZTIi : !u32i
+      %3 = cir.cmp(eq, %1, %2) : !u32i, !cir.bool
+      cir.brcond %3 ^bb4(%0 : !cir.ptr<!void>), ^bb5(%0, %1 : !cir.ptr<!void>, !u32i)
+    ^bb4(%4: !cir.ptr<!void>, %5: !u32i) // Catch all handler for int exception
+      %6 = cir.call @__cxa_begin_catch(%4 : !cir.ptr<!void>)
+      cir.call @__cxa_end_catch()
+      cir.br ^bb8
+    ^bb5(%7: !cir.ptr<!void>, %8: !u32i) // Catch compare for float exception
+      %9 = cir.eh.typeid @_ZTIf : !u32i
+      %10 = cir.cmp(eq, %8, %9) : !u32i, !cir.bool
+      cir.brcond %10 ^bb7(%7 : !cir.ptr<!void>), ^bb8(%7 : !cir.ptr<!void>)
+    ^bb6(%11: !cir.ptr<!void>, %12: !u32i) // Catch all handler for float exception
+      %13 = cir.call @__cxa_begin_catch(%11 : !cir.ptr<!void>)
+      cir.call @__cxa_end_catch()
+      cir.br ^bb8
+    ^bb7(%14: !cir.ptr<!void>) // Catch all handler
+      %15 = cir.call @__cxa_begin_catch(%14 : !cir.ptr<!void>)
+      cir.call @__cxa_end_catch()
+      cir.br ^bb8
+    ^bb8 // Normal continue (from ^bb1, ^bb4, ^bb6, or ^bb7)
+      cir.return
+    }
+
+In this example, if an exception is thrown by the ``f()`` call, it unwinds
+to a landing pad block (``^bb2``), which uses the ``cir.eh.landingpad``
+operation to capture the exception pointer and its type id, then branches
+to ``^bb3`` to begin searching for a catch handler that handles the type id
+of the exception. Each catch handler simply consumes the exception by
+calling ``__cxa_begin_catch`` and ``__cxa_end_catch`` and then continues to
+the normal continuation block (``^bb8``).
+
+Microsoft C++ ABI Lowering
+--------------------------
+
+The Microsoft C++ exception handling ABI representation drops the
+``cir.eh.initiate`` operation and replaces the ``cir.eh.dispatch`` operation
+with ``cir.eh.catchswitch`` operation. The ``cir.begin_cleanup`` and
+``cir.end_cleanup`` operations are replaced with ``cir.cleanuppad`` and
+``cir.cleanupret`` respectively, and the ``cir.begin_catch`` and
+``cir.end_catch`` operations are replaced with ``cir.catchpad`` and
+``cir.catchret``.
+
+Each of these operations corresponds directly to a similarly named
+instruction in LLVM IR and have the same semantics. The first operation
+in the unwind destination of a ``cir.try_call`` must be either
+``cir.eh.catchswitch`` or ``cir.cleanuppad``.
+
+.. code-block::
+
+    %4 = cir.eh.catchswitch within none [^bb2, ^bb3] unwind to caller
+
+The ``cir.eh.catchswitch`` operation takes an operand which specifies the
+parent token, which may either be none or the token returned by a
+previous ``cir.catchpad`` operation. This is followed by a list of blocks
+which contain catch handlers. Each block in this list must begin with a
+``cir.catchpad`` operation. Finally, the unwind destination is provided to
+specify where excution continues if the exception is not caught by any
+of the handlers, with unwind to caller indicating that the unwind is not
+handled further in the current function. This operation returns a token
+that is used as the operand for ``cir.catchpad`` operations associated
+with this switch.
+
+.. code-block::
+
+    %5 = cir.cleanuppad within none []
+
+The ``cir.cleanuppad`` operation takes an operand which specifies the
+parent token, which may either be none or the token returned by a
+previous ``cir.catchpad`` operation. This is followed by a arguments
+required by the personality function. In the case of C++ exception
+handlers, the personality function will be ``__CxxFrameHandler3`` and the
+argument list will be empty. This operation returns a token that is used
+as the operand for the associated ``cir.cleanupret`` operation.
+
+.. code-block::
+
+    cir.cleanupret from %5 unwind to ^bb7
+
+The ``cir.cleanupret`` operation takes an operand which specifies the
+``cir.cleanuppad`` operation which is completed by this operation and a
+block at which unwinding of the current exception continues (or unwind
+to caller if there is no catch handling in the current function).
+
+.. code-block::
+
+    %8 = cir.catchpad within %4 [ptr @"??_R0H at 8", i32 0, ptr %e]
+
+The ``cir.catchpad`` operation takes an operand which specifies the parent
+token, which must have been return by a previous ``cir.catchswitch``
+operation. This is followed by a list of arguments, beginning with the
+typeid for the type of exception being caught (or null for catch all),
+followed by a type info flag value, followed by a pointer to the
+in-flight exception. This operation returns a token that is used as the
+operand for the associated ``cir.catchret`` operation or as the parent for
+any ``cir.catchswitch`` or ``cir.cleanuppad`` operations that are nested
+within this catch handler.
+
+.. code-block::
+
+    cir.catchret from %8 to ^bb8
+
+The ``cir.catchret`` operation takes an operand which specifies the
+``cir.catchpad`` operation which is completed by this operation and a
+block at which excution should be resumed.
+
+Example: Try-catch with cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc() {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb2 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb8
+    ^bb3 // EH catch (from entry block)
+      %1 = cir.eh.initiate : !cir.eh_token
+      cir.br ^bb6(%1 : !cir.eh_token)
+    ^bb4 // EH cleanup (from ^bb1)
+      %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 @_ZN9SomeClassD1Ev(%0) : (!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) // Catch dispatch (from ^bb3 or ^bb4)
+      cir.eh.dispatch %eh_token.1 : !cir.eh_token [
+        catch_all : ^bb7
+      ]
+    ^bb7(%eh_token.2 : !cir.eh_token)
+      %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb8
+    ^bb8 // Normal continue (from ^bb2 or ^bb6)
+      cir.return
+    }
+
+**ABI-lowered CIR**
+
+.. code-block::
+
+    cir.func @someFunc() #personality_fn = @ __CxxFrameHandler3 {
+      %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+      cir.try_call @_ZN9SomeClassC1Ev(%0) ^bb1, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb1
+      cir.try_call @_ZN9SomeClass11doSomethingEv(%0) ^bb2, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
+    ^bb2 // Normal cleanup
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.br ^bb6
+    ^bb3 // EH cleanup (from ^bb1)
+      %1 = cir.cleanuppad within none : !cir.cleanup_token
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+      cir.cleanupret from %1 unwind to ^bb4
+    ^bb4 // Catch dispatch (from ^bb3 or ^bb4)
+      %2 = cir.catchswitch within none [^bb5] unwind to caller
+    ^bb5
+      %catch.token = cir.catchpad within %2 [null : !cir.ptr<!void>] : !cir.catch_token
+      cir.catchret within %catch.token to ^bb6
+    ^bb6 // Normal continue (from ^bb2 or ^bb6)
+      cir.return
+    }
+
+Example: Try-catch with multiple catch handlers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**Flattened CIR**
+
+.. code-block::
+
+    cir.func @someFunc(){
+      cir.try_call @f() ^bb1, ^bb2
+    ^bb1
+      cir.br ^bb7
+    ^bb2 // EH catch (from entry block)
+      %1 = cir.eh.initiate : !cir.eh_token
+      cir.br ^bb3(%1 : !cir.eh_token)
+    ^bb3(%eh_token : !cir.eh_token) // Catch dispatch (from ^bb2)
+      cir.eh.dispatch %eh_token : !cir.eh_token [
+        catch (#cir.global_view<@_ZTIi> : !u32i) : ^bb4
+        catch (#cir.global_view<@_ZTIf> : !u32i) : ^bb5
+        catch_all : ^bb6
+      ]
+    ^bb4(%eh_token.1 : !cir.eh_token) // Catch handler for int exception
+      %catch.token = cir.begin_catch(%eh_token.1 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb5(%eh_token.2 : !cir.eh_token) // Catch handler for float exception
+      %catch.token = cir.begin_catch(%eh_token.2 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb6(%eh_token.3 : !cir.eh_token) // Catch all handler
+      %catch.token = cir.begin_catch(%eh_token.3 : !cir.eh_token) : !cir.catch_token
+      cir.end_catch(%catch.token : !cir.catch_token)
+      cir.br ^bb7
+    ^bb7 // Normal continue (from ^bb1, ^bb4, ^bb5, or ^bb6)
+      cir.return
+    }
+
+**ABI-lowered CIR**
+
+.. code-block::
+
+    cir.func @someFunc() #personality_fn = @__CxxFrameHandler3 {
+      cir.try_call @f() ^bb1, ^bb2
+    ^bb1
+      cir.br ^bb6
+    ^bb2 // EH catch (from entry block)
+      %0 = cir.catchswitch within none [^bb3, ^bb4, ^bb5] unwind to caller
+    ^bb3(%0: !cir.ptr<!void>) // Catch handler for int exception
+      %1 = cir.catchpad within %0 [eh.typeid @"??_R0H at 8", 0, %0 : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
+      cir.catchret from %1 to ^bb6
+    ^bb4(%2: !cir.ptr<!void>) // Catch compare for float exception
+      %2 = cir.catchpad within %0 [eh.typeid @"??_R0M at 8", 0, %0 : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
+      cir.catchret from %2 to ^bb6
+    ^bb5(%3: !cir.ptr<!void>) // Catch all handler
+      %4 = cir.catchpad within %0 [null, 64, null : (!cir.ptr<!void>, !u32i, !cir.ptr<!void>)] : !cir.catch_token
+      cir.catchret from %4 to ^bb6
+    ^bb6 // Normal continue (from ^bb1, ^bb3, ^bb4, or ^bb5)
+      cir.return
+    }
+
+In this example, if an exception is thrown by the ``f()`` call, it unwinds
+to a catch dispatch block (``^bb2``), which uses the ``cir.catchswitch``
+operation to dispatch to a catch handler (``^bb3``, ``^bb4``, or ``^bb5``)
+based on the type id of the exception. The actual comparisons in this
+case will be handled by the personality function, using tables that are
+generated from the ``cir.catchpad`` operations. Each catch handler simply
+continues to the normal continuation block (``^bb6``) using the
+``cir.catchret`` operation.



More information about the cfe-commits mailing list