mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 14:23:45 +00:00
rust_for_linux: -Zregparm=<N> commandline flag for X86 (#116972)
This commit is contained in:
parent
a0c2aba29a
commit
b3ae64d24f
@ -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;
|
||||
}
|
||||
|
@ -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>) -> ! {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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<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],
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
20
src/doc/unstable-book/src/compiler-flags/regparm.md
Normal file
20
src/doc/unstable-book/src/compiler-flags/regparm.md
Normal 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.
|
53
tests/codegen/regparm-inreg-rust-cc.rs
Normal file
53
tests/codegen/regparm-inreg-rust-cc.rs
Normal 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) {}
|
||||
}
|
133
tests/codegen/regparm-inreg.rs
Normal file
133
tests/codegen/regparm-inreg.rs
Normal 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) {}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
error: `-Zregparm=4` is unsupported (valid values 0-3)
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -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]
|
@ -0,0 +1,4 @@
|
||||
error: `-Zregparm=N` is only supported on x86
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
21
tests/ui/invalid-compile-flags/regparm/requires-x86.rs
Normal file
21
tests/ui/invalid-compile-flags/regparm/requires-x86.rs
Normal 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]
|
@ -0,0 +1,4 @@
|
||||
error: `-Zregparm=N` is only supported on x86
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
Loading…
Reference in New Issue
Block a user