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 to annotate your generated IR so the optimizer can make better decisions — inlining, alias analysis, vectorization, branch layout, and more.
Correct attributes are one of the highest-ROI improvements you can make to a frontend:
nounwind eliminates EH cleanup code on every call.nocapture on parameters enables escape analysis and alias analysis.noundef enables constant folding and value-range propagation.Set once per function, before or after emitting its body:
// Never throws — most important; enables call-site EH cleanup elision
F->addFnAttr(llvm::Attribute::NoUnwind);
// Reads no memory (pure function)
F->addFnAttr(llvm::Attribute::ReadNone);
// Reads memory but never writes
F->addFnAttr(llvm::Attribute::ReadOnly);
// Only accesses memory via its own arguments (no globals)
F->addFnAttr(llvm::Attribute::ArgMemOnly);
// Does not recurse (direct or indirect)
F->addFnAttr(llvm::Attribute::NoRecurse);
// Always returns (no infinite loops, no exit() calls)
F->addFnAttr(llvm::Attribute::WillReturn);
// Never returns (e.g., panic(), abort()) — always emit unreachable after call
F->addFnAttr(llvm::Attribute::NoReturn);
// Rarely called — deprioritize in layout
F->addFnAttr(llvm::Attribute::Cold);
// On a call site (not function definition):
CI->addFnAttr(llvm::Attribute::NoUnwind);llvm::AttrBuilder AB(Ctx);
// Pointer not stored past the call (enables escape analysis)
AB.addAttribute(llvm::Attribute::NoCapture);
// Pointer does not alias any other pointer in the call (like C restrict)
AB.addAttribute(llvm::Attribute::NoAlias);
// Pointer is never null
AB.addAttribute(llvm::Attribute::NonNull);
// Safe to dereference N bytes through this pointer
AB.addDereferenceableAttr(8);
// Value is never undef or poison
AB.addAttribute(llvm::Attribute::NoUndef);
// Attach to parameter index 0 (0-indexed)
F->addParamAttrs(0, AB);
// Return attributes (e.g., returned pointer is never null)
llvm::AttrBuilder RetAB(Ctx);
RetAB.addAttribute(llvm::Attribute::NonNull);
RetAB.addDereferenceableAttr(16);
F->addRetAttrs(RetAB);| Attribute | Impact |
|---|---|
NoCapture | Enables escape analysis; critical for alias analysis quality |
NoAlias | Like restrict — unlocks reordering and vectorization |
NonNull | Eliminates null checks on the parameter |
Noundef | Enables more aggressive constant propagation |
Dereferenceable(N) | Allows speculative loads without null checks |
Attach !llvm.loop to the back-edge branch (terminator of the loop latch block):
#include "llvm/IR/MDBuilder.h"
llvm::LLVMContext &Ctx = M.getContext();
// Back-edge branch of the loop
llvm::BranchInst *BackBr =
llvm::cast<llvm::BranchInst>(LoopLatchBB->getTerminator());
// Build hint nodes
llvm::MDNode *VecEnable = llvm::MDNode::get(Ctx, {
llvm::MDString::get(Ctx, "llvm.loop.vectorize.enable"),
llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(llvm::Type::getInt1Ty(Ctx), 1))
});
llvm::MDNode *UnrollCount = llvm::MDNode::get(Ctx, {
llvm::MDString::get(Ctx, "llvm.loop.unroll.count"),
llvm::ConstantAsMetadata::get(
llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 4))
});
// Build the loop MDNode — first operand MUST be the node itself (self-reference)
llvm::MDNode *LoopMD = llvm::MDNode::get(Ctx, {nullptr, VecEnable, UnrollCount});
LoopMD->replaceOperandWith(0, LoopMD); // replace placeholder with self
BackBr->setMetadata(llvm::LLVMContext::MD_loop, LoopMD);| Key | Value | Effect |
|---|---|---|
llvm.loop.vectorize.enable | i1 1 | Force vectorization on |
llvm.loop.vectorize.enable | i1 0 | Disable vectorization |
llvm.loop.vectorize.width | i32 N | Hint SIMD width |
llvm.loop.unroll.count | i32 N | Unroll N times |
llvm.loop.unroll.disable | i1 1 | Disable unrolling |
llvm.loop.interleave.count | i32 N | Interleave N iterations |
Guide block layout and branch prediction:
#include "llvm/IR/MDBuilder.h"
llvm::MDBuilder MDB(Ctx);
// CondBr weights: TrueWeight : FalseWeight
llvm::MDNode *Weights = MDB.createBranchWeights(/*True=*/100, /*False=*/1);
llvm::BranchInst *Br = B.CreateCondBr(Cond, ThenBB, ElseBB);
Br->setMetadata(llvm::LLVMContext::MD_prof, Weights);
// SwitchInst: one weight per case including default (order: default first)
llvm::MDNode *SwitchW = MDB.createBranchWeights({10, 90, 5});
SI->setMetadata(llvm::LLVMContext::MD_prof, SwitchW);TBAA lets the optimizer prove that accesses to different types cannot alias:
#include "llvm/IR/MDBuilder.h"
llvm::MDBuilder MDB(Ctx);
// Create a type tree (do once, cache the nodes)
llvm::MDNode *Root = MDB.createTBAARoot("MyLang TBAA");
llvm::MDNode *IntTy = MDB.createTBAANode("int", Root);
llvm::MDNode *FloatTy = MDB.createTBAANode("float", Root);
// Create access tags (type, type, offset)
llvm::MDNode *IntTag = MDB.createTBAAStructTagNode(IntTy, IntTy, 0);
llvm::MDNode *FloatTag = MDB.createTBAAStructTagNode(FloatTy, FloatTy, 0);
// Tag loads/stores
llvm::LoadInst *LI = B.CreateLoad(B.getInt32Ty(), IntPtr);
LI->setMetadata(llvm::LLVMContext::MD_tbaa, IntTag);
llvm::StoreInst *SI = B.CreateStore(FVal, FloatPtr);
SI->setMetadata(llvm::LLVMContext::MD_tbaa, FloatTag);
// Optimizer now knows: int stores can never alias float stores!range, !nonnull, !align// !range — loaded integer is in [lo, hi)
llvm::Type *I32 = B.getInt32Ty();
llvm::MDNode *Range = llvm::MDNode::get(Ctx, {
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(I32, 0)),
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(I32, 256))
});
LI->setMetadata(llvm::LLVMContext::MD_range, Range);
// !nonnull — loaded pointer is never null
LI->setMetadata(llvm::LLVMContext::MD_nonnull,
llvm::MDNode::get(Ctx, {}));
// !align / setAlignment — pointer alignment (prefer setAlignment)
LI->setAlignment(llvm::Align(8));| Function kind | Attributes to set |
|---|---|
| Pure math function (no I/O) | nounwind, readnone, willreturn |
| Read-only accessor | nounwind, readonly |
| Allocator returning new memory | nounwind, noalias on return |
| Panic / error handler | nounwind, noreturn |
| Pointer parameter not stored | nocapture on param |
| Pointer param not null | nonnull on param |
readnone or readonly on functions that actually write memory — the optimizer will incorrectly eliminate writes.nounwind on functions that call longjmp or throw — EH tables will be silently omitted.noundef on values you know are fully initialized — it enables value-range propagation.nocapture on pointer parameters your language guarantees don't escape — it is the most impactful single attribute for alias analysis.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