A Python 3 tool for compiling C++ files with custom LLVM optimization passes.
- Drop-In Replacement: Use as a transparent replacement for clang++ or g++ in existing build systems
- Incremental Configuration: Start with standard optimization levels (O0, O1, O2, O3, Os) and make targeted modifications
- Legacy Configuration: Full control with explicit optimization pass lists
- Pass Discovery: Comprehensive tools to explore and understand LLVM optimization passes
- LLVM Integration: Automatic LLVM tool detection with CXX environment variable support
- Flexible Configuration: YAML-based configuration with support for both incremental and legacy formats
- Robust Compilation: Multi-stage compilation pipeline (C++ → LLVM IR → Optimized IR → Executable)
# Use as direct replacement for clang++
./optclang_dropin.py source.cpp -o output -O2
# Create symlinks for transparent replacement
ln -sf /path/to/optclang_dropin.py /usr/local/bin/clang++
export CXX=/usr/local/bin/clang++
make # Works with any build system
# Use with custom configuration
OPTCLANG_CONFIG=my_config.yaml clang++ source.cpp -o output# Install dependencies
./setup.sh
# List all available optimization passes
./optclang_cli.py --list-passes
# See incremental differences between optimization levels
./optclang_cli.py --incremental-diff
# Compile with incremental configuration (O2 base + custom tweaks)
./optclang_cli.py examples/sample.cpp -c examples/incremental_config.yaml -o optimized_sample
# Compile with legacy configuration (explicit pass list)
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml -o basic_sample- Python 3.8 or higher
- LLVM/Clang toolchain
- PyYAML
# Clone or download the project
cd optclang
# Run the setup script
./setup.sh# Install dependencies
pip3 install -r requirements.txt
# Run tests to verify installation
PYTHONPATH=src python3 -m pytest tests/ -vOptClang can transparently replace clang++ or g++ in existing build systems:
# Direct usage as compiler replacement
./optclang_dropin.py source.cpp -o output -O2 -std=c++17
# Create symbolic links for system-wide usage
sudo ln -sf /path/to/optclang_dropin.py /usr/local/bin/clang++
export CXX=clang++
# Use with make, cmake, or any build system
make clean && make
# Configure with environment variables
OPTCLANG_CONFIG=examples/o3_custom.yaml make
OPTCLANG_VERBOSE=1 make # Enable verbose outputFor detailed drop-in usage instructions, see docs/dropin-replacement-guide.md.
# List all available optimization passes
./optclang_cli.py --list-passes
# List passes used in standard optimization levels
./optclang_cli.py --list-O1
./optclang_cli.py --list-O2
./optclang_cli.py --list-O3
# Show incremental differences between optimization levels
./optclang_cli.py --incremental-diff
# Basic compilation with output filename
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml -o optimized_sample
# Compilation with output directory (automatically uses input filename stem)
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml --output-dir ./build
# Multiple files to the same directory
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml --output-dir ./dist
./optclang_cli.py examples/my_program.cpp -c examples/advanced_config.yaml --output-dir ./dist
# Direct module execution
python3 src/optclang/main.py examples/sample.cpp -c examples/basic_config.yaml -o optimized_sample
# As a Python module
PYTHONPATH=src python3 -m optclang.main examples/sample.cpp -c examples/basic_config.yaml -o optimized_sample
# With custom LLVM installation
CXX=/usr/local/llvm/bin/clang++ ./optclang_cli.py examples/sample.cpp -c examples/advanced_config.yaml -o advanced_sample
# Verbose output
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml -vOptClang provides flexible output options to suit different workflows:
# Specify exact output filename
./optclang_cli.py source.cpp -c config.yaml -o my_program
./optclang_cli.py source.cpp -c config.yaml -o /path/to/executable# Place executable in specific directory (uses input filename stem)
./optclang_cli.py examples/sample.cpp -c config.yaml --output-dir ./build
# Creates: ./build/sample
./optclang_cli.py examples/my_program.cpp -c config.yaml --output-dir ./dist
# Creates: ./dist/my_program
# Directory is created automatically if it doesn't exist
./optclang_cli.py source.cpp -c config.yaml --output-dir ./new/nested/dir# No output option - uses input filename stem in current directory
./optclang_cli.py examples/sample.cpp -c config.yaml
# Creates: ./sampleNote: The --output and --output-dir options are mutually exclusive.
# Make the main script executable
chmod +x src/optclang/main.py
# Run directly (requires PYTHONPATH)
PYTHONPATH=src ./src/optclang/main.py examples/sample.cpp -c examples/basic_config.yaml -o optimized_sample
# Or use the provided CLI script
./optclang_cli.py examples/sample.cpp -c examples/basic_config.yaml -o optimized_sampleOptClang supports two configuration formats: legacy full pass lists and new incremental configurations.
Start with a base optimization level and apply incremental changes:
# Incremental configuration - Start with O2, then customize
base_optimization: "2"
incremental_changes:
- "-loop-vectorize" # Remove vectorization
- "-slp-vectorizer" # Remove SLP vectorization
- "+aggressive-instcombine" # Add aggressive instruction combining
compiler_flags:
- "-std=c++20"
- "-Wall"
linker_flags:
- "-lm"Base optimization levels:
"s"- Size optimization (77 passes, equivalent to-Os)"0"- No optimization (3 basic passes, equivalent to-O0)"1"- Basic optimization (75 passes, equivalent to-O1)"2"- Standard optimization (86 passes, equivalent to-O2)"3"- Aggressive optimization (88 passes, equivalent to-O3)
Incremental changes formats:
# Array format (recommended for readability)
incremental_changes:
- "-loop-vectorize"
- "+aggressive-instcombine"
- "-slp-vectorizer"
# Or string format (compact)
incremental_changes: "+mem2reg,-dce,+instcombine"Specify the complete list of optimization passes:
# Legacy configuration - Full pass list
optimization_passes:
- mem2reg
- instcombine
- simplifycfg
- dce
compiler_flags:
- "-std=c++17"
- "-Wall"
- "-Wextra"
linker_flags:
- "-lm"# Advanced legacy configuration with custom LLVM path
cxx_path: "/usr/local/llvm/bin/clang++"
optimization_passes:
- mem2reg
- sroa
- instcombine
- simplifycfg
- reassociate
- gvn
- dce
- adce
- loop-unroll
- licm
compiler_flags:
- "-std=c++20"
- "-Wall"
- "-Wextra"
- "-O0" # Start with no built-in optimization
linker_flags:
- "-lm"
- "-lpthread"base_optimization: Base optimization level ("s","0","1","2", or"3")incremental_changes: List or string of pass modifications ("+pass"to add,"-pass"to remove)compiler_flags: Additional flags for the compilation steplinker_flags: Additional flags for the linking stepcxx_path: Path to clang++ compiler (overrides CXX environment variable)
optimization_passes: List of LLVM optimization passes to applycompiler_flags: Additional flags for the compilation steplinker_flags: Additional flags for the linking stepcxx_path: Path to clang++ compiler (overrides CXX environment variable)
Incremental configuration advantages:
- Intuitive: Start with known optimization levels (O0, O1, O2, O3, Os)
- Maintainable: Only specify what you want to change
- Robust: Automatically handles duplicates and missing passes
- Flexible: Mix and match optimizations from different levels
Legacy configuration advantages:
- Explicit: Full control over the exact pass sequence
- Deterministic: Same results regardless of LLVM version differences
- Educational: See exactly which passes are being applied
To see all available optimization passes for your LLVM installation:
./optclang_cli.py --list-passesTo see which passes are used in standard optimization levels:
# List passes used in -O1
./optclang_cli.py --list-O1
# List passes used in -O2
./optclang_cli.py --list-O2
# List passes used in -O3
./optclang_cli.py --list-O3To see incremental differences between optimization levels:
# Show what passes are added/removed between O0→O1, O1→O2, O2→O3
./optclang_cli.py --incremental-diffThese commands will display:
- A numbered list of optimization passes
- Example configuration file snippet
- Equivalent clang++ optimization level
- Incremental changes: One-line comma-separated lists of added/removed passes
The incremental diff shows exactly what changes between optimization levels:
- O0 to O1: Adds ~64 basic optimization passes
- O1 to O2: Adds ~12 passes (vectorization, advanced opts), removes 1
- O2 to O3: Adds ~3 aggressive optimization passes
This helps you understand what optimizations are applied at each level and lets you create custom optimization pipelines based on standard levels.
Common optimization passes include:
mem2reg: Promote memory to registerinstcombine: Combine redundant instructionssimplifycfg: Simplify the control flow graphdce: Dead code eliminationgvn: Global value numberingsroa: Scalar replacement of aggregateslicm: Loop invariant code motionloop-unroll: Unroll loops
CXX: Path to the C++ compiler (default:clang++)
Run the test suite with pytest:
# Quick test with setup script
./setup.sh
# Manual testing
PYTHONPATH=src python3 -m pytest tests/
# Run tests with verbose output
PYTHONPATH=src python3 -m pytest -v tests/The examples/ directory contains sample configurations:
incremental_config.yaml: O2 base with vectorization removed and aggressive instcombine addedsize_opt_config.yaml: Size optimization base with loop unrolling and custom dead code eliminationo3_custom.yaml: O3 base with vectorization disabledo0_basic.yaml: O0 base with basic memory and instruction optimizations added
basic_config.yaml: Basic optimization configuration with essential passesadvanced_config.yaml: Advanced optimization with custom LLVM path and comprehensive passes
sample.cpp: Example C++ source file for testing (vector operations, loops, function calls)
- IR Generation: Compiles C++ source to LLVM IR using
clang++ -S -emit-llvm - Optimization: Applies specified optimization passes using
opt - Executable Generation: Compiles optimized IR to executable using
clang++
MIT License