llvm-passes について
New Pass Manager を使って LLVM の最適化や IR 変換を行う開発者向けのスキルです。プラグイン登録、解析、テスト、デバッグの手順を扱います。
主な機能
- 01FunctionPass と ModulePass の作成
- 02Pass プラグインの登録と実行
- 03制御フローとエイリアス解析の利用
- 04IR 変更と解析情報の管理
- 05llvm-lit と FileCheck によるテスト
- 06Pass の確認、検証、デバッグ
活用シーン
- 01LLVM コンパイラーへの独自最適化の追加
- 02インライン化、死コード除去、lowering の実装
- 03ループと制御フローの解析
- 04Pass 順序や IR エラーの調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill llvm-passesインストール後、Claude Code で/llvm-passesを入力して読み込まれたか確認します。
詳細
LLVM パス
目的
New Pass Manager を使った LLVM 最適化パスの作成方法として、FunctionPass と ModulePass の構成、PassPluginLibraryInfo の登録、opt -load-pass-plugin を使った実行、一般的な解析ユーティリティ(DominatorTree、LoopInfo、AliasAnalysis)、IR の変更パターン、llvm-lit によるテスト、opt -print-after-all を使ったデバッグについて、エージェント向けに説明します。
使用する場面
- LLVM ベースのコンパイラーにカスタム最適化を追加するとき
- IR 変換パス(インライン化、DCE、カスタムな低水準化)を作成するとき
- ドミネーターツリーやループ情報を使って制御フローを解析するとき
- FileCheck と llvm-lit でパスをテストするとき
- パスの順序や IR の破損をデバッグするとき
- プラグインを介して Clang にパスを統合するとき
ワークフロー
1. New Pass Manager のアーキテクチャ
opt / clang
├── ModulePassManager
│ └── FunctionPassManager (per function)
│ └── FunctionPass instances
└── AnalysisManager (cached analyses)
パスは使用する解析を宣言します。IR が変更されると、解析は無効になります。
2. 最小限の FunctionPass(C++ プラグイン)
// MyPass.cpp
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct MyPass : PassInfoMixin<MyPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
bool changed = false;
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (auto *Call = dyn_cast<CallInst>(&I)) {
if (Call->getCalledFunction() &&
Call->getCalledFunction()->getName() == "dead_func") {
Call->eraseFromParent();
changed = true;
}
}
}
}
return changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
};
} // namespace
extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "my-pass") {
FPM.addPass(MyPass());
return true;
}
return false;
});
}
};
}
# CMakeLists.txt
find_package(LLVM REQUIRED CONFIG)
add_library(MyPass MODULE MyPass.cpp)
target_include_directories(MyPass SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(MyPass PRIVATE ${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(llvm_libs core passes support)
target_link_libraries(MyPass PRIVATE ${llvm_libs})
cmake -B build -DLLVM_DIR=$(llvm-config --cmakedir)
cmake --build build
3. opt を使った実行
# Run pass on IR file
opt -load-pass-plugin ./build/MyPass.so -passes=my-pass -S input.ll -o output.ll
# Print IR after each pass
opt -load-pass-plugin ./build/MyPass.so -passes=my-pass -print-after-all input.ll -o /dev/null
# Pass pipeline string
opt -passes="function(instcombine),my-pass,function(dce)" input.ll -S -o out.ll
4. ModulePass の例
struct MyModulePass : PassInfoMixin<MyModulePass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
for (Function &F : M) {
if (F.isDeclaration()) continue;
// module-level transformation
}
return PreservedAnalyses::none();
}
};
プラグインのコールバックで ModulePassManager に登録します。
5. 解析ユーティリティ
#include "llvm/Analysis/DominatorTree.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/AliasAnalysis.h"
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &AA = AM.getResult<AAManager>(F);
for (Loop *L : LI) {
BasicBlock *Header = L->getHeader();
// Loop invariant code motion, etc.
}
DominatorTreeNode *IDom = DT.getNode(&F.getEntryBlock());
(void)IDom;
return PreservedAnalyses::all();
}
使用する解析を宣言します。
AnalysisUsage MyLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<DominatorTreeWrapperPass>();
AU.addRequired<LoopInfoWrapperPass>();
return AU;
}
(New PM では AM.getResult<> を使って解析を要求し、依存関係は自動的に追跡されます。)
6. IR の変更パターン
// Insert instruction before iterator
IRBuilder<> Builder(&*I.getIterator());
Value *NewVal = Builder.CreateAdd(I.getOperand(0), ConstantInt::get(I.getType(), 1));
I.replaceAllUsesWith(NewVal);
I.eraseFromParent();
// Clone basic block
BasicBlock *Clone = CloneBasicBlock(OrigBB, VMap, ".clone", &F);
// Create function
FunctionCallee Fn = M.getOrInsertFunction("my_fn",
FunctionType::get(Builder.getVoidTy(), false));
構造を変更した後は、必ず SSA を更新し、解析を無効にしてください。
7. llvm-lit によるテスト
test/
├── lit.cfg.py
└── my-pass.test
# my-pass.test
# RUN: opt -load-pass-plugin %shlibdir/MyPass.so -passes=my-pass -S %s | FileCheck %s
define void @test() {
call void @dead_func()
ret void
}
; CHECK-NOT: dead_func
llvm-lit test/my-pass.test -v
8. パスのデバッグ
# Verify IR after pass
opt -load-pass-plugin ./MyPass.so -passes=my-pass input.ll -o out.ll
opt -verify-each out.ll
# Time passes
opt -passes=my-pass -time-passes input.ll -o /dev/null
# Debug pass manager
OPT_DEBUG=1 opt -passes=my-pass input.ll -o /dev/null
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
Pass not found: my-pass | プラグインが読み込まれていない、または名前が一致していない | registerPipelineParsingCallback の名前を確認します |
| モジュールの検証に失敗する | 変換後に SSA が壊れている | -verify-each を実行し、use-def チェーンを確認します |
| 解析結果が古い | 無効化せずに IR を変更した | PreservedAnalyses::none() を返します |
| プラグインの読み込みエラー | LLVM のバージョンが一致していない | opt と同じ LLVM に対してビルドします |
| opt の出力が空 | -S を指定していない | テキスト形式の IR を出力するには -S を追加します |
| lit テストが FAIL になる | CHECK 行が一致しない | opt -S を実行し、手動で比較します |
関連スキル
skills/compilers/llvm— LLVM IR の調査とユーザーとしての opt パイプラインの利用skills/compiler-internals/compiler-frontend— パスに入力する IR の生成skills/compiler-internals/jit-compilation— 最適化済み IR を実行する ORC JITskills/compiler-internals/mlir— MLIR パス(類似の概念)skills/compilers/clang— Clang の最適化フラグskills/build-systems/cmake— パスプラグインのビルド
コメント
Markdown 対応