Sandbox IR:LLVM IR 之上的事务层

Sandbox IR 是 LLVM IR 之上的一个 IR 层,允许您保存/恢复其状态。

快速入门注意事项

在您的 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();

请确保在 CMakeLists.txt 中链接 SandboxIR

LINK_COMPONENTS
...
SandboxIR
...

API

Sandbox IR API 的设计旨在感觉像 LLVM,复制了许多常见的 API 类和函数以镜像 LLVM API。类层次结构是相似的(但在 llvm::sandboxir 命名空间中)。例如,这里是它的一小部分

namespace sandboxir {
              Value
              /  \
            User BasicBlock ...
           /   \
  Instruction Constant
        /
     ...
}

设计

Sandbox IR Value <-> LLVM IR Value 映射

每个 LLVM IR Value 映射到一个 Sandbox IR Value。在大多数情况下,反之亦然,但 Sandbox IR 指令除外,这些指令映射到多个 LLVM IR 指令。此类指令可以在基本 Sandbox IR 的扩展中定义。

  • 正向映射:Sandbox IR Value -> LLVM IR Value 每个 Sandbox IR Value 都包含一个指向相应 LLVM IR Value 的 llvm::Value *Val 成员变量。

  • 反向映射:LLVM IR Value -> Sandbox IR Value 此映射存储在 sandboxir::Context::LLVMValueToValue 中。

例如,sandboxir::User::getOperand(OpIdx) 对于 sandboxir::User *U 的工作方式如下

  • 首先,我们找到 LLVM User:llvm::User *LLVMU = U->Val

  • 接下来,我们获取 LLVM Value 操作数:llvm::Value *LLVMOp = LLVMU->getOperand(OpIdx)

  • 最后,我们通过查询 Sandbox IR 上下文中的映射来获取与 LLVMOp 对应的 Sandbox IR 操作数:retrun Ctx.getValue(LLVMOp)

Sandbox IR 是直写式的

Sandbox IR 的设计旨在依赖 LLVM IR 来管理其状态。因此,对 Sandbox IR 对象所做的任何更改都会直接更新相应的 LLVM IR。

这具有以下优点

  • 它最大限度地减少了状态的复制,并且

  • 它确保 Sandbox IR 和 LLVM IR 始终保持同步,这有助于避免错误并消除了对降低步骤的需求。

  • 由于我们可以依赖 LLVM IR,因此无需序列化/反序列化基础设施。

  • 可以将实际的 llvm::Instruction 传递给成本建模 API。

修改 IR 状态的 Sandbox IR API 函数会调用相应的 LLVM IR 函数来修改 LLVM IR 的状态。例如,对于 sandboxir::User::setOperand(OpIdx, sandboxir::Value *Op)

  • 我们获取相应的 LLVM User:llvm::User *LLVMU = cast<llvm::User>(Val)

  • 接下来,我们获取相应的 LLVM Operand:llvm::Value *LLVMOp = Op->Val

  • 最后,我们修改 LLVMU 的操作数:`LLVMU->setOperand(OpIdx, LLVMOp)

IR 变更跟踪

Sandbox IR 的状态可以被保存和恢复。这是通过跟踪器组件完成的,该组件与公共 Sandbox IR API 函数紧密耦合。请注意,目前不支持嵌套的保存/恢复。

要保存状态并启用跟踪,用户需要调用 sandboxir::Context::save()。从那时起,对 Sandbox IR 状态所做的任何更改都将自动创建一个更改对象并将其注册到跟踪器,而无需用户的任何干预。更改累积在跟踪器内的向量中。

要回滚到保存的状态,用户需要调用 sandboxir::Context::revert()。恢复到保存的状态就是反向遍历所有累积的更改并撤消每个单独的更改。

要接受对 IR 所做的更改,用户需要调用 sandboxir::Context::accept()。在内部,这将遍历更改并运行任何需要的最终化操作。

请注意,在调用 revert()accept() 后,跟踪将停止。要再次开始跟踪,用户需要调用 save()