$npx -y skills add mohitmishra786/low-level-dev-skills --skill llvm-passesLLVM passes skill for writing compiler optimizations. Use when writing FunctionPass or ModulePass, registering PassPlugins, running with opt, using analysis utilities, or testing with llvm-lit. Activates on queries about LLVM pass, PassPlugin, opt -passes, DominatorTree, llvm-lit
| 1 | # LLVM Passes |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | 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`. |
| 6 | |
| 7 | ## When to Use |
| 8 | |
| 9 | - Adding a custom optimization to an LLVM-based compiler |
| 10 | - Writing an IR transformation pass (inlining, DCE, custom lowering) |
| 11 | - Analyzing control flow with dominator trees or loop info |
| 12 | - Testing passes with FileCheck and llvm-lit |
| 13 | - Debugging pass ordering and IR corruption |
| 14 | - Integrating passes into Clang via plugin |
| 15 | |
| 16 | ## Workflow |
| 17 | |
| 18 | ### 1. New Pass Manager architecture |
| 19 | |
| 20 | ``` |
| 21 | opt / clang |
| 22 | ├── ModulePassManager |
| 23 | │ └── FunctionPassManager (per function) |
| 24 | │ └── FunctionPass instances |
| 25 | └── AnalysisManager (cached analyses) |
| 26 | ``` |
| 27 | |
| 28 | Passes declare analysis usage; analyses are invalidated on IR mutation. |
| 29 | |
| 30 | ### 2. Minimal FunctionPass (C++ plugin) |
| 31 | |
| 32 | ```cpp |
| 33 | // MyPass.cpp |
| 34 | #include "llvm/IR/PassManager.h" |
| 35 | #include "llvm/Passes/PassBuilder.h" |
| 36 | #include "llvm/Passes/PassPlugin.h" |
| 37 | #include "llvm/Support/raw_ostream.h" |
| 38 | |
| 39 | using namespace llvm; |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | struct MyPass : PassInfoMixin<MyPass> { |
| 44 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { |
| 45 | bool changed = false; |
| 46 | for (BasicBlock &BB : F) { |
| 47 | for (Instruction &I : BB) { |
| 48 | if (auto *Call = dyn_cast<CallInst>(&I)) { |
| 49 | if (Call->getCalledFunction() && |
| 50 | Call->getCalledFunction()->getName() == "dead_func") { |
| 51 | Call->eraseFromParent(); |
| 52 | changed = true; |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | return changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); |
| 58 | } |
| 59 | }; |
| 60 | |
| 61 | } // namespace |
| 62 | |
| 63 | extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() { |
| 64 | return { |
| 65 | LLVM_PLUGIN_API_VERSION, "MyPass", "v0.1", |
| 66 | [](PassBuilder &PB) { |
| 67 | PB.registerPipelineParsingCallback( |
| 68 | [](StringRef Name, FunctionPassManager &FPM, |
| 69 | ArrayRef<PassBuilder::PipelineElement>) { |
| 70 | if (Name == "my-pass") { |
| 71 | FPM.addPass(MyPass()); |
| 72 | return true; |
| 73 | } |
| 74 | return false; |
| 75 | }); |
| 76 | } |
| 77 | }; |
| 78 | } |
| 79 | ``` |
| 80 | |
| 81 | ```cmake |
| 82 | # CMakeLists.txt |
| 83 | find_package(LLVM REQUIRED CONFIG) |
| 84 | add_library(MyPass MODULE MyPass.cpp) |
| 85 | target_include_directories(MyPass SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) |
| 86 | target_compile_definitions(MyPass PRIVATE ${LLVM_DEFINITIONS}) |
| 87 | llvm_map_components_to_libnames(llvm_libs core passes support) |
| 88 | target_link_libraries(MyPass PRIVATE ${llvm_libs}) |
| 89 | ``` |
| 90 | |
| 91 | ```bash |
| 92 | cmake -B build -DLLVM_DIR=$(llvm-config --cmakedir) |
| 93 | cmake --build build |
| 94 | ``` |
| 95 | |
| 96 | ### 3. Running with opt |
| 97 | |
| 98 | ```bash |
| 99 | # Run pass on IR file |
| 100 | opt -load-pass-plugin ./build/MyPass.so -passes=my-pass -S input.ll -o output.ll |
| 101 | |
| 102 | # Print IR after each pass |
| 103 | opt -load-pass-plugin ./build/MyPass.so -passes=my-pass -print-after-all input.ll -o /dev/null |
| 104 | |
| 105 | # Pass pipeline string |
| 106 | opt -passes="function(instcombine),my-pass,function(dce)" input.ll -S -o out.ll |
| 107 | ``` |
| 108 | |
| 109 | ### 4. ModulePass example |
| 110 | |
| 111 | ```cpp |
| 112 | struct MyModulePass : PassInfoMixin<MyModulePass> { |
| 113 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { |
| 114 | for (Function &F : M) { |
| 115 | if (F.isDeclaration()) continue; |
| 116 | // module-level transformation |
| 117 | } |
| 118 | return PreservedAnalyses::none(); |
| 119 | } |
| 120 | }; |
| 121 | ``` |
| 122 | |
| 123 | Register on `ModulePassManager` in plugin callback. |
| 124 | |
| 125 | ### 5. Analysis utilities |
| 126 | |
| 127 | ```cpp |
| 128 | #include "llvm/Analysis/DominatorTree.h" |
| 129 | #include "llvm/Analysis/LoopInfo.h" |
| 130 | #include "llvm/Analysis/AliasAnalysis.h" |
| 131 | |
| 132 | PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) { |
| 133 | auto &DT = AM.getResult<DominatorTreeAnalysis>(F); |
| 134 | auto &LI = AM.getResult<LoopAnalysis>(F); |
| 135 | auto &AA = AM.getResult<AAManager>(F); |
| 136 | |
| 137 | for (Loop *L : LI) { |
| 138 | BasicBlock *Header = L->getHeader(); |
| 139 | // Loop invariant code motion, etc. |
| 140 | } |
| 141 | |
| 142 | DominatorTreeNode *IDom = DT.getNode(&F.getEntryBlock()); |
| 143 | (void)IDom; |
| 144 | return PreservedAnalyses::all(); |
| 145 | } |
| 146 | ``` |
| 147 | |
| 148 | Declare analyses used: |
| 149 | |
| 150 | ```cpp |
| 151 | AnalysisUsage MyLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const { |
| 152 | AU.addRequired<DominatorTreeWrapperPass>(); |
| 153 | AU.addRequired<LoopInfoWrapperPass>(); |
| 154 | return AU; |
| 155 | } |
| 156 | ``` |
| 157 | |
| 158 | (New PM: analyses requested via `AM.getResult<>` — dependency auto-tracked.) |
| 159 | |
| 160 | ### 6. IR modification patterns |
| 161 | |
| 162 | ```cpp |
| 163 | // Insert instruction before iter |