xref: /llvm-project/llvm/docs/SandboxIR.md (revision 485b1ac8a265dcf19c55a98aeefff95158cc63a2)
1# Sandbox IR: A transactional layer over LLVM IR
2
3Sandbox IR is an IR layer on top of LLVM IR that allows you to save/restore its state.
4
5# Quick Start Notes
6
7Within your LLVM pass:
8
9``` C++
10// 1. Include the necessary Sandbox IR header files.
11#include "llvm/SandboxIR/Context.h
12#include "llvm/SandboxIR/Function.h
13
14// 2. Create a sandboxir::Context using LLVMContext `LLVMCtx`.
15sandboxir::Context Ctx(LLVMCtx);
16
17// 3. Create a sandboxir::Function using LLVM IR Function `LLVMF`.
18auto *F = Ctx.createFunction(LLVMF);
19
20// ... Use Sandbox IR in `F` as usual, e.g., iterating, modifying it etc. ...
21
22// 4. Save state when needed.
23Ctx.save();
24
25// ... Modify Sandbox IR ...
26
27// 5. Revert to the saved state.
28Ctx.revert();
29```
30
31Make sure you link against `SandboxIR` in `CMakeLists.txt`:
32
33```
34LINK_COMPONENTS
35...
36SandboxIR
37...
38```
39
40# API
41The Sandbox IR API is designed to feel like LLVM, replicating many common API classes and functions to mirror the LLVM API.
42The class hierarchy is similar (but in the `llvm::sandboxir` namespace).
43For example here is a small part of it:
44```
45namespace sandboxir {
46              Value
47              /  \
48            User BasicBlock ...
49           /   \
50  Instruction Constant
51        /
52     ...
53}
54```
55
56# Design
57
58## Sandbox IR Value <-> LLVM IR Value Mapping
59Each LLVM IR Value maps to a single Sandbox IR Value.
60The reverse is also true in most cases, except for Sandbox IR Instructions that map to more than one LLVM IR Instruction.
61Such instructions can be defined in extensions of the base Sandbox IR.
62
63- Forward mapping: Sandbox IR Value -> LLVM IR Value
64Each Sandbox IR Value contains an `llvm::Value *Val` member variable that points to the corresponding LLVM IR Value.
65
66- Reverse mapping: LLVM IR Value -> Sandbox IR Value
67This mapping is stored in `sandboxir::Context::LLVMValueToValue`.
68
69For example `sandboxir::User::getOperand(OpIdx)` for a `sandboxir::User *U` works as follows:
70- First we find the LLVM User: `llvm::User *LLVMU = U->Val`.
71- Next we get the LLVM Value operand: `llvm::Value *LLVMOp = LLVMU->getOperand(OpIdx)`
72- Finally we get the Sandbox IR operand that corresponds to `LLVMOp` by querying the map in the Sandbox IR context: `retrun Ctx.getValue(LLVMOp)`.
73
74## Sandbox IR is Write-Through
75Sandbox IR is designed to rely on LLVM IR for its state.
76So any change made to Sandbox IR objects directly updates the corresponding LLVM IR.
77
78This has the following benefits:
79- It minimizes the replication of state, and
80- 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.
81- No need for serialization/de-serialization infrastructure as we can rely on LLVM IR for it.
82- One can pass actual `llvm::Instruction`s to cost modeling APIs.
83
84Sandbox IR API functions that modify the IR state call the corresponding LLVM IR function that modifies the LLVM IR's state.
85For example, for `sandboxir::User::setOperand(OpIdx, sandboxir::Value *Op)`:
86- We get the corresponding LLVM User: `llvm::User *LLVMU = cast<llvm::User>(Val)`
87- Next we get the corresponding LLVM Operand: `llvm::Value *LLVMOp = Op->Val`
88- Finally we modify `LLVMU`'s operand: `LLVMU->setOperand(OpIdx, LLVMOp)
89
90## IR Change Tracking
91Sandbox IR's state can be saved and restored.
92This is done with the help of the tracker component that is tightly coupled to the public Sandbox IR API functions.
93Please note that nested saves/restores are currently not supported.
94
95To save the state and enable tracking the user needs to call `sandboxir::Context::save()`.
96From 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.
97The changes are accumulated in a vector within the tracker.
98
99To rollback to the saved state the user needs to call `sandboxir::Context::revert()`.
100Reverting back to the saved state is a matter of going over all the accumulated changes in reverse and undoing each individual change.
101
102To accept the changes made to the IR the user needs to call `sandboxir::Context::accept()`.
103Internally this will go through the changes and run any finalization required.
104
105Please note that after a call to `revert()` or `accept()` tracking will stop.
106To start tracking again, the user needs to call `save()`.
107