Skip to content

Commit c2813de

Browse files
committed
rust_for_linux: -Zregparm=<N> commandline flag for X86 (#116972)
1 parent 8f93a10 commit c2813de

File tree

9 files changed

+114
-15
lines changed

9 files changed

+114
-15
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use rustc_span::def_id::DefId;
3030
use rustc_span::Span;
3131
use rustc_target::abi::call::FnAbi;
3232
use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange};
33-
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
33+
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi};
3434

3535
use crate::common::{type_is_pointer, SignType, TypeReflection};
3636
use crate::context::CodegenCx;
@@ -2374,6 +2374,12 @@ impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> {
23742374
}
23752375
}
23762376

2377+
impl<'tcx> HasX86AbiOpt for Builder<'_, '_, 'tcx> {
2378+
fn x86_abi_opt(&self) -> X86Abi {
2379+
self.cx.x86_abi_opt()
2380+
}
2381+
}
2382+
23772383
pub trait ToGccComp {
23782384
fn to_gcc_comparison(&self) -> ComparisonOp;
23792385
}

compiler/rustc_codegen_gcc/src/context.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use rustc_span::source_map::respan;
2020
use rustc_span::{Span, DUMMY_SP};
2121
use rustc_target::abi::call::FnAbi;
2222
use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
23-
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi};
23+
use rustc_target::spec::{
24+
HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi,
25+
};
2426

2527
use crate::callee::get_fn;
2628
use crate::common::SignType;
@@ -571,6 +573,12 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
571573
}
572574
}
573575

576+
impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
577+
fn x86_abi_opt(&self) -> X86Abi {
578+
X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm }
579+
}
580+
}
581+
574582
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
575583
type LayoutOfResult = TyAndLayout<'tcx>;
576584

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ fn test_unstable_options_tracking_hash() {
834834
tracked!(profile_emit, Some(PathBuf::from("abc")));
835835
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
836836
tracked!(profiler_runtime, "abc".to_string());
837+
tracked!(regparm, Some(3));
837838
tracked!(relax_elf_relocations, Some(true));
838839
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
839840
tracked!(sanitizer, SanitizerSet::ADDRESS);

compiler/rustc_middle/src/ty/layout.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
1818
use rustc_target::abi::call::FnAbi;
1919
use rustc_target::abi::*;
2020
use rustc_target::spec::abi::Abi as SpecAbi;
21-
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi};
21+
use rustc_target::spec::{
22+
HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi,
23+
};
2224
use tracing::debug;
2325

2426
use crate::error::UnsupportedFnAbi;
@@ -541,6 +543,12 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> {
541543
}
542544
}
543545

546+
impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> {
547+
fn x86_abi_opt(&self) -> X86Abi {
548+
X86Abi { regparm: self.sess.opts.unstable_opts.regparm }
549+
}
550+
}
551+
544552
impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
545553
#[inline]
546554
fn tcx(&self) -> TyCtxt<'tcx> {
@@ -592,6 +600,12 @@ impl<'tcx, T: HasWasmCAbiOpt> HasWasmCAbiOpt for LayoutCx<'tcx, T> {
592600
}
593601
}
594602

603+
impl<'tcx, T: HasX86AbiOpt> HasX86AbiOpt for LayoutCx<'tcx, T> {
604+
fn x86_abi_opt(&self) -> X86Abi {
605+
self.tcx.x86_abi_opt()
606+
}
607+
}
608+
595609
impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
596610
fn tcx(&self) -> TyCtxt<'tcx> {
597611
self.tcx.tcx()

compiler/rustc_session/src/options.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,9 @@ options! {
19641964
"enable queries of the dependency graph for regression testing (default: no)"),
19651965
randomize_layout: bool = (false, parse_bool, [TRACKED],
19661966
"randomize the layout of types (default: no)"),
1967+
regparm: Option<u32> = (None, parse_opt_number, [TRACKED],
1968+
"On x86-32 targets, the regparm N causes the compiler to pass N arguments \
1969+
in registers EAX, EDX, and ECX instead of on the stack."),
19671970
relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
19681971
"whether ELF relocations can be relaxed"),
19691972
remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],

compiler/rustc_target/src/abi/call/mod.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_macros::HashStable_Generic;
55
use rustc_span::Symbol;
66

77
use crate::abi::{self, Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
8-
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi};
8+
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
99

1010
mod aarch64;
1111
mod amdgpu;
@@ -877,7 +877,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
877877
) -> Result<(), AdjustForForeignAbiError>
878878
where
879879
Ty: TyAbiInterface<'a, C> + Copy,
880-
C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt,
880+
C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt,
881881
{
882882
if abi == spec::abi::Abi::X86Interrupt {
883883
if let Some(arg) = self.args.first_mut() {
@@ -890,14 +890,18 @@ impl<'a, Ty> FnAbi<'a, Ty> {
890890
let spec = cx.target_spec();
891891
match &spec.arch[..] {
892892
"x86" => {
893-
let flavor = if let spec::abi::Abi::Fastcall { .. }
894-
| spec::abi::Abi::Vectorcall { .. } = abi
895-
{
896-
x86::Flavor::FastcallOrVectorcall
897-
} else {
898-
x86::Flavor::General
893+
let (flavor, regparm) = match abi {
894+
spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => {
895+
(x86::Flavor::FastcallOrVectorcall, None)
896+
}
897+
spec::abi::Abi::C { .. }
898+
| spec::abi::Abi::Cdecl { .. }
899+
| spec::abi::Abi::Stdcall { .. } => {
900+
(x86::Flavor::General, cx.x86_abi_opt().regparm)
901+
}
902+
_ => (x86::Flavor::General, None),
899903
};
900-
x86::compute_abi_info(cx, self, flavor);
904+
x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm });
901905
}
902906
"x86_64" => match abi {
903907
spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),

compiler/rustc_target/src/abi/call/x86.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ pub(crate) enum Flavor {
88
FastcallOrVectorcall,
99
}
1010

11-
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor)
11+
pub(crate) struct X86Options {
12+
pub flavor: Flavor,
13+
pub regparm: Option<u32>,
14+
}
15+
16+
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
1217
where
1318
Ty: TyAbiInterface<'a, C> + Copy,
1419
C: HasDataLayout + HasTargetSpec,
1520
{
21+
let flavor = opts.flavor;
1622
if !fn_abi.ret.is_ignore() {
1723
if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() {
1824
// Returning a structure. Most often, this will use
@@ -128,7 +134,7 @@ where
128134
}
129135
}
130136

131-
if flavor == Flavor::FastcallOrVectorcall {
137+
if flavor == Flavor::FastcallOrVectorcall || opts.regparm.is_some_and(|x| x > 0) {
132138
// Mark arguments as InReg like clang does it,
133139
// so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
134140

@@ -138,7 +144,8 @@ where
138144
// IsSoftFloatABI is only set to true on ARM platforms,
139145
// which in turn can't be x86?
140146

141-
let mut free_regs = 2;
147+
// 2 for fastcall/vectorcall, regparm limited by 3 otherwise
148+
let mut free_regs: u64 = opts.regparm.map_or(2, |v| std::cmp::min(v, 3)).into();
142149

143150
for arg in fn_abi.args.iter_mut() {
144151
let attrs = match arg.mode {

compiler/rustc_target/src/spec/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2012,6 +2012,18 @@ pub trait HasWasmCAbiOpt {
20122012
fn wasm_c_abi_opt(&self) -> WasmCAbi;
20132013
}
20142014

2015+
/// x86 (32-bit) abi options.
2016+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
2017+
pub struct X86Abi {
2018+
/// On x86-32 targets, the regparm N causes the compiler to pass arguments
2019+
/// in registers EAX, EDX, and ECX instead of on the stack.
2020+
pub regparm: Option<u32>,
2021+
}
2022+
2023+
pub trait HasX86AbiOpt {
2024+
fn x86_abi_opt(&self) -> X86Abi;
2025+
}
2026+
20152027
type StaticCow<T> = Cow<'static, T>;
20162028

20172029
/// Optional aspects of a target specification.

tests/codegen/regparm-inreg.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Checks how `regparm` flag works with different calling conventions:
2+
// marks function arguments as "inreg" like the C/C++ compilers for the platforms.
3+
// x86 only.
4+
5+
//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=3 -O -C no-prepopulate-passes
6+
//@ needs-llvm-components: x86
7+
8+
#![crate_type = "lib"]
9+
#![no_core]
10+
#![feature(no_core, lang_items)]
11+
12+
#[lang = "sized"]
13+
trait Sized {}
14+
#[lang = "copy"]
15+
trait Copy {}
16+
17+
pub mod tests {
18+
// regparm doesn't work for "fastcall" calling conv (only 2 inregs)
19+
// CHECK: @f1(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
20+
#[no_mangle]
21+
pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}
22+
23+
// regparm doesn't work for "Rust" calling conv
24+
// CHECK: @f2(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
25+
#[no_mangle]
26+
pub extern "Rust" fn f2(_: i32, _: i32, _: i32) {}
27+
28+
// CHECK: @f3(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
29+
#[no_mangle]
30+
pub extern "C" fn f3(_: i32, _: i32, _: i32) {}
31+
32+
// CHECK: @f4(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
33+
#[no_mangle]
34+
pub extern "cdecl" fn f4(_: i32, _: i32, _: i32) {}
35+
36+
// CHECK: @f5(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
37+
#[no_mangle]
38+
pub extern "stdcall" fn f5(_: i32, _: i32, _: i32) {}
39+
40+
// regparm doesn't work for thiscall
41+
// CHECK: @f6(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
42+
#[no_mangle]
43+
pub extern "thiscall" fn f6(_: i32, _: i32, _: i32) {}
44+
}

0 commit comments

Comments
 (0)