Optimisations
xcc’s optimiser works on its SSA intermediate representation, shared by every target and by
the GPU kernel generator. -O3, the default, is the same as -O2. -O1 runs only the
cheapest passes, and -O0 only the mandatory ones.
SSA construction
Section titled “SSA construction”- Direct SSA from structured control flow. The lowering keeps each local’s current value and places a phi at each join and loop header for the names that differ, so scalar locals never live in memory.
- mem2reg in the Cytron style, with iterated dominance frontiers and dominator-tree renaming, promotes aggregates’ fields, and every local of a C program.
- Memory ordering by a single chain of memory tokens.
IR optimisations
Section titled “IR optimisations”| Optimisation | What xcc does |
|---|---|
| Inlining | Small functions inlined, twice over, with a size limit (-Fli). |
| Common subexpression elimination | With redundant-load elimination and store-to-load forwarding, partly across blocks. |
| Constant folding and algebraic simplification | Including integer tree-height reduction. |
| Dead code and dead store elimination | Dead functions, unused values and local dead stores. |
| Scalar replacement of aggregates (SROA) | Struct locals split into scalars. |
| If-conversion | Branches that pick a value become selects. |
| Jump threading and block merging | |
| Tail-recursion elimination | Self-tail-calls become loops. |
| Demanded-width narrowing | Arithmetic done at the width its result needs. |
| Loop-invariant code motion (LICM) | |
| Strength reduction and pointer induction variables | Index arithmetic becomes pointer increments. |
| Loop rotation and induction-variable narrowing | |
| Full and partial loop unrolling | Constant trip counts fully, unknown ones four-fold. |
| Loop-invariant reduction collapse | A reduction over an invariant value computed directly. |
| Loop vectorisation | Maps, sums, dot products, minimum and maximum, widening sums and counts, with interleaved accumulators, loop distribution and scalar epilogues, for NEON, SSE2, AVX2, AVX-512 and WebAssembly SIMD. |
| Outer-loop vectorisation | |
| Vector combining | Bit-select and rotate on arm64, ternary logic on AVX-512. |
| Function multiversioning | AVX2 and AVX-512 clones chosen at load time (-msimd=auto). |
| Guarded devirtualisation | A virtual call becomes a class test and a direct call that can be inlined. |
| Idiom recognition | memset loops, and matrix multiplication replaced by kernels xcc emits for SME on Apple silicon and AVX2 and AVX-512 on x86-64. |
| Bounds-check elimination | Range analysis removes checks it can prove, when checks are on. |
| Floating-point contraction | Fused multiply-add: on arm64 an adjacent multiply and add, as clang does; on x86-64 only in :goal(speed) loops, so a result does not depend on which CPU runs it. |
| Select-to-branch | A float select on one bit becomes a branch when one arm is costly (arm64). |
| Hot-path block placement | arm64. |
A float reduction is reassociated, and x86-64 contracts multiply-adds, only under :goal(speed), which
par blocks use by default.
Code generation
Section titled “Code generation”- Register allocation over live intervals from backward liveness, most-used values first, callee-saved registers for values live across calls, phi coalescing, and stack-slot colouring.
- Peephole optimisation of the generated assembly: spill forwarding, redundant reload and compare removal, copy propagation, fused shift-and-add, and fallthrough branch removal.
- Instruction selection for conditional selects (
csel,cmov), compare-and-branch, test-bit-and-branch (tbz), address-mode folding, and fixed-point conversions. - Division by constants with multiply-high magic numbers, vectorised on arm64.
- Loop alignment on x86-64.
GPU kernels
Section titled “GPU kernels”Each par block’s kernel is generated from a copy of its IR optimised again for the GPU:
reductions are split across lanes, and loops with an escape test can run several trips per
test. See GPU kernels.