Skip to content

Commit

Permalink
rust_for_linux: -Zregparm=<N> commandline flag for X86 (rust-lang#116972
Browse files Browse the repository at this point in the history
)
  • Loading branch information
azhogin committed Oct 2, 2024
1 parent c8dff28 commit dff5a6d
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 52 deletions.
8 changes: 7 additions & 1 deletion compiler/rustc_codegen_gcc/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2359,6 +2359,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;
}
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_codegen_gcc/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>;

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(profile_emit, Some(PathBuf::from("abc")));
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
tracked!(profiler_runtime, "abc".to_string());
tracked!(regparm, Some(3));
tracked!(relax_elf_relocations, Some(true));
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
tracked!(sanitizer, SanitizerSet::ADDRESS);
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,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;
Expand Down Expand Up @@ -534,6 +536,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> {
Expand Down Expand Up @@ -585,6 +593,12 @@ impl<'tcx> HasWasmCAbiOpt for LayoutCx<'tcx> {
}
}

impl<'tcx> HasX86AbiOpt for LayoutCx<'tcx> {
fn x86_abi_opt(&self) -> X86Abi {
self.calc.cx.x86_abi_opt()
}
}

impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.calc.cx
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1978,6 +1978,10 @@ 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<u32> = (None, parse_opt_number, [TRACKED],
"On x86-32 targets, setting this to N causes the compiler to pass N arguments \
in registers EAX, EDX, and ECX instead of on the stack.\
It is UNSOUND to link together crates that use different values for this flag!"),
relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
"whether ELF relocations can be relaxed"),
remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
Expand Down
43 changes: 34 additions & 9 deletions compiler/rustc_target/src/abi/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -875,7 +875,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() {
Expand All @@ -888,14 +888,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),
Expand Down Expand Up @@ -961,6 +965,27 @@ impl<'a, Ty> FnAbi<'a, Ty> {

Ok(())
}

pub fn fill_inregs_for_rust_abi<C>(&mut self, cx: &C)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasTargetSpec + HasX86AbiOpt,
{
let spec = cx.target_spec();
match &spec.arch[..] {
"x86" => {
x86::fill_inregs(
cx,
self,
x86::X86Options {
flavor: x86::Flavor::General,
regparm: cx.x86_abi_opt().regparm,
},
);
}
_ => {}
}
}
}

impl FromStr for Conv {
Expand Down
93 changes: 53 additions & 40 deletions compiler/rustc_target/src/abi/call/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ 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<u32>,
}

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,
Expand Down Expand Up @@ -128,58 +133,66 @@ where
}
}

if flavor == Flavor::FastcallOrVectorcall {
// Mark arguments as InReg like clang does it,
// so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
fill_inregs(cx, fn_abi, opts);
}

// Clang reference: lib/CodeGen/TargetInfo.cpp
// See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()
pub(crate) fn fill_inregs<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
where
Ty: TyAbiInterface<'a, C> + Copy,
{
if opts.flavor != Flavor::FastcallOrVectorcall && !opts.regparm.is_some_and(|x| x > 0) {
return;
}
// Mark arguments as InReg like clang does it,
// so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.

// IsSoftFloatABI is only set to true on ARM platforms,
// which in turn can't be x86?
// Clang reference: lib/CodeGen/TargetInfo.cpp
// See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs()

let mut free_regs = 2;
// IsSoftFloatABI is only set to true on ARM platforms,
// which in turn can't be x86?

for arg in fn_abi.args.iter_mut() {
let attrs = match arg.mode {
PassMode::Ignore
| PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
continue;
}
PassMode::Direct(ref mut attrs) => attrs,
PassMode::Pair(..)
| PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }
| PassMode::Cast { .. } => {
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
}
};
// 2 for fastcall/vectorcall, regparm limited by 3 otherwise
let mut free_regs = opts.regparm.unwrap_or(2).into();

// At this point we know this must be a primitive of sorts.
let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
assert_eq!(unit.size, arg.layout.size);
if unit.kind == RegKind::Float {
for arg in fn_abi.args.iter_mut() {
let attrs = match arg.mode {
PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
continue;
}
PassMode::Direct(ref mut attrs) => attrs,
PassMode::Pair(..)
| PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }
| PassMode::Cast { .. } => {
unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
}
};

let size_in_regs = (arg.layout.size.bits() + 31) / 32;
// At this point we know this must be a primitive of sorts.
let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
assert_eq!(unit.size, arg.layout.size);
if unit.kind == RegKind::Float {
continue;
}

if size_in_regs == 0 {
continue;
}
let size_in_regs = (arg.layout.size.bits() + 31) / 32;

if size_in_regs > free_regs {
break;
}
if size_in_regs == 0 {
continue;
}

free_regs -= size_in_regs;
if size_in_regs > free_regs {
break;
}

if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
attrs.set(ArgAttribute::InReg);
}
free_regs -= size_in_regs;

if free_regs == 0 {
break;
}
if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
attrs.set(ArgAttribute::InReg);
}

if free_regs == 0 {
break;
}
}
}
12 changes: 12 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2050,6 +2050,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 N causes the compiler to pass arguments
/// in registers EAX, EDX, and ECX instead of on the stack.
pub regparm: Option<u32>,
}

pub trait HasX86AbiOpt {
fn x86_abi_opt(&self) -> X86Abi;
}

type StaticCow<T> = Cow<'static, T>;

/// Optional aspects of a target specification.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ty_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,9 @@ fn fn_abi_adjust_for_abi<'tcx>(
for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
fixup(arg, Some(arg_idx));
}
if tcx.sess.target.arch == "x86" {
fn_abi.fill_inregs_for_rust_abi(cx);
}
} else {
fn_abi
.adjust_for_foreign_abi(cx, abi)
Expand Down
Loading

0 comments on commit dff5a6d

Please sign in to comment.