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

@ -30,7 +30,7 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt};
use rustc_span::Span; use rustc_span::Span;
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_target::abi::call::FnAbi; use rustc_target::abi::call::FnAbi;
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi}; use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi};
use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::common::{SignType, TypeReflection, type_is_pointer};
use crate::context::CodegenCx; use crate::context::CodegenCx;
@ -2347,6 +2347,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 { pub trait ToGccComp {
fn to_gcc_comparison(&self) -> ComparisonOp; fn to_gcc_comparison(&self) -> ComparisonOp;
} }

View File

@ -19,7 +19,9 @@ use rustc_session::Session;
use rustc_span::source_map::respan; use rustc_span::source_map::respan;
use rustc_span::{DUMMY_SP, Span}; use rustc_span::{DUMMY_SP, Span};
use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; 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::callee::get_fn;
use crate::common::SignType; use crate::common::SignType;
@ -538,6 +540,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> { impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline] #[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {

View File

@ -836,6 +836,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(profile_emit, Some(PathBuf::from("abc"))); tracked!(profile_emit, Some(PathBuf::from("abc")));
tracked!(profile_sample_use, Some(PathBuf::from("abc"))); tracked!(profile_sample_use, Some(PathBuf::from("abc")));
tracked!(profiler_runtime, "abc".to_string()); tracked!(profiler_runtime, "abc".to_string());
tracked!(regparm, Some(3));
tracked!(relax_elf_relocations, Some(true)); tracked!(relax_elf_relocations, Some(true));
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc"))); tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
tracked!(sanitizer, SanitizerSet::ADDRESS); tracked!(sanitizer, SanitizerSet::ADDRESS);

View File

@ -21,7 +21,9 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_target::abi::call::FnAbi; use rustc_target::abi::call::FnAbi;
use rustc_target::abi::{FieldIdx, TyAbiInterface, VariantIdx, call}; use rustc_target::abi::{FieldIdx, TyAbiInterface, VariantIdx, call};
use rustc_target::spec::abi::Abi as SpecAbi; 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 tracing::debug;
use {rustc_abi as abi, rustc_hir as hir}; use {rustc_abi as abi, rustc_hir as hir};
@ -544,6 +546,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> { impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
#[inline] #[inline]
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
@ -595,6 +603,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> { impl<'tcx> HasTyCtxt<'tcx> for LayoutCx<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> {
self.calc.cx self.calc.cx

View File

@ -136,3 +136,6 @@ session_unsupported_crate_type_for_target =
dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`
session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5
session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3)
session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86

View File

@ -485,6 +485,16 @@ pub(crate) struct FunctionReturnRequiresX86OrX8664;
#[diag(session_function_return_thunk_extern_requires_non_large_code_model)] #[diag(session_function_return_thunk_extern_requires_non_large_code_model)]
pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel;
#[derive(Diagnostic)]
#[diag(session_unsupported_regparm)]
pub(crate) struct UnsupportedRegparm {
pub(crate) regparm: u32,
}
#[derive(Diagnostic)]
#[diag(session_unsupported_regparm_arch)]
pub(crate) struct UnsupportedRegparmArch;
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(session_failed_to_create_profiler)] #[diag(session_failed_to_create_profiler)]
pub(crate) struct FailedToCreateProfiler { pub(crate) struct FailedToCreateProfiler {

View File

@ -2000,6 +2000,10 @@ options! {
"enable queries of the dependency graph for regression testing (default: no)"), "enable queries of the dependency graph for regression testing (default: no)"),
randomize_layout: bool = (false, parse_bool, [TRACKED], randomize_layout: bool = (false, parse_bool, [TRACKED],
"randomize the layout of types (default: no)"), "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], relax_elf_relocations: Option<bool> = (None, parse_opt_bool, [TRACKED],
"whether ELF relocations can be relaxed"), "whether ELF relocations can be relaxed"),
remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],

View File

@ -1337,6 +1337,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
} }
} }
if let Some(regparm) = sess.opts.unstable_opts.regparm {
if regparm > 3 {
sess.dcx().emit_err(errors::UnsupportedRegparm { regparm });
}
if sess.target.arch != "x86" {
sess.dcx().emit_err(errors::UnsupportedRegparmArch);
}
}
// The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is // The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is
// kept as a `match` to force a change if new ones are added, even if we currently only support // kept as a `match` to force a change if new ones are added, even if we currently only support
// `thunk-extern` like Clang. // `thunk-extern` like Clang.

View File

@ -6,7 +6,7 @@ use rustc_macros::HashStable_Generic;
use rustc_span::Symbol; use rustc_span::Symbol;
use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; 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 aarch64;
mod amdgpu; mod amdgpu;
@ -631,7 +631,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
) -> Result<(), AdjustForForeignAbiError> ) -> Result<(), AdjustForForeignAbiError>
where where
Ty: TyAbiInterface<'a, C> + Copy, Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt, C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt,
{ {
if abi == spec::abi::Abi::X86Interrupt { if abi == spec::abi::Abi::X86Interrupt {
if let Some(arg) = self.args.first_mut() { if let Some(arg) = self.args.first_mut() {
@ -643,14 +643,18 @@ impl<'a, Ty> FnAbi<'a, Ty> {
let spec = cx.target_spec(); let spec = cx.target_spec();
match &spec.arch[..] { match &spec.arch[..] {
"x86" => { "x86" => {
let flavor = if let spec::abi::Abi::Fastcall { .. } let (flavor, regparm) = match abi {
| spec::abi::Abi::Vectorcall { .. } = abi spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => {
{ (x86::Flavor::FastcallOrVectorcall, None)
x86::Flavor::FastcallOrVectorcall }
} else { spec::abi::Abi::C { .. }
x86::Flavor::General | 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 { "x86_64" => match abi {
spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
@ -716,6 +720,28 @@ impl<'a, Ty> FnAbi<'a, Ty> {
Ok(()) 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 { impl FromStr for Conv {

View File

@ -8,7 +8,12 @@ pub(crate) enum Flavor {
FastcallOrVectorcall, 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 where
Ty: TyAbiInterface<'a, C> + Copy, Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout + HasTargetSpec, C: HasDataLayout + HasTargetSpec,
@ -128,58 +133,77 @@ where
} }
} }
if flavor == Flavor::FastcallOrVectorcall { fill_inregs(cx, fn_abi, opts, false);
// Mark arguments as InReg like clang does it, }
// so our fastcall/vectorcall is compatible with C/C++ fastcall/vectorcall.
// Clang reference: lib/CodeGen/TargetInfo.cpp pub(crate) fn fill_inregs<'a, Ty, C>(
// See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() 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, // Clang reference: lib/CodeGen/TargetInfo.cpp
// which in turn can't be x86? // 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() { // 2 for fastcall/vectorcall, regparm limited by 3 otherwise
let attrs = match arg.mode { let mut free_regs = opts.regparm.unwrap_or(2).into();
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)
}
};
// At this point we know this must be a primitive of sorts. // For types generating PassMode::Cast, InRegs will not be set.
let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap(); // Maybe, this is a FIXME
assert_eq!(unit.size, arg.layout.size); let has_casts = fn_abi.args.iter().any(|arg| matches!(arg.mode, PassMode::Cast { .. }));
if unit.kind == RegKind::Float { 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; continue;
} }
PassMode::Direct(ref mut attrs) => attrs,
let size_in_regs = (arg.layout.size.bits() + 31) / 32; PassMode::Pair(..)
| PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }
if size_in_regs == 0 { | PassMode::Cast { .. } => {
continue; unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
} }
};
if size_in_regs > free_regs { // At this point we know this must be a primitive of sorts.
break; 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 { if size_in_regs == 0 {
attrs.set(ArgAttribute::InReg); continue;
} }
if free_regs == 0 { if size_in_regs > free_regs {
break; 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; 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>; type StaticCow<T> = Cow<'static, T>;
/// Optional aspects of a target specification. /// Optional aspects of a target specification.

View File

@ -797,6 +797,9 @@ fn fn_abi_adjust_for_abi<'tcx>(
for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() { for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
fixup(arg, Some(arg_idx)); fixup(arg, Some(arg_idx));
} }
if tcx.sess.target.arch == "x86" {
fn_abi.fill_inregs_for_rust_abi(cx);
}
} else { } else {
fn_abi fn_abi
.adjust_for_foreign_abi(cx, abi) .adjust_for_foreign_abi(cx, abi)

View File

@ -0,0 +1,20 @@
# `regparm`
The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/131749.
------------------------
Option -Zregparm=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.
It is only supported on `x86`.
It is equivalent to [Clang]'s and [GCC]'s `-mregparm`.
Supported values for this option are 0-3.
[Clang]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mregparm
[GCC]: https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#index-regparm-function-attribute_002c-x86
Implementation details:
For eligible arguments, llvm `inreg` attribute is set.

View File

@ -0,0 +1,53 @@
// Checks how `regparm` flag works with Rust calling convention with array types.
// When there is a small array type in signature (casted to combined int type),
// inregs will not be set. PassMode::Cast is unsupported.
// x86 only.
//@ compile-flags: --target i686-unknown-linux-gnu -O -C no-prepopulate-passes
//@ needs-llvm-components: x86
//@ revisions:regparm0 regparm1 regparm2 regparm3
//@[regparm0] compile-flags: -Zregparm=0
//@[regparm1] compile-flags: -Zregparm=1
//@[regparm2] compile-flags: -Zregparm=2
//@[regparm3] compile-flags: -Zregparm=3
#![crate_type = "lib"]
#![no_core]
#![feature(no_core, lang_items)]
#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}
pub mod tests {
// CHECK: @f1(i16 %0, i32 noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "Rust" fn f1(_: [u8; 2], _: i32, _: i32) {}
// CHECK: @f2(i24 %0, i32 noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "Rust" fn f2(_: [u8; 3], _: i32, _: i32) {}
// regparm0: @f3(ptr {{.*}} %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f3(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm2: @f3(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
// regparm3: @f3(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "Rust" fn f3(_: [u8; 7], _: i32, _: i32) {}
// regparm0: @f4(ptr {{.*}} %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f4(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm2: @f4(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
// regparm3: @f4(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "Rust" fn f4(_: [u8; 11], _: i32, _: i32) {}
// regparm0: @f5(ptr {{.*}} %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f5(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm2: @f5(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
// regparm3: @f5(ptr {{.*}} %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "Rust" fn f5(_: [u8; 33], _: i32, _: i32) {}
}

View File

@ -0,0 +1,133 @@
// 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 -O -C no-prepopulate-passes
//@ needs-llvm-components: x86
//@ only-x86
//@ revisions:regparm0 regparm1 regparm2 regparm3
//@[regparm0] compile-flags: -Zregparm=0
//@[regparm1] compile-flags: -Zregparm=1
//@[regparm2] compile-flags: -Zregparm=2
//@[regparm3] compile-flags: -Zregparm=3
#![crate_type = "lib"]
#![no_core]
#![feature(no_core, lang_items, repr_simd)]
#[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) {}
// regparm0: @f2(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f2(i32 inreg noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm2: @f2(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm3: @f2(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "Rust" fn f2(_: i32, _: i32, _: i32) {}
// regparm0: @f3(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f3(i32 inreg noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm2: @f3(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm3: @f3(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "C" fn f3(_: i32, _: i32, _: i32) {}
// regparm0: @f4(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f4(i32 inreg noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm2: @f4(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm3: @f4(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3)
#[no_mangle]
pub extern "cdecl" fn f4(_: i32, _: i32, _: i32) {}
// regparm0: @f5(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm1: @f5(i32 inreg noundef %_1, i32 noundef %_2, i32 noundef %_3)
// regparm2: @f5(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
// regparm3: @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) {}
struct S1 {
x1: i32,
}
// regparm0: @f7(i32 noundef %_1, i32 noundef %_2, i32 noundef %_3, i32 noundef %_4)
// regparm1: @f7(i32 inreg noundef %_1, i32 noundef %_2, i32 noundef %_3, i32 noundef %_4)
// regparm2: @f7(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3, i32 noundef %_4)
// regparm3: @f7(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3,
// regparm3-SAME: i32 noundef %_4)
#[no_mangle]
pub extern "C" fn f7(_: i32, _: i32, _: S1, _: i32) {}
#[repr(C)]
struct S2 {
x1: i32,
x2: i32,
}
// regparm0: @f8(i32 noundef %_1, i32 noundef %_2, ptr {{.*}} %_3, i32 noundef %_4)
// regparm1: @f8(i32 inreg noundef %_1, i32 noundef %_2, ptr {{.*}} %_3, i32 noundef %_4)
// regparm2: @f8(i32 inreg noundef %_1, i32 inreg noundef %_2, ptr {{.*}} %_3, i32 noundef %_4)
// regparm3: @f8(i32 inreg noundef %_1, i32 inreg noundef %_2, ptr {{.*}} %_3,
// regparm3-SAME: i32 inreg noundef %_4)
#[no_mangle]
pub extern "C" fn f8(_: i32, _: i32, _: S2, _: i32) {}
// regparm0: @f9(i1 noundef zeroext %_1, i16 noundef signext %_2, i64 noundef %_3,
// regparm0-SAME: i128 noundef %_4)
// regparm1: @f9(i1 inreg noundef zeroext %_1, i16 noundef signext %_2, i64 noundef %_3,
// regparm1-SAME: i128 noundef %_4)
// regparm2: @f9(i1 inreg noundef zeroext %_1, i16 inreg noundef signext %_2, i64 noundef %_3,
// regparm2-SAME: i128 noundef %_4)
// regparm3: @f9(i1 inreg noundef zeroext %_1, i16 inreg noundef signext %_2, i64 noundef %_3,
// regparm3-SAME: i128 noundef %_4)
#[no_mangle]
pub extern "C" fn f9(_: bool, _: i16, _: i64, _: u128) {}
// regparm0: @f10(float noundef %_1, double noundef %_2, i1 noundef zeroext %_3,
// regparm0-SAME: i16 noundef signext %_4)
// regparm1: @f10(float noundef %_1, double noundef %_2, i1 inreg noundef zeroext %_3,
// regparm1-SAME: i16 noundef signext %_4)
// regparm2: @f10(float noundef %_1, double noundef %_2, i1 inreg noundef zeroext %_3,
// regparm2-SAME: i16 inreg noundef signext %_4)
// regparm3: @f10(float noundef %_1, double noundef %_2, i1 inreg noundef zeroext %_3,
// regparm3-SAME: i16 inreg noundef signext %_4)
#[no_mangle]
pub extern "C" fn f10(_: f32, _: f64, _: bool, _: i16) {}
#[allow(non_camel_case_types)]
#[repr(simd)]
pub struct __m128([f32; 4]);
// regparm0: @f11(i32 noundef %_1, <4 x float> %_2, i32 noundef %_3, i32 noundef %_4)
// regparm1: @f11(i32 inreg noundef %_1, <4 x float> %_2, i32 noundef %_3, i32 noundef %_4)
// regparm2: @f11(i32 inreg noundef %_1, <4 x float> %_2, i32 inreg noundef %_3,
// regparm2-SAME: i32 noundef %_4)
// regparm3: @f11(i32 inreg noundef %_1, <4 x float> %_2, i32 inreg noundef %_3,
// regparm3-SAME: i32 inreg noundef %_4)
#[no_mangle]
pub extern "C" fn f11(_: i32, _: __m128, _: i32, _: i32) {}
#[allow(non_camel_case_types)]
#[repr(simd)]
pub struct __m256([f32; 8]);
// regparm0: @f12(i32 noundef %_1, <8 x float> %_2, i32 noundef %_3, i32 noundef %_4)
// regparm1: @f12(i32 inreg noundef %_1, <8 x float> %_2, i32 noundef %_3, i32 noundef %_4)
// regparm2: @f12(i32 inreg noundef %_1, <8 x float> %_2, i32 inreg noundef %_3,
// regparm2-SAME: i32 noundef %_4)
// regparm3: @f12(i32 inreg noundef %_1, <8 x float> %_2, i32 inreg noundef %_3,
// regparm3-SAME: i32 inreg noundef %_4)
#[no_mangle]
pub extern "C" fn f12(_: i32, _: __m256, _: i32, _: i32) {}
}

View File

@ -0,0 +1,4 @@
error: `-Zregparm=4` is unsupported (valid values 0-3)
error: aborting due to 1 previous error

View File

@ -0,0 +1,24 @@
//@ revisions: regparm0 regparm1 regparm2 regparm3 regparm4
//@ needs-llvm-components: x86
//@ compile-flags: --target i686-unknown-linux-gnu
//@[regparm0] check-pass
//@[regparm0] compile-flags: -Zregparm=0
//@[regparm1] check-pass
//@[regparm1] compile-flags: -Zregparm=1
//@[regparm2] check-pass
//@[regparm2] compile-flags: -Zregparm=2
//@[regparm3] check-pass
//@[regparm3] compile-flags: -Zregparm=3
//@[regparm4] check-fail
//@[regparm4] compile-flags: -Zregparm=4
//@[regparm4] error-pattern: `-Zregparm=4` is unsupported (valid values 0-3)
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zregparm=N` is only supported on x86
error: aborting due to 1 previous error

View File

@ -0,0 +1,21 @@
//@ revisions: x86 x86_64 aarch64
//@ compile-flags: -Zregparm=3
//@[x86] check-pass
//@[x86] needs-llvm-components: x86
//@[x86] compile-flags: --target i686-unknown-linux-gnu
//@[x86_64] check-fail
//@[x86_64] needs-llvm-components: x86
//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86_64] error-pattern: `-Zregparm=N` is only supported on x86
//@[aarch64] check-fail
//@[aarch64] needs-llvm-components: aarch64
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
//@[aarch64] error-pattern: `-Zregparm=N` is only supported on x86
#![feature(no_core)]
#![no_core]
#![no_main]

View File

@ -0,0 +1,4 @@
error: `-Zregparm=N` is only supported on x86
error: aborting due to 1 previous error