Files
TheRock/compiler/amd-llvm/llvm/docs/SandboxIR.md
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

4.8 KiB

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:

// 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::Instructions 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().

Sandbox IR supports nested checkpoints, meaning that you can save more than once and revert more than once. Conceptually each save() adds a new checkpoint to a stack and each revert() rolls back the IR state to that of the checkpoint at the top of the stack and pops the checkpoint off the stack. A call to accept() pops the last checkpoint from the stack. Reverting or accepting all can be done with revert(/*RevertAll=*/true) and accept(/*AcceptAll=*/true).

Users of Sandbox IR