-
- Notifications
You must be signed in to change notification settings - Fork 14.2k
Description
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