mcpField
L
llvm-passes

llvm-passes

github.com/mohitmishra786Development269300 installsUpdated 18w ago
Repository

About llvm-passes

A skill for developers writing LLVM optimization and IR transformation passes with the New Pass Manager. It covers plugin registration, analyses, testing, and debugging.

Key features

  1. 01Write FunctionPass and ModulePass
  2. 02Register and run pass plugins
  3. 03Use control-flow and alias analyses
  4. 04Modify IR and maintain analysis validity
  5. 05Test with llvm-lit and FileCheck
  6. 06Inspect, verify, and debug passes

Use cases

  1. 01Add a custom optimization to an LLVM compiler
  2. 02Implement inlining, dead-code elimination, or lowering
  3. 03Analyze loops and control flow
  4. 04Diagnose pass ordering or IR errors

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill llvm-passes

After installing, type/llvm-passesin Claude Code to confirm it loaded.

Details

From the project README

LLVM Passes

Purpose

Guide agents through writing LLVM optimization passes with the New Pass Manager: FunctionPass and ModulePass structure, PassPluginLibraryInfo registration, running via opt -load-pass-plugin, common analysis utilities (DominatorTree, LoopInfo, AliasAnalysis), IR modification patterns, llvm-lit testing, and debugging with opt -print-after-all.

When to Use

  • Adding a custom optimization to an LLVM-based compiler
  • Writing an IR transformation pass (inlining, DCE, custom lowering)
  • Analyzing control flow with dominator trees or loop info
  • Testing passes with FileCheck and llvm-lit
  • Debugging pass ordering and IR corruption
  • Integrating passes into Clang via plugin

Workflow

1. New Pass Manager architecture
opt / clang
├── ModulePassManager
│   └── FunctionPassManager (per function)
│       └── FunctionPass instances
└── AnalysisManager (cached analyses)

Passes declare analysis usage; analyses are invalidated on IR mutation.

2. Minimal FunctionPass (C++ plugin)
// 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. Running with 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 example
struct MyModulePass : PassInfoMixin<MyModulePass> {
    PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) {
        for (Function &F : M) {
            if (F.isDeclaration()) continue;
            // module-level transformation
        }
        return PreservedAnalyses::none();
    }
};

Register on ModulePassManager in plugin callback.

5. Analysis utilities
#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();
}

Declare analyses used:

AnalysisUsage MyLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
    AU.addRequired<DominatorTreeWrapperPass>();
    AU.addRequired<LoopInfoWrapperPass>();
    return AU;
}

(New PM: analyses requested via AM.getResult<> — dependency auto-tracked.)

6. IR modification patterns
// 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));

Always update SSA and invalidate analyses after structural changes.

7. llvm-lit testing
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. Debugging passes
# 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

Common Problems

SymptomCauseFix
Pass not found: my-passPlugin not loaded or name mismatchCheck registerPipelineParsingCallback name
Verify module failedBroken SSA after transformRun -verify-each; check use-def chains
Analysis staleModified IR without invalidationReturn PreservedAnalyses::none()
Plugin load errorLLVM version mismatchBuild against same LLVM as opt
Empty opt outputForgot -SAdd -S for textual IR
lit test FAILCHECK line mismatchopt -S and compare manually

Related Skills

  • skills/compilers/llvm — LLVM IR inspection and opt pipeline as user
  • skills/compiler-internals/compiler-frontend — IR generation feeding passes
  • skills/compiler-internals/jit-compilation — ORC JIT running optimized IR
  • skills/compiler-internals/mlir — MLIR passes (similar concepts)
  • skills/compilers/clang — Clang optimization flags
  • skills/build-systems/cmake — building pass plugins

Comments

Markdown supported