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
Reference: LLVM 22 Release Notes | Source: llvmorg-22.1.2
This page documents the most impactful API and behavioral changes when migrating to LLVM 22 from earlier versions. Read this before starting any migration — see the version-sync skill for the step-by-step workflow.
Intrinsic::getDeclaration() removedgetDeclaration() is gone. Two replacements:
// Create or find an intrinsic declaration (most common — use this)
Function *F = Intrinsic::getOrInsertDeclaration(M, Intrinsic::memcpy,
{PtrTy, I64Ty});
// Look up only if it already exists — returns nullptr if not
Function *F = Intrinsic::getDeclarationIfExists(M, Intrinsic::memcpy);DbgInstPtr return typeinsertDeclare, insertDbgValueIntrinsic, insertDbgAssign, and insertLabel
now return DbgInstPtr instead of Instruction *:
// LLVM 22: DbgInstPtr = PointerUnion<Instruction *, DbgRecord *>
DbgInstPtr Result = DBuilder.insertDeclare(Alloca, DIVar, Expr, Loc, InsertPt);
// If you need to branch on which kind was returned:
if (auto *I = Result.dyn_cast<Instruction *>()) { /* ... */ }
if (auto *R = Result.dyn_cast<DbgRecord *>()) { /* ... */ }
// Most of the time you don't need to inspect the return — just ignore it
DBuilder.insertDeclare(Alloca, DIVar, DBuilder.createExpression(), Loc, InsertPt);Why DbgInstPtr? LLVM 22 completes the DbgRecord migration: debug intrinsics
(llvm.dbg.declare, llvm.dbg.value) can now be represented as non-instruction
DbgRecord nodes attached to instructions, separate from the instruction stream.
This improves optimization accuracy for debug info.
// insertDeclareValue — separate from insertDeclare
DbgInstPtr DBuilder.insertDeclareValue(Value *Val, DILocalVariable *Var,
DIExpression *Expr,
const DILocation *DL,
InsertPosition InsertPt);
// insertDbgAssign — assignment tracking (new in LLVM 22)
// Links a store instruction to its debug variable assignment
DbgInstPtr DBuilder.insertDbgAssign(Instruction *LinkedInstr, Value *Val,
DILocalVariable *SrcVar,
DIExpression *ValExpr, Value *Addr,
DIExpression *AddrExpr,
const DILocation *DL);
// createFunction: new UseKeyInstructions parameter (last, defaults false)
DISubprogram *DBuilder.createFunction(
DIScope *Scope, StringRef Name, StringRef LinkageName, DIFile *File,
unsigned LineNo, DISubroutineType *Ty, unsigned ScopeLine,
DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
// ... existing params ...
bool UseKeyInstructions = false // NEW in LLVM 22
);CreateGEP now accepts an optional GEPNoWrapFlags parameter:
// LLVM 22 signature:
Value *CreateGEP(Type *Ty, Value *Ptr, ArrayRef<Value *> IdxList,
const Twine &Name = "",
GEPNoWrapFlags NW = GEPNoWrapFlags::none()); // NEW
// Flags:
GEPNoWrapFlags::none() // default — no assumptions
GEPNoWrapFlags::inBounds() // same as CreateInBoundsGEP; UB if out of bounds
GEPNoWrapFlags::noUnsignedWrap() // pointer arithmetic doesn't unsigned-overflow
GEPNoWrapFlags::noSignedWrap() // pointer arithmetic doesn't signed-overflow
// CreateInBoundsGEP is still a convenience wrapper:
Value *CreateInBoundsGEP(Type *Ty, Value *Ptr, ArrayRef<Value *> IdxList,
const Twine &Name = "");llvm/ExecutionEngine/MCJIT.h and related classes are removed.
Use LLJIT or LLLazyJIT from llvm/ExecutionEngine/Orc/LLJIT.h.
Note: LLLazyJIT is declared in LLJIT.h — there is no separate LLLazyJIT.h.
// LLVM 22:
#include "llvm/ExecutionEngine/Orc/LLJIT.h" // contains both LLJIT and LLLazyJIT
auto JIT = ExitOnErr(LLJITBuilder().create());
auto LazyJIT = ExitOnErr(LLLazyJITBuilder().create());llvm/IR/LegacyPassManager.h, legacy::PassManager, legacy::FunctionPassManager,
FunctionPass, ModulePass base classes in llvm/Pass.h are still present in
LLVM 22 for backward compatibility and for TargetMachine::addPassesToEmitFile.
Do not use them for new pass development. All new passes must use NPM
(PassInfoMixin<T>, run(IRUnit &, AnalysisManager &), PreservedAnalyses).
// Still works in LLVM 22 (for codegen emission only):
legacy::PassManager PM;
TM->addPassesToEmitFile(PM, OS, nullptr, CodeGenFileType::ObjectFile);
PM.run(*M);
// New pass development — always use NPM:
class MyPass : public PassInfoMixin<MyPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
};getIndexTy() — new IRBuilder method// Get the integer type appropriate for pointer indexing in a given address space
IntegerType *IdxTy = B.getIndexTy(M->getDataLayout(), /*AddrSpace=*/0);
// Equivalent to DL.getIndexType(B.getPtrTy(AddrSpace))// All pointer construction:
Type *Ptr = B.getPtrTy(); // ptr (addrspace 0)
Type *Ptr = PointerType::get(Ctx, AS); // ptr addrspaceN
// LoadInst / StoreInst — explicit element type required:
B.CreateLoad(ElemTy, Ptr, "val");
B.CreateStore(Val, Ptr);
// GEP — explicit element type required:
B.CreateGEP(ElemTy, Ptr, Indices);
B.CreateInBoundsGEP(ElemTy, Ptr, Indices);llvm::Optional removed (since LLVM 17)// Before:
llvm::Optional<int> X = llvm::None;
// After:
std::optional<int> X = std::nullopt;| Old path | New path |
|---|---|
llvm/ADT/Triple.h | llvm/TargetParser/Triple.h |
llvm/Support/Host.h | llvm/TargetParser/Host.h |
llvm/Support/TargetRegistry.h | llvm/MC/TargetRegistry.h |
[ ] Intrinsic::getDeclaration() → getOrInsertDeclaration() or getDeclarationIfExists()
[ ] DIBuilder insertDeclare() → return type is now DbgInstPtr — update any Instruction* captures
[ ] DIBuilder createFunction() → new UseKeyInstructions param (last, default false — safe to ignore)
[ ] MCJIT → LLJIT / LLLazyJIT from llvm/ExecutionEngine/Orc/LLJIT.h
[ ] LLLazyJIT.h include → use LLJIT.h (LLLazyJIT is declared there)
[ ] Legacy PM for new passes → replace with PassInfoMixin + NPM
[ ] Opaque pointers → remove any getPointerElementType() calls
[ ] llvm::Optional → std::optional
[ ] llvm/ADT/Triple.h → llvm/TargetParser/Triple.h
[ ] llvm/Support/Host.h → llvm/TargetParser/Host.h
[ ] C++ standard → CMAKE_CXX_STANDARD 17 minimum
[ ] GEP inbounds → optionally adopt GEPNoWrapFlags for richer semantics
[ ] Rebuild and run all testsdocs
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