Inline assembly: extra `push rax`, `pop rcx` added to function

While fiddling with the new inline assembly syntax, I noticed that the compiler was adding a push rax at the beginning of my function and a corresponding pop rcx at the end. Why is it doing that? How can I suppress the generation of those instructions?

I’m using rustc nightly to compile a simple function that adds 5 to its argument:

pub fn asm_add_five(n: u64) -> u64 {
    let mut result;
    unsafe {
        asm!(
            "lea {result}, [{n} + 5]",
            n = in(reg) n,
            result = out(reg) result,
        );
    }
    result
}

The generated assembly looks like this:

example::asm_add_five:
        push    rax
        lea     rax, [rdi + 5]
        pop     rcx
        ret

I normally compile with -Ctarget-feature=+sse,+sse-unaligned-mem,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2 -Cdebuginfo=2 -Copt-level=3 -Ccodegen-units=1 -Clto=fat.

You can run the code on Godbolt. There is also a native Rust add_five() function there for comparison.

I’m new to Rust and to x86 assembly and I’d be happy to read any foundational books or documentation that can clarify this.

It's to align the stack pointer to 16 bytes, to satisfy the System-V ABI.

Native Rust code doesn't need it as the compiler knows there's no call instruction in this function. But the compiler doesn't care the code inside asm!(), it should be prepared for the potential call instruction.

1 Like

If you add options(nostack) the compiler will know that the stack is not accessed and thus doesn't need to be aligned. https://godbolt.org/z/T7bsn7

3 Likes