diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 9cdc0801b1f00..7c71873b007c0 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -128,6 +128,19 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, RustcAbi) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(rustc_abi) => base.$key_name = Some(rustc_abi), + _ => return Some(Err(format!( + "'{s}' is not a valid value for rust-abi. \ + Use 'x86-softfloat' or 'x86-sse2'." + ))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, RelocModel) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -611,6 +624,7 @@ impl Target { key!(llvm_mcount_intrinsic, optional); key!(llvm_abiname); key!(llvm_floatabi, FloatAbi)?; + key!(rustc_abi, RustcAbi)?; key!(relax_elf_relocations, bool); key!(llvm_args, list); key!(use_ctors_section, bool); @@ -786,6 +800,7 @@ impl ToJson for Target { target_option_val!(llvm_mcount_intrinsic); target_option_val!(llvm_abiname); target_option_val!(llvm_floatabi); + target_option_val!(rustc_abi); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); target_option_val!(use_ctors_section); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bcd2aff54bb11..9fd28506d8fdf 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1114,6 +1114,33 @@ impl ToJson for FloatAbi { } } +/// The Rustc-specific variant of the ABI used for this target. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RustcAbi { + /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI/ + X86Softfloat, +} + +impl FromStr for RustcAbi { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "x86-softfloat" => RustcAbi::X86Softfloat, + _ => return Err(()), + }) + } +} + +impl ToJson for RustcAbi { + fn to_json(&self) -> Json { + match *self { + RustcAbi::X86Softfloat => "x86-softfloat", + } + .to_json() + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TlsModel { GeneralDynamic, @@ -2502,6 +2529,12 @@ pub struct TargetOptions { /// If not provided, LLVM will infer the float ABI from the target triple (`llvm_target`). pub llvm_floatabi: Option, + /// Picks a specific ABI for this target. This is *not* just for "Rust" ABI functions, + /// it can also affect "C" ABI functions; the point is that this flag is interpreted by + /// rustc and not forwarded to LLVM. + /// So far, this is only used on x86. + pub rustc_abi: Option, + /// Whether or not RelaxElfRelocation flag will be passed to the linker pub relax_elf_relocations: bool, @@ -2661,10 +2694,6 @@ impl TargetOptions { .collect(); } } - - pub(crate) fn has_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature)) - } } impl Default for TargetOptions { @@ -2770,6 +2799,7 @@ impl Default for TargetOptions { llvm_mcount_intrinsic: None, llvm_abiname: "".into(), llvm_floatabi: None, + rustc_abi: None, relax_elf_relocations: false, llvm_args: cvs![], use_ctors_section: false, @@ -3236,6 +3266,17 @@ impl Target { _ => {} } + // Check consistency of Rust ABI declaration. + if let Some(rust_abi) = self.rustc_abi { + match rust_abi { + RustcAbi::X86Softfloat => check_matches!( + &*self.arch, + "x86" | "x86_64", + "`x86-softfloat` ABI is only valid for x86 targets" + ), + } + } + // Check that the given target-features string makes some basic sense. if !self.features.is_empty() { let mut features_enabled = FxHashSet::default(); diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs index e481e967ebfa2..a2598d84a6f3e 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs @@ -2,6 +2,7 @@ use crate::spec::Target; pub(crate) fn target() -> Target { let mut base = super::i686_unknown_linux_gnu::target(); + base.rustc_abi = None; base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-gnu".into(); base diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs index 8ad93496f3a8c..9d7c4e5dc1853 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs @@ -2,6 +2,7 @@ use crate::spec::Target; pub(crate) fn target() -> Target { let mut base = super::i686_unknown_linux_musl::target(); + base.rustc_abi = None; base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-musl".into(); // FIXME(compiler-team#422): musl targets should be dynamically linked by default. diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs index 39d3a5a46330f..c1ed565f0fe68 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs @@ -5,7 +5,7 @@ // The cdecl ABI is used. It differs from the stdcall or fastcall ABI. // "i686-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{Target, base}; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -22,6 +22,7 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); // Use -GNU here, because of the reason below: // Background and Problem: diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs index cfefb1de99387..e14a367358944 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs @@ -5,8 +5,8 @@ // features. use crate::spec::{ - Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, SanitizerSet, StackProbeType, - Target, TargetOptions, + Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetOptions, }; pub(crate) fn target() -> Target { @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { relro_level: RelroLevel::Full, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), + rustc_abi: Some(RustcAbi::X86Softfloat), features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float".into(), supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, disable_redzone: true, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs index a11a79ff41a8e..8fd92e8123b99 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs @@ -6,7 +6,7 @@ // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. use crate::abi::call::Conv; -use crate::spec::{Target, base}; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -26,6 +26,7 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); Target { llvm_target: "x86_64-unknown-windows".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa1..e203003f8b702 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::{Symbol, sym}; -use crate::spec::{FloatAbi, Target}; +use crate::spec::{FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -770,23 +770,39 @@ impl Target { // questions "which ABI is used". match &*self.arch { "x86" => { - // We support 2 ABIs, hardfloat (default) and softfloat. - // x86 has no sane ABI indicator so we have to use the target feature. - if self.has_feature("soft-float") { - NOTHING - } else { - // Hardfloat ABI. x87 must be enabled. - FeatureConstraints { required: &["x87"], incompatible: &[] } + // We use our own ABI indicator here; LLVM does not have anything native. + match self.rustc_abi { + None => { + // Default hardfloat ABI. + // x87 must be enabled, soft-float must be disabled. + FeatureConstraints { required: &["x87"], incompatible: &["soft-float"] } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } } } "x86_64" => { - // We support 2 ABIs, hardfloat (default) and softfloat. - // x86 has no sane ABI indicator so we have to use the target feature. - if self.has_feature("soft-float") { - NOTHING - } else { - // Hardfloat ABI. x87 and SSE2 must be enabled. - FeatureConstraints { required: &["x87", "sse2"], incompatible: &[] } + // We use our own ABI indicator here; LLVM does not have anything native. + match self.rustc_abi { + None => { + // Default hardfloat ABI. On x86-64, this always includes SSE2. + FeatureConstraints { + required: &["x87", "sse2"], + incompatible: &["soft-float"], + } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } } } "arm" => {