1
Fork 0

rust_for_linux: -Zregparm=<N> commandline flag for X86 (#116972)

This commit is contained in:
Andrew Zhogin 2024-09-16 22:14:35 +07:00
parent a0c2aba29a
commit b3ae64d24f
20 changed files with 436 additions and 53 deletions

View file

@ -6,7 +6,7 @@ use rustc_macros::HashStable_Generic;
use rustc_span::Symbol;
use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, WasmCAbi};
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
mod aarch64;
mod amdgpu;
@ -631,7 +631,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() {
@ -643,14 +643,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),
@ -716,6 +720,28 @@ 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,
},
true,
);
}
_ => {}
}
}
}
impl FromStr for Conv {

View file

@ -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,
@ -128,58 +133,77 @@ 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, false);
}
// 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,
rust_abi: bool,
) where
Ty: TyAbiInterface<'a, C> + Copy,
{
if opts.flavor != Flavor::FastcallOrVectorcall && opts.regparm.is_none_or(|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 types generating PassMode::Cast, InRegs will not be set.
// Maybe, this is a FIXME
let has_casts = fn_abi.args.iter().any(|arg| matches!(arg.mode, PassMode::Cast { .. }));
if has_casts && rust_abi {
return;
}
for arg in fn_abi.args.iter_mut() {
let attrs = match arg.mode {
PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
continue;
}
let size_in_regs = (arg.layout.size.bits() + 31) / 32;
if size_in_regs == 0 {
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)
}
};
if size_in_regs > free_regs {
break;
}
// 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 matches!(unit.kind, RegKind::Float | RegKind::Vector) {
continue;
}
free_regs -= size_in_regs;
let size_in_regs = (arg.layout.size.bits() + 31) / 32;
if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
attrs.set(ArgAttribute::InReg);
}
if size_in_regs == 0 {
continue;
}
if free_regs == 0 {
break;
}
if size_in_regs > free_regs {
break;
}
free_regs -= size_in_regs;
if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer {
attrs.set(ArgAttribute::InReg);
}
if free_regs == 0 {
break;
}
}
}

View file

@ -2096,6 +2096,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.