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

via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 17 09:12:05 PDT 2026


Author: Sirui Mu
Date: 2026-04-18T00:12:00+08:00
New Revision: fce4a1eab3801c250eb6a271d0248a7b6293fa19

URL: https://github.com/llvm/llvm-project/commit/fce4a1eab3801c250eb6a271d0248a7b6293fa19
DIFF: https://github.com/llvm/llvm-project/commit/fce4a1eab3801c250eb6a271d0248a7b6293fa19.diff

LOG: [CIR][docs] Migrate existing Markdown documents to reStructuredText format (#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 .

Added: 
    clang/docs/CIR/ABILowering.rst
    clang/docs/CIR/CleanupAndEHDesign.rst

Modified: 
    

Removed: 
    clang/docs/CIR/ABILowering.md
    clang/docs/CIR/CleanupAndEHDesign.md


################################################################################
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 
diff erential 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 
diff erent rules for registers vs.
-memory), type-dependent (rules 
diff er 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 
diff icult 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 
diff erential 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 
diff erent rules for registers vs.
+memory), type-dependent (rules 
diff er 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 
diff icult 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
-
diff erent 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
-
diff erent 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 
diff erent 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 
diff erent
-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
+
diff erent 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
+
diff erent 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 
diff erent 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 
diff erent
+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