From b3ae64d24fc323365bd09fe6ac8b7438f5713078 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_interface/src/tests.rs | 1 + compiler/rustc_middle/src/ty/layout.rs | 16 ++- compiler/rustc_session/messages.ftl | 3 + compiler/rustc_session/src/errors.rs | 10 ++ compiler/rustc_session/src/options.rs | 4 + compiler/rustc_session/src/session.rs | 9 ++ compiler/rustc_target/src/callconv/mod.rs | 44 ++++-- compiler/rustc_target/src/callconv/x86.rs | 106 ++++++++------ compiler/rustc_target/src/spec/mod.rs | 12 ++ compiler/rustc_ty_utils/src/abi.rs | 3 + .../src/compiler-flags/regparm.md | 20 +++ tests/codegen/regparm-inreg-rust-cc.rs | 53 +++++++ tests/codegen/regparm-inreg.rs | 133 ++++++++++++++++++ .../regparm-valid-values.regparm4.stderr | 4 + .../regparm/regparm-valid-values.rs | 24 ++++ .../regparm/requires-x86.aarch64.stderr | 4 + .../regparm/requires-x86.rs | 21 +++ .../regparm/requires-x86.x86_64.stderr | 4 + 20 files changed, 436 insertions(+), 53 deletions(-) create mode 100644 src/doc/unstable-book/src/compiler-flags/regparm.md create mode 100644 tests/codegen/regparm-inreg-rust-cc.rs create mode 100644 tests/codegen/regparm-inreg.rs create mode 100644 tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr create mode 100644 tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs create mode 100644 tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr create mode 100644 tests/ui/invalid-compile-flags/regparm/requires-x86.rs create mode 100644 tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index b611f9ba8bc..45738068509 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; 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::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 { 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 7cb49bf7991..707b35967a6 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -19,7 +19,9 @@ use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Span}; 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; @@ -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> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3189620e969..d3762e739db 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -836,6 +836,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); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6c12b691c26..99a4cb56dd3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -21,7 +21,9 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{FieldIdx, TyAbiInterface, VariantIdx, call}; 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 {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> { #[inline] 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> { fn tcx(&self) -> TyCtxt<'tcx> { self.calc.cx diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 1816d1278fe..893c532f1fb 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -136,3 +136,6 @@ session_unsupported_crate_type_for_target = 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_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) +session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index dbb74d1e244..20e8fb38b88 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -485,6 +485,16 @@ pub(crate) struct FunctionReturnRequiresX86OrX8664; #[diag(session_function_return_thunk_extern_requires_non_large_code_model)] 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)] #[diag(session_failed_to_create_profiler)] pub(crate) struct FailedToCreateProfiler { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index f9964b59a94..f4a9d4bf92c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2000,6 +2000,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 = (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 = (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_session/src/session.rs b/compiler/rustc_session/src/session.rs index 27879d817b2..1963cf4eb7c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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 // kept as a `match` to force a change if new ones are added, even if we currently only support // `thunk-extern` like Clang. diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 832246495bc..07ee3f892b7 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -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(&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 { diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index d9af83d3205..40c3e7a891a 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -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, +} + +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; } } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 82e11a3afce..812edf14070 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -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, +} + +pub trait HasX86AbiOpt { + fn x86_abi_opt(&self) -> X86Abi; +} + type StaticCow = Cow<'static, T>; /// Optional aspects of a target specification. diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index deda16b76b5..661f140e697 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -797,6 +797,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) diff --git a/src/doc/unstable-book/src/compiler-flags/regparm.md b/src/doc/unstable-book/src/compiler-flags/regparm.md new file mode 100644 index 00000000000..a054d55cd8b --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/regparm.md @@ -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. diff --git a/tests/codegen/regparm-inreg-rust-cc.rs b/tests/codegen/regparm-inreg-rust-cc.rs new file mode 100644 index 00000000000..a2d8d5349ea --- /dev/null +++ b/tests/codegen/regparm-inreg-rust-cc.rs @@ -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) {} +} diff --git a/tests/codegen/regparm-inreg.rs b/tests/codegen/regparm-inreg.rs new file mode 100644 index 00000000000..ce87a66d0e9 --- /dev/null +++ b/tests/codegen/regparm-inreg.rs @@ -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) {} +} diff --git a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr new file mode 100644 index 00000000000..8fc04adf57f --- /dev/null +++ b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr @@ -0,0 +1,4 @@ +error: `-Zregparm=4` is unsupported (valid values 0-3) + +error: aborting due to 1 previous error + diff --git a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs new file mode 100644 index 00000000000..b548d678520 --- /dev/null +++ b/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs @@ -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] diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr b/tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr new file mode 100644 index 00000000000..2433519f803 --- /dev/null +++ b/tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr @@ -0,0 +1,4 @@ +error: `-Zregparm=N` is only supported on x86 + +error: aborting due to 1 previous error + diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.rs b/tests/ui/invalid-compile-flags/regparm/requires-x86.rs new file mode 100644 index 00000000000..ce6e437fb47 --- /dev/null +++ b/tests/ui/invalid-compile-flags/regparm/requires-x86.rs @@ -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] diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr b/tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr new file mode 100644 index 00000000000..2433519f803 --- /dev/null +++ b/tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr @@ -0,0 +1,4 @@ +error: `-Zregparm=N` is only supported on x86 + +error: aborting due to 1 previous error +