Skip to main content

llvm-optimization

Implement or analyze LLVM optimization passes, pipelines, and generated-code quality. Use for IR transformation and pass behavior, not general application performance tuning.

Source facts

Repository
NeverSight/NeverD
Last source activity
September 13, 2026 at 11:12
Detected SKILL.md language
English
Stars
77
Forks
14

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
llvm-optimization
description
Implement or analyze LLVM optimization passes, pipelines, and generated-code quality. Use for IR transformation and pass behavior, not general application performance tuning.
# LLVM Optimization Skill This skill covers LLVM optimization infrastructure, pass development, and performance tuning techniques. ## Optimization Pipeline Overview ### Pipeline Stages ``` Source → Frontend → LLVM IR → Optimization Passes → CodeGen → Machine Code ↓ [Transform Passes] [Analysis Passes] ``` ### Optimization Levels ```bash # No optimization clang -O0 source.c # Basic optimization (most optimizations enabled) clang -O1 source.c # Full optimization (aggressive inlining, vectorization) clang -O2 source.c # Maximum optimization (may increase code size) clang -O3 source.c # Size optimization clang -Os source.c # Optimize for size clang -Oz source.c # Aggressive size optimization ``` ## Core Optimization Passes ### Scalar Optimizations - **Constant Propagation**: Replace variables with known constant values - **Dead Code Elimination (DCE)**: Remove unreachable or unused code - **Common Subexpression Elimination (CSE)**: Avoid redundant computations - **Instruction Combining**: Merge multiple instructions into simpler forms - **Scalar Replacement of Aggregates (SROA)**: Break up aggregate allocations ### Loop Optimizations - **Loop Invariant Code Motion (LICM)**: Hoist invariant computations - **Loop Unrolling**: Duplicate loop body to reduce overhead - **Loop Vectorization**: Convert scalar loops to vector operations - **Loop Fusion/Fission**: Combine or split loops - **Induction Variable Simplification**: Optimize loop counters ### Interprocedural Optimizations - **Inlining**: Replace call sites with function body - **Dead Argument Elimination**: Remove unused function parameters - **Interprocedural Constant Propagation**: Propagate constants across functions - **Link-Time Optimization (LTO)**: Whole-program optimization ## Writing Custom Optimization Passes ### New Pass Manager (LLVM 13+) ```cpp #include "llvm/IR/PassManager.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" struct MyOptimizationPass : public llvm::PassInfoMixin<MyOptimizationPass> { llvm::PreservedAnalyses run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) { bool Changed = false; for (auto &BB : F) { for (auto &I : BB) { // Implement optimization logic if (optimizeInstruction(I)) { Changed = true; } } } if (Changed) return llvm::PreservedAnalyses::none(); return llvm::PreservedAnalyses::all(); } private: bool optimizeInstruction(llvm::Instruction &I) { // Example: Replace add x, 0 with x if (auto *BinOp = llvm::dyn_cast<llvm::BinaryOperator>(&I)) { if (BinOp->getOpcode() == llvm::Instruction::Add) { if (auto *C = llvm::dyn_cast<llvm::ConstantInt>(BinOp->getOperand(1))) { if (C->isZero()) { I.replaceAllUsesWith(BinOp->getOperand(0)); return true; } } } } return false; } }; // Plugin registration extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { return {LLVM_PLUGIN_API_VERSION, "MyOptPass", LLVM_VERSION_STRING, [](llvm::PassBuilder &PB) { PB.registerPipelineParsingCallback( [](llvm::StringRef Name, llvm::FunctionPassManager &FPM, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) { if (Name == "my-opt") { FPM.addPass(MyOptimizationPass()); return true; } return false; }); }}; } ``` ### Analysis Dependencies ```cpp struct MyAnalysis : public llvm::AnalysisInfoMixin<MyAnalysis> { using Result = MyAnalysisResult; Result run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) { // Compute analysis result return Result(); } static llvm::AnalysisKey Key; }; // Using analysis in a pass llvm::PreservedAnalyses run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) { auto &DT = FAM.getResult<llvm::DominatorTreeAnalysis>(F); auto &LI = FAM.getResult<llvm::LoopAnalysis>(F); auto &AA = FAM.getResult<llvm::AAManager>(F); // Use analysis results... } ``` ## Instruction Patterns ### Strength Reduction ```cpp // Replace expensive operations with cheaper ones // x * 2 → x << 1 // x / 4 → x >> 2 // x % 8 → x & 7 bool reduceStrength(llvm::BinaryOperator *BO) { if (BO->getOpcode() == llvm::Instruction::Mul) { if (auto *C = llvm::dyn_cast<llvm::ConstantInt>(BO->getOperand(1))) { if (C->getValue().isPowerOf2()) { unsigned Shift = C->getValue().exactLogBase2(); auto *Shl = llvm::BinaryOperator::CreateShl( BO->getOperand(0), llvm::ConstantInt::get(C->getType(), Shift)); BO->replaceAllUsesWith(Shl); return true; } } } return false; } ``` ### Algebraic Simplification ```cpp // x + 0 → x // x * 1 → x // x * 0 → 0 // x - x → 0 // x | x → x // x & 0 → 0 ``` ## Dominator Tree Usage ### Finding Optimization Opportunities ```cpp void optimizeWithDominators(llvm::Function &F, llvm::DominatorTree &DT) { // Use dominance for safe code motion for (auto &BB : F) { for (auto &I : BB) { if (auto *Load = llvm::dyn_cast<llvm::LoadInst>(&I)) { // Check if we can hoist this load if (canHoist(Load, DT)) { hoistInstruction(Load, DT); } } } } } bool canHoist(llvm::Instruction *I, llvm::DominatorTree &DT) { llvm::BasicBlock *DefBB = I->getParent(); // Check all uses are dominated for (auto *U : I->users()) { if (auto *UI = llvm::dyn_cast<llvm::Instruction>(U)) { if (!DT.dominates(DefBB, UI->getParent())) { return false; } } } return true; } ``` ## Loop Optimization Techniques ### Loop Analysis ```cpp void analyzeLoops(llvm::Function &F, llvm::LoopInfo &LI) { for (auto *L : LI) { // Get loop trip count if (auto *TC = L->getTripCount()) { llvm::errs() << "Trip count: " << *TC << "\n"; } // Check if loop is simple if (L->isLoopSimplifyForm()) { llvm::BasicBlock *Header = L->getHeader(); llvm::BasicBlock *Latch = L->getLoopLatch(); llvm::BasicBlock *Exit = L->getExitBlock(); } // Get induction variables llvm::PHINode *IV = L->getCanonicalInductionVariable(); } } ``` ### Loop Unrolling ```cpp // Manually trigger loop unrolling #pragma unroll 4 for (int i = 0; i < N; i++) { // Loop body will be unrolled 4x } // LLVM unroll metadata !llvm.loop.unroll.count = !{i32 4} ``` ## Vectorization ### Auto-Vectorization Hints ```cpp // Enable vectorization #pragma clang loop vectorize(enable) for (int i = 0; i < N; i++) { a[i] = b[i] + c[i]; } // Specify vector width #pragma clang loop vectorize_width(8) for (int i = 0; i < N; i++) { a[i] = b[i] * c[i]; } ``` ### SLP Vectorization Superword Level Parallelism - vectorize straight-line code: ```cpp // Before SLP a[0] = b[0] + c[0]; a[1] = b[1] + c[1]; a[2] = b[2] + c[2]; a[3] = b[3] + c[3]; // After SLP (conceptual) <4 x float> tmp = load <4 x float> b <4 x float> tmp2 = load <4 x float> c <4 x float> result = fadd tmp, tmp2 store result to a ``` ## Debugging Optimizations ### Viewing Pass Execution ```bash # Print passes being run opt -debug-pass-manager input.ll -O2 # Print IR after each pass opt -print-after-all input.ll -O2 # Print specific pass output opt -print-after=instcombine input.ll -O2 # Statistics opt -stats input.ll -O2 ``` ### Optimization Remarks ```bash # Enable all optimization remarks clang -Rpass=.* source.c # Specific remarks clang -Rpass=loop-vectorize source.c clang -Rpass-missed=inline source.c clang -Rpass-analysis=loop-vectorize source.c ``` ## Link-Time Optimization (LTO) ### Enabling LTO ```bash # Full LTO clang -flto source1.c source2.c -o program # Thin LTO (faster, parallel) clang -flto=thin source1.c source2.c -o program ``` ### LTO Benefits - Whole-program dead code elimination - Interprocedural constant propagation - Cross-module inlining - Better devirtualization ## Correctness Verification ### Alive2 Automatic verification of LLVM optimizations: ```bash # Verify transformation correctness alive-tv before.ll after.ll # Check specific optimization opt -instcombine input.ll | alive-tv input.ll - ``` ## NeverC Compiler-Internal Performance Optimization Pitfalls When optimizing NeverC's own compiler code (lexer, preprocessor, Sema), the following bug patterns have been observed and must be avoided. **Always check for these before committing any "performance refactoring" to the compiler codebase.** ### 1. Packed-Integer Endian Checks: Never Use `LLVM_IS_LITTLE_ENDIAN` `LLVM_IS_LITTLE_ENDIAN` is defined in `llvm/Support/SwapByteOrder.h`. NeverC `.cpp` files that don't transitively include it will silently evaluate `#if LLVM_IS_LITTLE_ENDIAN` as false, using big-endian encoding on a little-endian host. This produces silent data corruption (wrong key values in switch tables). **Rule**: Always use the compiler built-in instead: ```cpp // BAD — may be undefined, silently evaluates to 0 #if LLVM_IS_LITTLE_ENDIAN // GOOD — always available in GCC/Clang #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ``` **Files historically affected**: `IdentifierTable.cpp` (`getPPKeywordID`), `BuiltinString.cpp` (`isRuntimeFunctionName`), `ModuleLayout.cpp` (module name matching). ### 2. Token Kind Enum Range Checks: Values Are NOT Contiguous Token kinds (`tok::kw_void`, `tok::kw_int`, `tok::kw_enum`, etc.) are assigned values by the order they appear in `TokenKinds.def`, which includes non-keyword tokens interspersed. Their numeric values are **not contiguous or monotonically ordered by semantic group**. **Rule**: Never add range-guard optimizations like `if (Raw >= KwVoid && Raw <= KwEnum)` before a switch on token kinds. The switch statement itself is the correct dispatch — the compiler's jump-table optimization handles it. ```cpp // BAD — kw_int (84) < kw_void (99), so kw_int is excluded! unsigned KwVoid = static_cast<unsigned>(tok::kw_void); // 99 unsigned KwEnum = static_cast<unsigned>(tok::kw_enum); if (Raw >= KwVoid && Raw <= KwEnum) { switch(K) { ... } } // GOOD — let the compiler optimize the switch switch (K) { case tok::kw_void: case tok::kw_int: ... return true; default: return false; } ``` **File historically affected**: `RunParser.cpp` (`isIntrinsicTypeToken`) — broke headerless forward-declaration generation for `int`-returning functions. ### 3. Hand-Written Small memcpy: Use `std::memcpy` for <= 64 Bytes Custom overlapping-store patterns for small copies (`Dst[0]=Src[0]; Dst[Len>>1]=Src[Len>>1]; Dst[Len-1]=Src[Len-1]`) are fragile. For `Len=2`, the middle store `Dst[1]=Src[1]` is correct but `Dst[0]` may read a stale/wrong byte if the source is a scratch-buffer location with alignment padding. **Rule**: For sizes <= 64 bytes, always use `std::memcpy`. Only use SIMD for bulk copies > 64 bytes where the overhead is justified. ```cpp LLVM_ATTRIBUTE_ALWAYS_INLINE static void fastCopy(char *Dst, const char *Src, unsigned Len) { if (LLVM_LIKELY(Len <= 64)) { std::memcpy(Dst, Src, Len); // safe for all lengths return; } // SIMD path for large copies... } ``` **File historically affected**: `TokenScratch.cpp` — broke `#(42)` stringification producing `" 2"` instead of `"42"`. ### 4. Scratch Buffer Alignment: Don't Change Location Math
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub