Course contentsShow
← Rust
Lesson 1446 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Analyzing Generated Assembly Code

You will examine the machine code generated by the Rust compiler to verify optimal implementation of critical paths.

In this lesson, you will learn how to inspect the machine code output by the Rust compiler to verify that your performance-critical abstractions translate into the efficient instruction sequences you expect.

When writing high-performance unsafe code or low-level primitives, the compiler’s optimizer is your best ally, but it can occasionally introduce unexpected overhead or fail to vectorize a loop as intended. Analyzing generated assembly allows you to confirm that zero-cost abstractions remain truly zero-cost.

Using cargo-show-asm

The most effective way to analyze your code is via the cargo-show-asm tool. By running cargo asm <path-to-function>, you can view the exact assembly instructions generated for your target architecture.

Consider a simple function performing a pointer-based summation:

pub unsafe fn sum_unchecked(data: *const i32, len: usize) -> i32 {
    let mut sum = 0;
    for i in 0..len {
        sum += *data.add(i);
    }
    sum
}

What to Look For

When you inspect the generated output, you aren't just looking for "correct" logic; you are looking for optimal patterns:

  1. Loop Unrolling: Check if the compiler has unrolled your loop to reduce branch overhead. You should see multiple additions per iteration if the compiler successfully vectorized the code.
  2. Instruction Selection: Verify if the compiler is using SIMD instructions (like vpxor or vpaddd on x86_64) instead of scalar arithmetic.
  3. Redundant Memory Access: Look for excessive mov instructions. If you see unnecessary movement between registers and the stack, it often indicates the compiler failed to keep a variable in a register, potentially suggesting you should use a more "optimization-friendly" data layout.
  4. Inlining: Ensure that small helper functions were indeed inlined; if you see a call instruction to a trivial function, the optimizer may have missed an inlining opportunity, which you could address with #[inline(always)].

By comparing your high-level Rust logic to the resulting assembly, you gain the ability to spot where abstractions "leak" performance, allowing you to refactor your unsafe code to better match the underlying CPU architecture's pipeline.

Key Takeaway: Analyzing generated assembly is the final verification step to ensure your Rust code compiles to the most efficient machine instructions possible, bridging the gap between high-level safety and low-level performance.