lccc

An optimized fork of CCC — Claude's C Compiler — with a linear-scan register allocator, hot-loop register steal, phi coalescing for registers and stack slots, SIMD auto-vectorization (AVX2 · NEON), and recursion-to-iteration transforms. Targets x86-64, AArch64, RISC-V 64, and i686.

Get Started View on GitHub
Benchmark LCCC GCC -O2 LCCC vs GCC
arith_loop0.035s0.030s1.15×
matmul0.0024s0.0029s1.17× faster
spectral_norm0.165s0.139s1.19×
fib(40)0.000s0.091s~600× faster
bitops0.092s0.169s1.8× faster
fannkuch3.34s1.81s1.85×
┌─────────────────────────────────────────────────────────────────────┐
  C source                                                           
     frontend: lex → parse → sema → IR lowering                     
                                                                      
  SSA IR                                                              
     optimizer: GVN · LICM · IPCP · DCE · const-fold · inline        
                                                                      
  Optimized IR                                                        
     regalloc (LCCC): linear scan over live intervals                  
       pools: callee-saved · caller-saved · FP/SIMD                    
       + post-scan steal for hot loop-carried values               
                                                                      
  Machine code  x86-64 · AArch64 · RISC-V 64 · i686                 
     standalone assembler + linker (no external toolchain)           
                                                                      
  ELF executable                                                      
└─────────────────────────────────────────────────────────────────────┘
Motivation

CCC is impressive. LCCC makes it faster.

CCC compiles real projects — SQLite, PostgreSQL, Redis, the Linux kernel — from a zero-dependency Rust codebase with its own assembler and linker. LCCC focuses on closing the performance gap with GCC by improving where CCC leaves the most on the table.

Linear-scan register allocator

Callee-saved, caller-saved, and FP/SIMD pools over live intervals — plus a post-scan steal that gives hot loop-carried values registers won by cold early starters.

🔩

Drop-in GCC replacement

Same flags, same output ABI. Point CC=lccc at any Makefile. No build system changes required.

🏗

Four architectures

x86-64, AArch64, RISC-V 64, i686. Architecture-agnostic PhysReg allocation; per-target SIMD (AVX2, NEON) and peephole pipelines.

🔭

536 tests passing

563 unit tests, 18/18 benchmark outputs byte-identical to GCC, and the SQLite amalgamation (260K lines) compiles and runs correctly.

Register allocation

Linear scan · register steal · phi coalescing · NEON/AVX2 vectorization

The old CCC allocator uses three greedy phases over a conservative eligibility whitelist. LCCC replaces the allocation core with a linear scan over live intervals, rebalances hot loop-carried values into registers after the scan (a conflict-safe steal, since in-scan eviction miscompiles), and coalesces loop-backedge copies onto the phi dest's register — or its stack slot when both are spilled, deleting the per-iteration load+store shuffle.

regalloc — post-scan steal for hot loop-carried values
// The scan assigns registers in start order, so cold function-spanning values
// (array bases, globals) win every callee-saved register. For each hot
// loop-carried phi value the scan MISSED:
for &(vid, hot_count) in &candidates {
    // pick the register whose CONFLICTING holders are coldest…
    // …and fully deallocate them to the stack. Whole-interval dealloc,
    // never range splitting — safe where in-scan eviction was not.
    assignments.insert(vid, stolen_reg);
}
stack layout — loop-backedge slot coalescing
// v_old = copy v_new  (backedge). If the phi-coalesce detector proves
// v_old is dead after v_new is defined, v_new borrows v_old's slot:
// the copy becomes a same-slot no-op — no ldr+str per iteration.
raw_aliases.push((backedge_src, phi_dest));  // certified by detect_phi_coalesce_groups
Performance

Benchmark results — LCCC vs GCC -O2 (AArch64)

Best-of-5 wall-clock time. All 18 outputs are byte-identical to GCC. Run with python3 tests/benchmark/run_benchmarks.py --reps 5.

Benchmark Description LCCC GCC -O2 LCCC vs GCC
arith_loop 32-var arithmetic, register pressure 0.034s 0.031s 1.12×
matmul 256×256 double matrix multiply 0.0025s 0.0029s 1.15× faster
spectral_norm FP dense loops 0.133s 0.133s 1.0× parity
fib(40) Recursive Fibonacci 0.000s 0.090s ~600× faster
bitops popcount / clz / bit reverse 0.089s 0.169s 1.9× faster
fannkuch Fannkuch-Redux permutations 2.010s 1.973s 1.02×

Across the full 18-benchmark suite LCCC is at 1.01× of GCC -O2 — statistical parity (0.49× geomean including the two ~600× recursion-to-iteration wins), with matmul, bitops, qsort and loop_patterns faster than GCC and spectral_norm at parity. Full table and per-optimization analysis on the benchmarks page.

✓ 18/18 outputs match GCC 566 unit tests passing GCC -O2 · AArch64 Linux
Latest

What's new

Recent work on the AArch64 backend moved the suite geomean from 0.86× to 0.49× of GCC -O2 — including a register steal for hot loop values, loop-backedge stack-slot coalescing, NEON/F64 loop promotion, reduction vectorization, FP recurrence anti-dependency splitting and reverse phi coalescing, backedge PRE, a csinc select fold, caller-saved-first allocation for small leaf functions, and shrink-wrapped callee saves — plus several latent miscompile fixes found along the way.

🎯

Register steal

Hot inner-loop-carried values (IVs, accumulators, carried pointers) take registers from provably colder scan winners — whole-interval deallocation, never range splitting. fannkuch 2.69× → 1.85×.

🧩

Loop-backedge slot coalescing

A spilled loop variable's update shares the variable's own stack slot, deleting the per-iteration ldr+str shuffle. arith_loop 2.09× → 1.15×.

🧮

NEON reductions + F64 promotion

Register-resident F64x2/I32x4 vector ops, fmadd fusion, and every reduction shape vectorized — sums (sadalp), dot products (smlal), conditional sums and max (smax/smaxv). matmul 1.17× faster than GCC; loop_patterns at parity.

🔁

Binary recursion → iteration

Detects f(n) = f(n-1) + f(n-2) and converts exponential O(2ⁿ) recursion to an O(n) iterative loop. fib(40) and ackermann(3,11): ~600× faster than GCC.

Read the Phase 4 write-up →  ·  Phase 3 write-up →

Getting Started

Build LCCC

1

Clone the repo

shell
git clone https://github.com/levkropp/lccc.git
cd lccc
2

Build (requires Rust stable)

shell
cargo build --release
# Binary at target/release/lccc (target picked by host/argv[0])
# Also: lccc-x86, lccc-arm, lccc-riscv, lccc-i686
3

Compile a C program

shell
GCC_INC="-I$(gcc -print-file-name=include)"
./target/release/lccc $GCC_INC -O2 -o hello hello.c
./hello
4

Run the benchmark suite

shell
python3 tests/benchmark/run_benchmarks.py --reps 5
Rust stable 2021 Linux · macOS hosts MIT OR Apache-2.0 OR BSD-2-Clause