Rust Performance Engineering

LLVM Optimization

After rustc type-checks and borrow-checks your code in Mid-level Intermediate Representation (MIR), it lowers MIR into LLVM Intermediate Representation (LLVM IR) and hands it to the LLVM optimizer. Because Rust's ownership rules guarantee that &mut T references never alias any other active reference (noalias in LLVM IR) and &T references are never mutated (unless wrapped in UnsafeCell), LLVM can apply optimizations to safe Rust code that C and C++ compilers can only apply when manual restrict annotations are written.

Rust Source (`&[i32]`, `&mut [i32]`)
        |
        v  `rustc` MIR Optimizations (Const prop, inlining, dead-branch removal)
        |
        v  Emits LLVM IR with automatic `noalias` & `readonly` attributes!
[ LLVM Optimization Pipeline (`opt-level = 3`) ]
  ├── Function Inlining & SROA (Scalar Replacement of Aggregates)
  ├── GVN & LICM (Loop-Invariant Code Motion)
  ├── Loop Unrolling & SLP / Loop Auto-Vectorization (AVX2 / AVX-512 / NEON)
  └── Tail-Call & Instruction Selection (Target Machine Assembly)

Key LLVM Optimization Passes Relevant to Rust

LLVM Pass What It Does to Rust Code How to Help LLVM Succeed
SROA (Scalar Replacement of Aggregates) Breaks temporary structs, tuples, Option<T>, and Result<T, E> apart into individual CPU registers, eliminating stack allocations completely. Return small structs/enums by value instead of boxing them on the heap.
Inlining Replaces a function call site with the callee's body, exposing inner operations to constant folding and vectorization. Keep hot cross-crate functions generic or mark small cross-crate helpers #[inline].
Alias Analysis (noalias) Uses Rust's guarantee that &mut T never overlaps with &U or another &mut T to hoist loads out of loops and vectorize without runtime overlap checks! Pass slices (&[T], &mut [T]) rather than raw pointers (*const T, *mut T), as raw pointers do not carry automatic noalias.
Loop Vectorization (SIMD) Processes 4, 8, or 16 array elements per CPU instruction using 128-bit NEON or 256/512-bit AVX registers. Use slice iterators (zip, chunks_exact) and avoid early data-dependent break exits inside numeric loops.
Constant Folding & Dead Code Elimination Evaluates constant math at compile time and deletes unreachable branches. Use const functions and exhaustive enums.

How Rust's &mut Exclusivity Unlocks Better LLVM Codegen than C/C++

Consider adding a constant value read from *factor into every element of an output slice out:

  • In C (void scale_add(int *out, const int *factor, size_t n)), out and factor might point to the same memory address. Therefore, the C compiler must re-load *factor from memory on every iteration (or emit a runtime pointer-overlap check).
  • In Rust (fn scale_add(out: &mut [i32], factor: &i32)), the borrow checker proves at compile time that out and factor cannot overlap! rustc marks both pointers noalias in LLVM IR, allowing LLVM to load *factor into a register once before the loop and vectorize the loop unconditionally:
pub fn add_scalar_in_place(out: &mut [u32], delta: &u32) {
    // Because `out: &mut [u32]` and `delta: &u32` are guaranteed not to alias,
    // LLVM hoists `*delta` into a SIMD vector register once before the loop!
    for elem in out.iter_mut() {
        *elem = elem.wrapping_add(*delta);
    }
}

fn main() {
    let mut values = [10u32, 20, 30, 40];
    let delta = 5u32;
    add_scalar_in_place(&mut values, &delta);

    assert_eq!(values, [15, 25, 35, 45]);
    println!("Vectorizable non-aliasing slice update: {values:?}");
}
Vectorizable non-aliasing slice update: [15, 25, 35, 45]

Inspecting Generated Assembly and LLVM IR (--emit)

To verify whether LLVM vectorized a loop or eliminated a bounds check, inspect the generated assembly or LLVM IR using rustc --emit or the cargo-show-asm tool (cargo install cargo-show-asm --locked):

# 1. Inspect the exact assembly generated for a specific function with `cargo-show-asm`:
cargo asm --lib my_crate::add_scalar_in_place

# 2. Emit `.ll` (LLVM IR) and `.s` (Assembly) files into target/release/deps/:
RUSTFLAGS="--emit=llvm-ir,asm -C llvm-args=-x86-asm-syntax=intel" cargo build --release