| # Sandbox IR: A transactional layer over LLVM IR | 
 |  | 
 | Sandbox IR is an IR layer on top of LLVM IR that allows you to save/restore its state. | 
 |  | 
 | # Quick Start Notes | 
 |  | 
 | Within your LLVM pass: | 
 |  | 
 | ``` C++ | 
 | // 1. Include the necessary Sandbox IR header files. | 
 | #include "llvm/SandboxIR/Context.h | 
 | #include "llvm/SandboxIR/Function.h | 
 |  | 
 | // 2. Create a sandboxir::Context using LLVMContext `LLVMCtx`. | 
 | sandboxir::Context Ctx(LLVMCtx); | 
 |  | 
 | // 3. Create a sandboxir::Function using LLVM IR Function `LLVMF`. | 
 | auto *F = Ctx.createFunction(LLVMF); | 
 |  | 
 | // ... Use Sandbox IR in `F` as usual, e.g., iterating, modifying it etc. ... | 
 |  | 
 | // 4. Save state when needed. | 
 | Ctx.save(); | 
 |  | 
 | // ... Modify Sandbox IR ... | 
 |  | 
 | // 5. Revert to the saved state. | 
 | Ctx.revert(); | 
 | ``` | 
 |  | 
 | Make sure you link against `SandboxIR` in `CMakeLists.txt`: | 
 |  | 
 | ``` | 
 | LINK_COMPONENTS | 
 | ... | 
 | SandboxIR | 
 | ... | 
 | ``` | 
 |  | 
 | # API | 
 | The Sandbox IR API is designed to feel like LLVM, replicating many common API classes and functions to mirror the LLVM API. | 
 | The class hierarchy is similar (but in the `llvm::sandboxir` namespace). | 
 | For example here is a small part of it: | 
 | ``` | 
 | namespace sandboxir { | 
 |               Value | 
 |               /  \ | 
 |             User BasicBlock ... | 
 |            /   \ | 
 |   Instruction Constant | 
 |         / | 
 |      ... | 
 | } | 
 | ``` | 
 |  | 
 | # Design | 
 |  | 
 | ## Sandbox IR Value <-> LLVM IR Value Mapping | 
 | Each LLVM IR Value maps to a single Sandbox IR Value. | 
 | The reverse is also true in most cases, except for Sandbox IR Instructions that map to more than one LLVM IR Instruction. | 
 | Such instructions can be defined in extensions of the base Sandbox IR. | 
 |  | 
 | - Forward mapping: Sandbox IR Value -> LLVM IR Value | 
 | Each Sandbox IR Value contains an `llvm::Value *Val` member variable that points to the corresponding LLVM IR Value. | 
 |  | 
 | - Reverse mapping: LLVM IR Value -> Sandbox IR Value | 
 | This mapping is stored in `sandboxir::Context::LLVMValueToValue`. | 
 |  | 
 | For example `sandboxir::User::getOperand(OpIdx)` for a `sandboxir::User *U` works as follows: | 
 | - First we find the LLVM User: `llvm::User *LLVMU = U->Val`. | 
 | - Next we get the LLVM Value operand: `llvm::Value *LLVMOp = LLVMU->getOperand(OpIdx)` | 
 | - Finally we get the Sandbox IR operand that corresponds to `LLVMOp` by querying the map in the Sandbox IR context: `retrun Ctx.getValue(LLVMOp)`. | 
 |  | 
 | ## Sandbox IR is Write-Through | 
 | Sandbox IR is designed to rely on LLVM IR for its state. | 
 | So any change made to Sandbox IR objects directly updates the corresponding LLVM IR. | 
 |  | 
 | This has the following benefits: | 
 | - It minimizes the replication of state, and | 
 | - It makes sure that Sandbox IR and LLVM IR are always in sync, which helps avoid bugs and removes the need for a lowering step. | 
 | - No need for serialization/de-serialization infrastructure as we can rely on LLVM IR for it. | 
 | - One can pass actual `llvm::Instruction`s to cost modeling APIs. | 
 |  | 
 | Sandbox IR API functions that modify the IR state call the corresponding LLVM IR function that modifies the LLVM IR's state. | 
 | For example, for `sandboxir::User::setOperand(OpIdx, sandboxir::Value *Op)`: | 
 | - We get the corresponding LLVM User: `llvm::User *LLVMU = cast<llvm::User>(Val)` | 
 | - Next we get the corresponding LLVM Operand: `llvm::Value *LLVMOp = Op->Val` | 
 | - Finally we modify `LLVMU`'s operand: `LLVMU->setOperand(OpIdx, LLVMOp) | 
 |  | 
 | ## IR Change Tracking | 
 | Sandbox IR's state can be saved and restored. | 
 | This is done with the help of the tracker component that is tightly coupled to the public Sandbox IR API functions. | 
 | Please note that nested saves/restores are currently not supported. | 
 |  | 
 | To save the state and enable tracking the user needs to call `sandboxir::Context::save()`. | 
 | From this point on any change made to the Sandbox IR state will automatically create a change object and register it with the tracker, without any intervention from the user. | 
 | The changes are accumulated in a vector within the tracker. | 
 |  | 
 | To rollback to the saved state the user needs to call `sandboxir::Context::revert()`. | 
 | Reverting back to the saved state is a matter of going over all the accumulated changes in reverse and undoing each individual change. | 
 |  | 
 | To accept the changes made to the IR the user needs to call `sandboxir::Context::accept()`. | 
 | Internally this will go through the changes and run any finalization required. | 
 |  | 
 | Please note that after a call to `revert()` or `accept()` tracking will stop. | 
 | To start tracking again, the user needs to call `save()`. | 
 |  | 
 | # Users of Sandbox IR | 
 | - [The Sandbox Vectorizer](project:SandboxVectorizer.md) |