LLVM 22.x tile for building compilers, language runtimes, and out-of-tree tooling
88
83%
Does it follow best practices?
Impact
96%
1.23xAverage score across 5 eval scenarios
Passed
No known issues
Use this skill whenever the user wants to create a new optimization or analysis pass using the LLVM New Pass Manager (NPM). All passes in LLVM 22 use NPM — the legacy PassManager is removed.
Ask or infer the pass kind from the user's description:
| Kind | Unit of IR | Base class | Use when |
|---|---|---|---|
| Function | llvm::Function | Inherits nothing; run(Function &, FunctionAnalysisManager &) | Most transforms and analyses |
| Module | llvm::Module | run(Module &, ModuleAnalysisManager &) | Whole-program / cross-function |
| Loop | llvm::Loop | run(Loop &, LoopAnalysisManager &, LoopStandardAnalysisResults &, LPMUpdater &) | Loop transforms |
| CGSCC | llvm::LazyCallGraph::SCC | run(LazyCallGraph::SCC &, CGSCCAnalysisManager &, LazyCallGraph &, CGSCCUpdateResult &) | Interprocedural over SCCs |
For a transform pass use PreservedAnalyses as the return type.
For an analysis pass inherit from AnalysisInfoMixin<YourPass> and return a result struct.
Path (in-tree): include/llvm/Transforms/<Category>/<PassName>.h
Path (out-of-tree): include/<PassName>.h
//===- <PassName>.h - <One-line description> --------------------*- C++ -*-===//
// LLVM 22 New Pass Manager pass
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_<CATEGORY>_<PASSNAME>_H
#define LLVM_TRANSFORMS_<CATEGORY>_<PASSNAME>_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class <PassName> : public PassInfoMixin<<PassName>> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
// Required for opt to skip this pass on optnone functions.
static bool isRequired() { return false; }
};
} // namespace llvm
#endif // LLVM_TRANSFORMS_<CATEGORY>_<PASSNAME>_HReplace Function / FunctionAnalysisManager with the appropriate IR unit from Step 0.
Path (in-tree): lib/Transforms/<Category>/<PassName>.cpp
Path (out-of-tree): lib/<PassName>.cpp or src/<PassName>.cpp
//===- <PassName>.cpp - <One-line description> ----------------------------===//
// LLVM 22 New Pass Manager pass implementation
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/<Category>/<PassName>.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
PreservedAnalyses <PassName>::run(Function &F, FunctionAnalysisManager &FAM) {
// TODO: implement pass logic
// Return PA indicating which analyses are preserved.
// If nothing is modified:
return PreservedAnalyses::all();
// If everything may be modified:
// return PreservedAnalyses::none();
// If only the CFG is preserved:
// PA.preserveSet<CFGAnalyses>(); return PA;
}Key LLVM 22 APIs to know inside run():
FAM.getResult<DominatorTreeAnalysis>(F) — get a cached analysis result.FAM.invalidate(F, PA) — explicitly invalidate analyses after modification.llvm::make_range(F.begin(), F.end()) — iterate basic blocks.llvm::PatternMatch — match IR patterns without manual casting.opt pipeline strings (in-tree only)PassRegistry.defAdd one line in the appropriate section of llvm/lib/Passes/PassRegistry.def:
FUNCTION_PASS("<pass-name>", <PassName>())Use MODULE_PASS, LOOP_PASS, or CGSCC_PASS for other kinds.
PassBuilder.cpp includesAdd the header include near the other Transforms/<Category>/ includes in
llvm/lib/Passes/PassBuilder.cpp:
#include "llvm/Transforms/<Category>/<PassName>.h"No other changes needed — PassRegistry.def drives the registration.
If out-of-tree, register the pass with a PassPluginLibraryInfo entry point so
opt --load-pass-plugin can find it:
#include "llvm/Passes/PassPlugin.h"
llvm::PassPluginLibraryInfo get<PassName>PluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "<pass-name>", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "<pass-name>") {
FPM.addPass(<PassName>());
return true;
}
return false;
});
}};
}
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return get<PassName>PluginInfo();
}In lib/Transforms/<Category>/CMakeLists.txt, add the source file:
add_llvm_component_library(LLVM<Category>
...
<PassName>.cpp
...
)find_package(LLVM 22 REQUIRED CONFIG)
add_llvm_pass_plugin(<PassName>
<PassName>.cpp
DEPENDS
intrinsics_gen
)If add_llvm_pass_plugin is not available in your LLVM install, use:
add_library(<PassName> MODULE <PassName>.cpp)
target_include_directories(<PassName> PRIVATE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(<PassName> PRIVATE ${LLVM_DEFINITIONS})
set_target_properties(<PassName> PROPERTIES
CXX_STANDARD 17
POSITION_INDEPENDENT_CODE ON
)Path (in-tree): test/Transforms/<Category>/<pass-name>.ll
Path (out-of-tree): test/<pass-name>.ll
; RUN: opt -passes=<pass-name> -S %s | FileCheck %s
; RUN: opt -passes=<pass-name> -disable-output %s ; smoke test — must not crash
define i32 @test_basic(i32 %x) {
entry:
; CHECK-LABEL: @test_basic
; CHECK: <expected IR pattern after pass>
ret i32 %x
}For an out-of-tree plugin:
; RUN: opt --load-pass-plugin=%llvmshlibdir/<PassName>%pluginext \
; RUN: -passes=<pass-name> -S %s | FileCheck %sRun tests:
# In-tree
llvm-lit test/Transforms/<Category>/<pass-name>.ll
# Out-of-tree (after cmake --build)
llvm-lit test/<pass-name>.ll# In-tree
echo 'define void @f() { ret void }' | \
opt -passes=<pass-name> -S -
# Out-of-tree plugin
echo 'define void @f() { ret void }' | \
opt --load-pass-plugin=./<PassName>.so -passes=<pass-name> -S -Expected: IR printed without crash, with any expected transforms applied.
PreservedAnalyses::all() — no IR changed
PreservedAnalyses::none() — anything may have changed
PA.preserve<DominatorTreeAnalysis>() — CFG unchanged, DT still valid
PA.preserveSet<CFGAnalyses>() — all CFG-only analyses preserved
FAM.getResult<X>(F) — get (cached) analysis X for function F
FAM.getCachedResult<X>(F) — get only if already computed (nullable)
FAM.invalidate(F, PA) — tell manager what F's pass invalidated
FunctionPassManager FPM;
FPM.addPass(MyPass()); — build a pass pipeline manually
ModulePassManager MPM;
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));legacy::PassManager, FunctionPass, ModulePass for new passes — LLVM 22 requires NPM (PassInfoMixin) for new pass development (legacy types may still appear for codegen emission APIs).AU.setPreservesAll() or getAnalysis<>() — NPM uses FAM.getResult<>().PassInfoMixin<YourPass> — required for pass identification.using namespace llvm; in headers.docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
skills
add-alias-analysis
add-attributes-metadata
add-calling-convention
add-debug-info
add-exception-handling
add-gc-statepoints
add-intrinsic
add-lto
add-sanitizer
add-vectorization-hint
frontend-to-ir
jit-setup
lit-filecheck
lower-struct-types
new-target
out-of-tree-setup
tessl-llvm
version-sync