Skip to content

-Zregparm doesn't put 64-bit arguments in registers #145694

@sulix

Description

@sulix

The -Zregparm option on 32-bit x86 should pass arguments in registers where possible, but for 64-bit arguments, it will push them onto the stack, whereas C compilers (both gcc and clang) will put them in a pair of registers.

For example, the following rust code:

unsafe extern "C" { pub fn test(s: i64) -> i64; } #[unsafe(no_mangle)] pub fn f1() { unsafe { let r = test(42); } }

compiles to (with -Zregparm=3):

f1: sub esp, 20 push 0 push 42 call test add esp, 28 ret 

Note the 64-bit value is put on the stack.

However, the equivalent C code:

extern long long test(long long s); void f1(void) { long long r = test(42); }

compiles to (gcc 15.2 with -mregparm=3, but clang produces something similar):

f1(): push ebp mov ebp, esp sub esp, 24 mov eax, 42 mov edx, 0 call test(long long) mov DWORD PTR [ebp-16], eax mov DWORD PTR [ebp-12], edx nop leave ret 

Note that the 64-bit value is put in edx:eax.

This can cause crashes/incorrect results due to an ABI mismatch, particularly in Rust-for-Linux on 32-bit x86.

Note that 64-bit return values seem to be functioning correctly, and are placed in edx:eax.

Compiler explorer link with the above examples: https://godbolt.org/z/feb7addG7

Rust-for-Linux tracking issue for 32-bit x86: Rust-for-Linux/linux#78
Tracking issue for -Zregparm: #131749

Metadata

Metadata

Assignees

No one assigned

    Labels

    -ZregparmUnstable option: -ZregparmA-ABIArea: Concerning the application binary interface (ABI)A-rust-for-linuxRelevant for the Rust-for-Linux projectC-bugCategory: This is a bug.O-x86_32Target: x86 processors, 32 bit (like i686-*) (also known as IA-32, i386, i586, i686)T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions