强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

LLVM 开发指南 / 第 10 章:JIT 编译

第 10 章:JIT 编译

“JIT 编译让你在运行时生成和执行机器码,如同代码会自我进化。”


10.1 JIT 编译概述

10.1.1 什么是 JIT?

JIT(Just-In-Time)编译是在程序运行时将 IR 或字节码编译为机器码并执行的技术。

AOT (Ahead-of-Time) 编译:
  源码 → [编译器] → 可执行文件 → [运行]

JIT (Just-In-Time) 编译:
  源码/IR → [运行时] → 机器码 → [立即执行]
                ↑
        编译和执行在同一进程中

10.1.2 JIT 的应用场景

场景说明示例
交互式解释器REPL 环境Julia, Cling
数据库查询动态生成查询计划Spark, DuckDB
游戏引擎运行时脚本编译Unity (IL2CPP)
机器学习动态计算图PyTorch (TorchScript)
编译器开发动态编译 DSLMLIR JIT
单元测试测试 LLVM IR 生成LLVM 测试套件

10.1.3 LLVM JIT 框架演进

框架状态说明
MCJIT已废弃LLVM 3.5 引入,旧框架
ORC JIT v1已废弃LLVM 3.7+,Object-Linking
ORC JIT v2✅ 推荐LLVM 9+,当前推荐方案

10.2 ORC JIT 基础

10.2.1 核心概念

概念说明
JITDylib符号表,类似动态库
MaterializationUnit懒编译单元
ExecutionSessionJIT 会话管理
IRCompileLayerIR 编译层
ObjectLayer目标文件链接层
LLJIT简化的 JIT API

10.2.2 最简单的 ORC JIT 示例

// simple_jit.cpp
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Error.h"
#include <iostream>

using namespace llvm;
using namespace llvm::orc;

int main() {
    // 1. 初始化 LLVM 目标
    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();

    // 2. 创建 LLJIT 实例
    auto JIT = LLJITBuilder().create();
    if (!JIT) {
        errs() << "创建 JIT 失败: " << toString(JIT.takeError()) << "\n";
        return 1;
    }

    // 3. 创建 LLVM 模块
    auto Ctx = std::make_unique<LLVMContext>();
    auto M = std::make_unique<Module>("jit_module", *Ctx);

    // 4. 生成 IR: int add(int a, int b) { return a + b; }
    IRBuilder<> Builder(*Ctx);
    FunctionType *FuncType = FunctionType::get(
        Builder.getInt32Ty(),
        {Builder.getInt32Ty(), Builder.getInt32Ty()},
        false
    );
    Function *Func = Function::Create(
        FuncType, Function::ExternalLinkage, "add", M.get()
    );
    BasicBlock *Entry = BasicBlock::Create(*Ctx, "entry", Func);
    Builder.SetInsertPoint(Entry);
    Value *Sum = Builder.CreateAdd(Func->getArg(0), Func->getArg(1), "sum");
    Builder.CreateRet(Sum);

    // 5. 将模块添加到 JIT
    auto TSM = ThreadSafeModule(std::move(M), std::move(Ctx));
    if (auto E = (*JIT)->addIRModule(std::move(TSM))) {
        errs() << "添加模块失败: " << toString(std::move(E)) << "\n";
        return 1;
    }

    // 6. 查找并调用函数
    auto Addr = (*JIT)->lookup("add");
    if (!Addr) {
        errs() << "查找函数失败: " << toString(Addr.takeError()) << "\n";
        return 1;
    }

    // 7. 转换为函数指针并调用
    auto *AddFunc = Addr->toPtr<int(int, int)>();
    int Result = AddFunc(3, 4);
    std::cout << "add(3, 4) = " << Result << "\n";

    return 0;
}
# 编译
clang++ -std=c++17 simple_jit.cpp -o simple_jit \
    $(llvm-config --cxxflags --ldflags --libs core native orcjit support)

# 运行
./simple_jit
# 输出: add(3, 4) = 7

10.3 ORC JIT 进阶

10.3.1 从 LLVM IR 文件 JIT

// file_jit.cpp
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"

using namespace llvm;
using namespace llvm::orc;

int main(int argc, char **argv) {
    if (argc < 2) {
        errs() << "用法: " << argv[0] << " <input.ll>\n";
        return 1;
    }

    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();

    auto JIT = ExitOnErr(LLJITBuilder().create());

    // 加载 LLVM IR 文件
    auto Ctx = std::make_unique<LLVMContext>();
    SMDiagnostic Err;
    auto M = parseIRFile(argv[1], Err, *Ctx);
    if (!M) {
        Err.print(argv[0], errs());
        return 1;
    }

    // 添加模块
    ExitOnErr(JIT->addIRModule(
        ThreadSafeModule(std::move(M), std::move(Ctx))));

    // 查找 main 函数
    auto MainAddr = ExitOnErr(JIT->lookup("main"));

    // 调用
    auto *MainFunc = MainAddr.toPtr<int()>();
    int Result = MainFunc();
    outs() << "main() = " << Result << "\n";

    return 0;
}

10.3.2 添加外部符号

// 注册外部函数,使 JIT 代码可以调用
auto &MainJD = JIT->getMainJITDylib();

// 方式一:使用符号映射
MainJD.addGenerator(
    cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(
        JIT->getDataLayout().getGlobalPrefix())));

// 方式二:手动注册符号
SymbolMap M;
M[Mangle("printf")] = { 
    pointerToJITTargetAddress(&printf), 
    JITSymbolFlags::Exported 
};
M[Mangle("my_custom_func")] = {
    pointerToJITTargetAddress(&my_custom_func),
    JITSymbolFlags::Exported
};
ExitOnErr(MainJD.define(absoluteSymbols(std::move(M))));

10.3.3 懒编译 (Lazy Compilation)

// 懒编译:函数在首次调用时才编译
#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"

auto JIT = ExitOnErr(LLJITBuilder()
    .setCompileFunctionCreator([](JITTargetMachineBuilder TM)
        -> Expected<std::unique_ptr<IRCompileLayer::IRCompiler>> {
        return std::make_unique<TMOwningSimpleCompiler>(std::move(TM));
    })
    .setLazyCompile(true)  // 启用懒编译
    .create());

10.4 Kaleidoscope JIT

LLVM 教程中的 Kaleidoscope 语言使用 JIT 实现 REPL:

// 简化的 REPL 循环
while (true) {
    std::cout << "ready> ";
    getNextToken();

    switch (CurTok) {
    case tok_eof:
        return 0;
    case ';':        // 忽略分号
        getNextToken();
        continue;
    case tok_def:    // 函数定义
        HandleDefinition();
        break;
    case tok_extern: // 外部声明
        HandleExtern();
        break;
    default:         // 表达式
        HandleTopLevelExpression();
        break;
    }
}

// 处理顶层表达式 — JIT 编译并执行
void HandleTopLevelExpression() {
    auto F = ParseTopLevelExpr();
    if (F) {
        auto *CF = TheJIT->compileExpr(std::move(F));
        double Result = CF();  // JIT 编译并执行!
        std::cout << "结果: " << Result << "\n";
    }
}

10.5 MCJIT(已废弃)

注意: MCJIT 已废弃,仅供了解历史。新代码请使用 ORC JIT。

// MCJIT 示例(仅供参考)
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"

// 创建 MCJIT 引擎
auto EE = EngineBuilder(std::move(M))
    .setEngineKind(EngineKind::JIT)
    .setMCJITMemoryManager(std::make_unique<SectionMemoryManager>())
    .create();

// 获取函数地址并调用
auto *Func = (int(*)())EE->getPointerToFunction(F);
int Result = Func();

10.6 JIT 事件监听

#include "llvm/ExecutionEngine/JITEventListener.h"

// 注册 GDB JIT 接口(调试器支持)
EE->registerJITEventListener(
    JITEventListener::createGDBRegistrationListener());

// 注册 perf 事件监听(性能分析支持)
EE->registerJITEventListener(
    JITEventListener::createPerfJITEventListener());

// 自定义事件监听
class MyListener : public JITEventListener {
    void notifyObjectLoaded(ObjectKey K, const object::ObjectFile &Obj,
                            const RuntimeDyld::LoadedObjectInfo &L) override {
        outs() << "JIT 加载对象: " << Obj.getFileName() << "\n";
    }
    void notifyFreeingObject(ObjectKey K) override {
        outs() << "JIT 释放对象: " << K << "\n";
    }
};

10.7 JIT 调试

# JIT 调试技巧

# 1. 查看生成的汇编代码
# 设置环境变量输出 JIT 编译结果
LLVM_JIT_DUMP=1 ./my_jit_program

# 2. 使用 perf 查看 JIT 函数
perf record ./my_jit_program
perf report  # JIT 函数应该可见

# 3. 禁用 JIT 优化(调试)
# 在构建 LLJIT 时不设置优化层

# 4. 打印 IR
M->print(outs(), nullptr);  // 在添加到 JIT 前打印

10.8 本章小结

概念要点
ORC JIT推荐的 JIT 框架
LLJIT简化的 JIT API
JITDylib符号表
懒编译首次调用时编译
外部符号动态库符号注入
MCJIT已废弃,不要使用

扩展阅读

  1. ORC JIT Design — ORC JIT 设计文档
  2. Kaleidoscope Tutorial — JIT 教程
  3. Building a JIT — 构建 JIT 系列教程
  4. ORC API Reference — API 参考

下一章: 第 11 章:MC 层 — 学习 LLVM MC 层的汇编器、反汇编器和目标文件生成。