From 67d6b432ec2a68d0033d7eeb9183a4968f431075 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sun, 12 Oct 2025 21:44:33 +0800 Subject: [PATCH] x86: Correctly pass large integers in registers with -Zregparm The -Zregparm option for 32-bit x86 modifies the calling convention to pass a number of arguments in registers instead of on the stack. (This is primarily used by the Linux kernel.) Currently, rustc will only pass integers of size <= 32 bits in registers, but gcc (and clang) will pass larger integers (e.g., 64-bit integers) across two registers if possible. This is not the case with fastcall/vectorcall. Note that gcc/clang with -mregparm will also pass some structs in registers: this patch does not attempt to fix that case. --- compiler/rustc_target/src/callconv/x86.rs | 4 +++- tests/assembly-llvm/regparm-module-flag.rs | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 918b71c80c4f0..a2bfbe11f006a 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -183,7 +183,9 @@ pub(crate) fn fill_inregs<'a, Ty, C>( free_regs -= size_in_regs; - if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { + if opts.flavor != Flavor::FastcallOrVectorcall + || arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer + { attrs.set(ArgAttribute::InReg); } diff --git a/tests/assembly-llvm/regparm-module-flag.rs b/tests/assembly-llvm/regparm-module-flag.rs index 67ef44285eac2..0f05ad6b698ea 100644 --- a/tests/assembly-llvm/regparm-module-flag.rs +++ b/tests/assembly-llvm/regparm-module-flag.rs @@ -19,6 +19,26 @@ use minicore::*; unsafe extern "C" { fn memset(p: *mut c_void, val: i32, len: usize) -> *mut c_void; fn non_builtin_memset(p: *mut c_void, val: i32, len: usize) -> *mut c_void; + fn test_i64_arg(s: i64) -> i64; +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn test_i64() -> i64 { + // REGPARM1-LABEL: test_i64 + // REGPARM1: pushl + // REGPARM1: pushl + // REGPARM1: calll test_i64_arg + + // REGPARM2-LABEL: test_i64 + // REGPARM2: movl $42, %eax + // REGPARM2: xorl %edx, %edx + // REGPARM2: jmp test_i64_arg + + // REGPARM3-LABEL: test_i64 + // REGPARM3: movl $42, %eax + // REGPARM3: xorl %edx, %edx + // REGPARM3: jmp test_i64_arg + unsafe { test_i64_arg(42) } } #[unsafe(no_mangle)]