jit-compilation について
JITコンパイルを実装するエージェント向けのスキルです。LLVM ORC/LLJIT、Cranelift、Rust dynasmに加え、遅延コンパイル、インラインキャッシュ、実行メモリの安全性を扱います。
主な機能
- 01LLVM ORC JIT v2とLLJITの解説
- 02Cranelift JITの設定
- 03トランポリンによる遅延コンパイル
- 04インラインキャッシュの設計
- 05W^XとJITのセキュリティ
- 06Rust dynasmによるx86コード生成
活用シーン
- 01インタープリターへのJITホットパス追加
- 02初回呼び出し時の関数コンパイル
- 03REPLやゲームエンジンへのコード生成の組み込み
- 04Rustによるx86コード片の生成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill jit-compilationインストール後、Claude Code で/jit-compilationを入力して読み込まれたか確認します。
詳細
JIT コンパイル
目的
ジャストインタイムコンパイルについてエージェントをガイドします。LLVM ORC JIT v2(ExecutionSession、IRLayer、ObjectLayer)、より簡単なユースケース向けの LLJIT、Cranelift JIT、動的ディスパッチ用のインラインキャッシュ、遅延コンパイル用のトランポリン、セキュリティ上の考慮事項(W^X、コード署名)、および x86 コード生成向けの Rust dynasm を扱います。
使用する場面
- JIT ホットパスを備えたインタープリターを構築する
- 初回呼び出し時に関数を遅延コンパイルする機能を実装する
- REPL やゲームエンジンに動的コード生成を組み込む
- 完全なリンカーを作成せずにコード生成のプロトタイプを作る
- ポリモーフィックな呼び出し箇所向けのインラインキャッシュを作成する
dynasmを使って Rust から x86 マシンコードを生成する
ワークフロー
1. JIT アーキテクチャの概要
Source/AST/Bytecode
→ IR (LLVM IR or Cranelift CLIF)
→ Object file (in memory)
→ Runtime linker (RTDyldObjectLinkingLayer)
→ Executable code in R+X memory
→ Function pointer call
2. LLJIT — 最もシンプルな LLVM JIT
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
using namespace llvm;
using namespace llvm::orc;
int main() {
auto JIT = cantFail(LLJITBuilder().create());
LLVMContext Context;
auto M = std::make_unique<Module>("jit", Context);
IRBuilder<> Builder(Context);
// int add(int a, int b) { return a + b; }
Function *AddFn = Function::Create(
FunctionType::get(Builder.getInt32Ty(),
{Builder.getInt32Ty(), Builder.getInt32Ty()}, false),
Function::ExternalLinkage, "add", M.get());
BasicBlock *BB = BasicBlock::Create(Context, "entry", AddFn);
Builder.SetInsertPoint(BB);
auto Args = AddFn->arg_begin();
Value *Sum = Builder.CreateAdd(Args, Args + 1);
Builder.CreateRet(Sum);
cantFail(JIT->addIRModule(ThreadSafeModule(std::move(M), std::make_unique<LLVMContext>())));
auto AddSym = JIT->lookup("add");
auto *AddPtr = (int (*)(int, int))AddSym->getValue();
int result = AddPtr(3, 4); // 7
return 0;
}
clang++ -std=c++17 jit.cpp $(llvm-config --cxxflags --ldflags --libs core orcjit native) -o jit
3. ORC JIT v2 のレイヤー
ExecutionSession ES;
auto &MainJD = ES.createBareJITDylib("main");
RTDyldObjectLinkingLayer ObjectLayer(
ES, []() { return std::make_unique<SectionMemoryManager>(); });
IRCompileLayer CompileLayer(
ES, ObjectLayer, std::make_unique<TargetMachineBuilder>());
// Add IR module to JITDylib
ThreadSafeModule TSM = ...;
CompileLayer.add(MainJD, std::move(TSM));
// Resolve symbol
auto Sym = ES.lookup({&MainJD}, "my_func");
レイヤー:
- IRLayer — LLVM IR をオブジェクトにコンパイルします
- ObjectLayer — 再配置可能なオブジェクトをリンクします
- ExecutionSession — シンボル検索と JITDylib の管理を行います
4. トランポリンによる遅延コンパイル
First call → trampoline → compile function → patch trampoline → direct call
// Simplified lazy compile on first invocation
void *LazyCompile(const std::string &Name) {
if (!Compiled.count(Name)) {
auto Fn = CompileFromAST(Name);
Compiled[Name] = Fn;
// Patch call site or update function pointer table
}
return Compiled[Name];
}
ORC は LazyCallThroughManager による遅延再エクスポートと遅延コンパイルをサポートします。
5. インラインキャッシュ
// Concept: monomorphic call site caches resolved target
// Pseudocode for dynamic language
function call_site(obj, method, args) {
if (obj.class_id === cache.class_id) {
return cache.fn_ptr(args); // fast path
}
cache.class_id = obj.class_id;
cache.fn_ptr = resolve_method(obj, method);
return cache.fn_ptr(args);
}
JIT はキャッシュされた型ごとに特化コードを生成し、キャッシュミス時には非最適化します。
6. Cranelift JIT
use cranelift::prelude::*;
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Linkage, Module};
let isa = cranelift_native::builder().finish(settings::Flags::new(settings::builder()))?;
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = JITModule::new(jit_builder);
let mut ctx = module.make_context();
ctx.func = Function::with_name_signature(
module.declare_function("add", Linkage::Export, &sig)?,
sig,
);
// ... build IR in ctx.func ...
module.define_function(func_id, &mut ctx)?;
module.finalize_definitions()?;
let code = module.get_finalized_function(func_id);
let add_fn: fn(i32, i32) -> i32 = unsafe { std::mem::transmute(code) };
Cranelift は LLVM よりコンパイル時間が短く、組み込み型 JIT に適しています。
7. セキュリティ — W^X ポリシー
W^X (Write XOR Execute)
├── Memory page is writable OR executable, never both
├── JIT: allocate RW → write code → mprotect(RX)
└── macOS hardened runtime requires signed JIT pages
#include <sys/mman.h>
void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// write machine code to mem
mprotect(mem, size, PROT_READ | PROT_EXEC);
Linux:Apple のプラットフォームでは MAP_JIT を使用し、セキュリティ強化システムでは sealed memfd を使用します。
8. Rust dynasm(x86)
use dynasm::dynasm;
use dynasmrt::{Assembler, ExecutableBuffer};
let mut asm = Assembler::new().unwrap();
dynasm!(asm
; .arch x86_64
; add:
; add eax, ecx
; ret
);
let buf = asm.finalize().unwrap();
let add_fn: fn(i32, i32) -> i32 = unsafe { std::mem::transmute(buf.ptr(0)) };
LLVM への依存なしで軽量なアセンブリ断片を使う場合に適しています。
9. 判断ツリー
JIT backend choice?
├── Need LLVM optimizations → ORC JIT / LLJIT
├── Fast compile, embeddable → Cranelift
├── Tiny asm snippets → dynasm
└── ML workloads → MLIR → ORC pipeline
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
| 検索時にシンボルが見つからない | 名前のマングリング、またはエクスポートされていない | C リンケージを使用します。LLVMExternalLinkage |
| JIT コードの呼び出し時にセグメンテーション違反が発生する | ABI の不一致 | 呼び出し規約と型を一致させます |
| W^X mmap に失敗する | SELinux/grsecurity | MAP_JIT を使用し、dmesg を確認します |
| 再コンパイル後に古いコードが実行される | 古い関数ポインター | キャッシュを無効化し、トランポリンを使用します |
| LLVM JIT のコンパイルが遅い | JIT での -O2 | JIT では -O0 を使用し、ホットパスのみ最適化します |
| Cranelift の検証エラー | 無効な CLIF | cranelift_codegen::verify_function を有効にします |
関連スキル
skills/compiler-internals/llvm-passes— JIT の前に最適化するskills/compiler-internals/compiler-frontend— JIT 入力向けに AST から IR に変換するskills/compiler-internals/mlir— JIT 向けに MLIR を LLVM に下げるskills/compilers/llvm— LLVM IR の基礎skills/low-level-programming/interpreters— JIT を使用するバイトコードインタープリターskills/low-level-programming/assembly-x86— 手書きアセンブリのコンテキスト
コメント
Markdown 対応