Cheeku now compiles through LLVM's new pass manager at four optimisation levels. O0 skips the optional IR pipeline, O1 simplifies scalar code, and O2 or O3 add loop and SLP vectorisation. O2 remains the default, so existing builds get a faster binary without changing a single command.
The target is configured before any pass runs. By default Cheeku uses a generic CPU for the detected host architecture, which keeps the executable portable across machines of the same architecture. Passing --cpu native opts into the host CPU's specific features; that binary may require those features elsewhere.
What each level does
- O0: no optional IR passes. LLVM still lowers instructions into machine code.
- O1: local promotion and scalar simplification.
- O2: the standard pipeline, including loop and SLP vectorisation. This is the default.
- O3: more aggressive standard transformations. Faster is not guaranteed.
cheeku examples/numerical.chuchu -O3 \
--emit-ir numerical.ll \
--emit-assembly numerical.s \
-o numericalThe checks do not move
Checked signed arithmetic, division guards, array bounds and value-copy semantics are part of the production lowering at every level. No unsafe fast-math flags or unjustified nsw/nuw annotations were added. LLVM may still remove a check when it can prove the operation is safe, which is the point of an optimiser.
Every module is verified after generation, before and after passes, and again before object emission. If a pass or target step fails, compilation reports the error; it never quietly drops to a lower optimisation level.
O3 permits more aggressive standard transformations; it is not guaranteed to be faster or smaller than O2.
Measuring instead of guessing
--time-compilation prints stage durations as JSON on stderr. --emit-ir and --emit-assembly expose the optimised artifacts for inspection. --no-vectorize turns off the loop and SLP options for experiments, but it is not a promise of scalar machine code: other standard passes can still form vector operations.
The optimisation guide covers the pipeline, the target policy and the library API in detail. If you are comparing levels, build a fresh module for each run rather than reusing one.