From 0a8cfc2f8f1343fc99f18ca3cad8e2d11f60d7d2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Dec 2024 19:08:30 +0100 Subject: adjust GCC backend --- compiler/rustc_codegen_gcc/src/errors.rs | 1 + compiler/rustc_codegen_gcc/src/gcc_util.rs | 196 +++++++++++++++++------------ 2 files changed, 116 insertions(+), 81 deletions(-) (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 56849cc8610..c896246866b 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[diag(codegen_gcc_forbidden_ctarget_feature)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 058a874501b..b0fd07827d6 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,9 +1,11 @@ +use std::iter::FromIterator; + #[cfg(feature = "master")] use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::bug; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; @@ -37,82 +39,128 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); - } - return None; - } - }; - // Get the backend feature name, if any. - // This excludes rustc-specific features, that do not get passed down to GCC. - let feature = backend_feature_name(s)?; - // Warn against use of GCC specific feature names on the CLI. + // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones + // are disabled. + let (abi_enable, abi_disable) = sess.target.abi_required_features(); + let abi_enable_set = FxHashSet::from_iter(abi_enable.iter().copied()); + let abi_disable_set = FxHashSet::from_iter(abi_disable.iter().copied()); + + // Compute implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + if let Some(feature) = feature.strip_prefix('+') { + all_rust_features.extend( + UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + .to_sorted_stable_ord() + .iter() + .map(|&&s| (true, s)), + ) + } else if let Some(feature) = feature.strip_prefix('-') { + // FIXME: Why do we not remove implied features on "-" here? + // We do the equivalent above in `target_features_cfg`. + // See . + all_rust_features.push((false, feature)); + } else if !feature.is_empty() { if diagnostics { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let gcc_features = to_gcc_features(sess, rust_feature); - if gcc_features.contains(&feature) - && !gcc_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - if let Err(reason) = - stability.toggle_allowed(&sess.target, enable_disable == '+') + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); + } + } + } + // Remove features that are meant for rustc, not LLVM. + all_rust_features.retain(|(_, feature)| { + // Retain if it is not a rustc feature + !RUSTC_SPECIFIC_FEATURES.contains(feature) + }); + + // Check feature validity. + if diagnostics { + for &(enable, feature) in &all_rust_features { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { + let gcc_features = to_gcc_features(sess, rust_feature); + if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. (It makes little sense - // to hard-error here since we just warn about fully unknown - // features above). - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + Some(rust_feature) + } else { + None } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. (It makes little sense + // to hard-error here since we just warn about fully unknown + // features above). + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); } } + } - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); + // Ensure that the features we enable/disable are compatible with the ABI. + if enable { + if abi_disable_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "enabled", + reason: "this feature is incompatible with the target ABI", + }); + } + } else { + if abi_enable_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "disabled", + reason: "this feature is required by the target ABI", + }); + } } - // ... otherwise though we run through `to_gcc_features` when + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable); + } + } + + // To be sure the ABI-relevant features are all in the right state, we explicitly + // (un)set them here. This means if the target spec sets those features wrong, + // we will silently correct them rather than silently producing wrong code. + // (The target sanity check tries to catch this, but we can't know which features are + // enabled in GCC by default so we can't be fully sure about that check.) + for feature in abi_enable { + all_rust_features.push((true, feature)); + } + for feature in abi_disable { + all_rust_features.push((false, feature)); + } + + // Translate this into GCC features. + let feats = all_rust_features + .iter() + .filter_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; + // We run through `to_gcc_features` when // passing requests down to GCC. This means that all in-language // features also work on the command line instead of having two // different names when the GCC name and the Rust name differ. @@ -146,26 +194,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Option<&str> { - // features must start with a `+` or `-`. - let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| { - bug!("target feature `{}` must begin with a `+` or `-`", s); - }); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to GCC. - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - // To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; match (arch, s) { + // FIXME: seems like x87 does not exist? + ("x86", "x87") => smallvec![], ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"], ("x86", "pclmulqdq") => smallvec!["pclmul"], ("x86", "rdrand") => smallvec!["rdrnd"], -- cgit 1.4.1-3-g733a5 From eb527424a5f0206a464d0968387d85636ac9d305 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 26 Dec 2024 19:47:41 +0100 Subject: x86-64 hardfloat actually requires sse2 --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 18 ++++++++++++------ compiler/rustc_codegen_llvm/src/llvm_util.rs | 19 +++++++++++++------ compiler/rustc_target/src/target_features.rs | 11 ++++++++++- tests/codegen/target-feature-overrides.rs | 4 ++-- ...n-hardfloat-target-feature-flag-disable-implied.rs | 10 ++++++++++ ...rdfloat-target-feature-flag-disable-implied.stderr | 7 +++++++ 6 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs create mode 100644 tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index b0fd07827d6..3e579d75d86 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -129,12 +129,18 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec { + "x86" => { // We support 2 ABIs, hardfloat (default) and softfloat. if self.has_feature("soft-float") { NOTHING @@ -762,6 +762,15 @@ impl Target { (&["x87"], &[]) } } + "x86_64" => { + // We support 2 ABIs, hardfloat (default) and softfloat. + if self.has_feature("soft-float") { + NOTHING + } else { + // Hardfloat ABI. x87 and SSE2 must be enabled. + (&["x87", "sse2"], &[]) + } + } "arm" => { // We support 2 ABIs, hardfloat (default) and softfloat. if self.has_feature("soft-float") { diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index f38a1ae72de..c4f9a8898a1 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -40,7 +40,7 @@ pub unsafe fn banana() -> u32 { // CHECK: attributes [[APPLEATTRS]] // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+x87,+sse2,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] // COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+x87,+sse2" diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs new file mode 100644 index 00000000000..3d09217327a --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-sse +// For now this is just a warning. +//@ build-pass +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr new file mode 100644 index 00000000000..72b2d03fe20 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -0,0 +1,7 @@ +warning: target feature `sse` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + -- cgit 1.4.1-3-g733a5 From 912b7291d0f8f477388282a04ccf085d1b3715b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 27 Dec 2024 22:19:55 +0100 Subject: add ABI target features *before* -Ctarget-features --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 12 ++++++------ compiler/rustc_codegen_llvm/src/llvm_util.rs | 12 ++++++------ compiler/rustc_target/src/target_features.rs | 3 +++ tests/codegen/target-feature-overrides.rs | 8 ++++---- 4 files changed, 19 insertions(+), 16 deletions(-) (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 3e579d75d86..e9dfb4e4cf4 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -154,12 +154,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+x87,+sse2,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+x87,+sse2" +// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx" -- cgit 1.4.1-3-g733a5 From 2e64b5352be9ec8b2a7b956c2be108394b85f4b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 Jan 2025 10:34:33 +0100 Subject: add dedicated type for ABI target feature constraints --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 19 ++++++++------- compiler/rustc_codegen_llvm/src/llvm_util.rs | 17 ++++++++------ compiler/rustc_codegen_ssa/src/target_features.rs | 4 ++-- compiler/rustc_target/src/spec/mod.rs | 8 +++---- compiler/rustc_target/src/target_features.rs | 28 +++++++++++++++-------- 5 files changed, 45 insertions(+), 31 deletions(-) (limited to 'compiler/rustc_codegen_gcc/src') diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index e9dfb4e4cf4..1994a2a3c53 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -47,9 +47,9 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec Vec Vec Vec &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { @@ -749,8 +757,8 @@ impl Target { /// All features enabled/disabled via `-Ctarget-features` and `#[target_features]` are checked /// against this. We also check any implied features, based on the information above. If LLVM /// implicitly enables more implied features than we do, that could bypass this check! - pub fn abi_required_features(&self) -> (&'static [&'static str], &'static [&'static str]) { - const NOTHING: (&'static [&'static str], &'static [&'static str]) = (&[], &[]); + pub fn abi_required_features(&self) -> FeatureConstraints { + const NOTHING: FeatureConstraints = FeatureConstraints { required: &[], incompatible: &[] }; // Some architectures don't have a clean explicit ABI designation; instead, the ABI is // defined by target features. When that is the case, those target features must be // "forbidden" in the list above to ensure that there is a consistent answer to the @@ -763,7 +771,7 @@ impl Target { NOTHING } else { // Hardfloat ABI. x87 must be enabled. - (&["x87"], &[]) + FeatureConstraints { required: &["x87"], incompatible: &[] } } } "x86_64" => { @@ -773,7 +781,7 @@ impl Target { NOTHING } else { // Hardfloat ABI. x87 and SSE2 must be enabled. - (&["x87", "sse2"], &[]) + FeatureConstraints { required: &["x87", "sse2"], incompatible: &[] } } } "arm" => { @@ -786,7 +794,7 @@ impl Target { } FloatAbi::Hard => { // Must have `fpregs` and must not have `soft-float`. - (&["fpregs"], &["soft-float"]) + FeatureConstraints { required: &["fpregs"], incompatible: &["soft-float"] } } } } @@ -803,7 +811,7 @@ impl Target { _ => { // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. // These are Rust feature names and we use "neon" to control both of them. - (&["neon"], &[]) + FeatureConstraints { required: &["neon"], incompatible: &[] } } } } @@ -813,15 +821,15 @@ impl Target { match &*self.llvm_abiname { "ilp32d" | "lp64d" => { // Requires d (which implies f), incompatible with e. - (&["d"], &["e"]) + FeatureConstraints { required: &["d"], incompatible: &["e"] } } "ilp32f" | "lp64f" => { // Requires f, incompatible with e. - (&["f"], &["e"]) + FeatureConstraints { required: &["f"], incompatible: &["e"] } } "ilp32" | "lp64" => { // Requires nothing, incompatible with e. - (&[], &["e"]) + FeatureConstraints { required: &[], incompatible: &["e"] } } "ilp32e" => { // ilp32e is documented to be incompatible with features that need aligned @@ -832,7 +840,7 @@ impl Target { // Note that the `e` feature is not required: the ABI treats the extra // registers as caller-save, so it is safe to use them only in some parts of // a program while the rest doesn't know they even exist. - (&[], &["d"]) + FeatureConstraints { required: &[], incompatible: &["d"] } } "lp64e" => { // As above, `e` is not required. -- cgit 1.4.1-3-g733a5