From c937a702789d0b90e7c28b7295e9690d76b9ccf6 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Mon, 16 Sep 2024 22:14:35 +0700 Subject: [PATCH] rust_for_linux: -Zregparm= commandline flag for X86 (#116972) --- compiler/rustc_codegen_gcc/src/builder.rs | 8 ++++- compiler/rustc_codegen_gcc/src/context.rs | 10 +++++- compiler/rustc_middle/src/ty/layout.rs | 16 ++++++++- compiler/rustc_session/src/options.rs | 3 ++ compiler/rustc_target/src/abi/call/mod.rs | 22 +++++++----- compiler/rustc_target/src/abi/call/x86.rs | 12 +++++-- compiler/rustc_target/src/spec/mod.rs | 12 +++++++ tests/codegen/regparm-inreg.rs | 44 +++++++++++++++++++++++ 8 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 tests/codegen/regparm-inreg.rs diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 6ba678e2e7c65..b660e16aee944 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -30,7 +30,7 @@ use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange}; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi}; +use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi}; use crate::common::{type_is_pointer, SignType, TypeReflection}; use crate::context::CodegenCx; @@ -2374,6 +2374,12 @@ impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> { } } +impl<'tcx> HasX86AbiOpt for Builder<'_, '_, 'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + self.cx.x86_abi_opt() + } +} + pub trait ToGccComp { fn to_gcc_comparison(&self) -> ComparisonOp; } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index e330102fbd84b..0556436f85418 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -20,7 +20,9 @@ use rustc_span::source_map::respan; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi}; +use rustc_target::spec::{ + HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, +}; use crate::callee::get_fn; use crate::common::SignType; @@ -571,6 +573,12 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { } } +impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm } + } +} + impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 8cec8eac1898a..7029299887492 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -18,7 +18,9 @@ use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::abi::Abi as SpecAbi; -use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi}; +use rustc_target::spec::{ + HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, PanicStrategy, Target, WasmCAbi, X86Abi, +}; use tracing::debug; use crate::error::UnsupportedFnAbi; @@ -541,6 +543,12 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> { } } +impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> { + fn x86_abi_opt(&self) -> X86Abi { + X86Abi { regparm: self.sess.opts.unstable_opts.regparm } + } +} + impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> { #[inline] fn tcx(&self) -> TyCtxt<'tcx> { @@ -592,6 +600,12 @@ impl<'tcx, T: HasWasmCAbiOpt> HasWasmCAbiOpt for LayoutCx<'tcx, T> { } } +impl<'tcx, T: HasX86AbiOpt> HasX86AbiOpt for LayoutCx<'tcx, T> { + fn x86_abi_opt(&self) -> X86Abi { + self.tcx.x86_abi_opt() + } +} + impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx.tcx() diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a57dc80b3168d..b39c92be5cfae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1964,6 +1964,9 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), + regparm: Option = (None, parse_opt_number, [TRACKED], + "On x86-32 targets, the regparm causes the compiler to pass arguments \ + in registers EAX, EDX, and ECX instead of on the stack."), relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], "whether ELF relocations can be relaxed"), remap_cwd_prefix: Option = (None, parse_opt_pathbuf, [TRACKED], diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 082c169b210f8..5bfd9237d0ee1 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -5,7 +5,7 @@ use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use crate::abi::{self, Abi, Align, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; -use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi}; +use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi}; mod aarch64; mod amdgpu; @@ -877,7 +877,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { ) -> Result<(), AdjustForForeignAbiError> where Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt, + C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt, { if abi == spec::abi::Abi::X86Interrupt { if let Some(arg) = self.args.first_mut() { @@ -890,14 +890,18 @@ impl<'a, Ty> FnAbi<'a, Ty> { let spec = cx.target_spec(); match &spec.arch[..] { "x86" => { - let flavor = if let spec::abi::Abi::Fastcall { .. } - | spec::abi::Abi::Vectorcall { .. } = abi - { - x86::Flavor::FastcallOrVectorcall - } else { - x86::Flavor::General + let (flavor, regparm) = match abi { + spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => { + (x86::Flavor::FastcallOrVectorcall, None) + } + spec::abi::Abi::C { .. } + | spec::abi::Abi::Cdecl { .. } + | spec::abi::Abi::Stdcall { .. } => { + (x86::Flavor::General, cx.x86_abi_opt().regparm) + } + _ => (x86::Flavor::General, None), }; - x86::compute_abi_info(cx, self, flavor); + x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm }); } "x86_64" => match abi { spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index bdb14350ded4b..ec7a6f3ce380f 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -8,11 +8,17 @@ pub(crate) enum Flavor { FastcallOrVectorcall, } -pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) +pub(crate) struct X86Options { + pub flavor: Flavor, + pub regparm: Option, +} + +pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { + let flavor = opts.flavor; if !fn_abi.ret.is_ignore() { if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() { // Returning a structure. Most often, this will use @@ -128,7 +134,7 @@ where } } - if flavor == Flavor::FastcallOrVectorcall { + if flavor == Flavor::FastcallOrVectorcall || opts.regparm.is_some() { // Mark arguments as InReg like clang does it, // so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall. @@ -138,7 +144,7 @@ where // IsSoftFloatABI is only set to true on ARM platforms, // which in turn can't be x86? - let mut free_regs = 2; + let mut free_regs: u64 = opts.regparm.unwrap_or(2).into(); for arg in fn_abi.args.iter_mut() { let attrs = match arg.mode { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 58d47c201f018..8ed859472129d 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2012,6 +2012,18 @@ pub trait HasWasmCAbiOpt { fn wasm_c_abi_opt(&self) -> WasmCAbi; } +/// x86 (32-bit) abi options. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct X86Abi { + /// On x86-32 targets, the regparm causes the compiler to pass arguments + /// in registers EAX, EDX, and ECX instead of on the stack. + pub regparm: Option, +} + +pub trait HasX86AbiOpt { + fn x86_abi_opt(&self) -> X86Abi; +} + type StaticCow = Cow<'static, T>; /// Optional aspects of a target specification. diff --git a/tests/codegen/regparm-inreg.rs b/tests/codegen/regparm-inreg.rs new file mode 100644 index 0000000000000..c9f925c4beca1 --- /dev/null +++ b/tests/codegen/regparm-inreg.rs @@ -0,0 +1,44 @@ +// Checks how `regparm` flag works with different calling conventions: +// marks function arguments as "inreg" like the C/C++ compilers for the platforms. +// x86 only. + +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=3 -O -C no-prepopulate-passes +//@ needs-llvm-components: x86 + +#![crate_type = "lib"] +#![no_core] +#![feature(no_core, lang_items)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub mod tests { + // regparm doesn't work for "fastcall" calling conv (only 2 inregs) + // CHECK: @f1(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3) + #[no_mangle] + pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {} + + // regparm doesn't work for "Rust" calling conv + // CHECK: @f2(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3) + #[no_mangle] + pub extern "Rust" fn f2(_: i32, _: i32, _: i32) {} + + // CHECK: @f3(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3) + #[no_mangle] + pub extern "C" fn f3(_: i32, _: i32, _: i32) {} + + // CHECK: @f4(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3) + #[no_mangle] + pub extern "cdecl" fn f4(_: i32, _: i32, _: i32) {} + + // CHECK: @f5(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3) + #[no_mangle] + pub extern "stdcall" fn f5(_: i32, _: i32, _: i32) {} + + // regparm doesn't work for thiscall + // CHECK: @f6(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3) + #[no_mangle] + pub extern "thiscall" fn f6(_: i32, _: i32, _: i32) {} +}