diff options
| author | bors <bors@rust-lang.org> | 2025-01-05 23:21:06 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-01-05 23:21:06 +0000 |
| commit | feb32c654619e50eda8408f9241d4cb7b948fea5 (patch) | |
| tree | 6630efc09f499b1658fb9272a93f27b9289f278b | |
| parent | b3b368a1833a26f5d48f51c45159f46e6cd01711 (diff) | |
| parent | 2e64b5352be9ec8b2a7b956c2be108394b85f4b0 (diff) | |
| download | rust-feb32c654619e50eda8408f9241d4cb7b948fea5.tar.gz rust-feb32c654619e50eda8408f9241d4cb7b948fea5.zip | |
Auto merge of #134794 - RalfJung:abi-required-target-features, r=workingjubilee
Add a notion of "some ABIs require certain target features" I think I finally found the right shape for the data and checks that I recently added in https://github.com/rust-lang/rust/pull/133099, https://github.com/rust-lang/rust/pull/133417, https://github.com/rust-lang/rust/pull/134337: we have a notion of "this ABI requires the following list of target features, and it is incompatible with the following list of target features". Both `-Ctarget-feature` and `#[target_feature]` are updated to ensure we follow the rules of the ABI. This removes all the "toggleability" stuff introduced before, though we do keep the notion of a fully "forbidden" target feature -- this is needed to deal with target features that are actual ABI switches, and hence are needed to even compute the list of required target features. We always explicitly (un)set all required and in-conflict features, just to avoid potential trouble caused by the default features of whatever the base CPU is. We do this *before* applying `-Ctarget-feature` to maintain backward compatibility; this poses a slight risk of missing some implicit feature dependencies in LLVM but has the advantage of not breaking users that deliberately toggle ABI-relevant target features. They get a warning but the feature does get toggled the way they requested. For now, our logic supports x86, ARM, and RISC-V (just like the previous logic did). Unsurprisingly, RISC-V is the nicest. ;) As a side-effect this also (unstably) allows *enabling* `x87` when that is harmless. I used the opportunity to mark SSE2 as required on x86-64, to better match the actual logic in LLVM and because all x86-64 chips do have SSE2. This infrastructure also prepares us for requiring SSE on x86-32 when we want to use that for our ABI (and for float semantics sanity), see https://github.com/rust-lang/rust/issues/133611, but no such change is happening in this PR. r? `@workingjubilee`
25 files changed, 831 insertions, 787 deletions
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..1994a2a3c53 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,137 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri let mut features = vec![]; // Features implied by an implicit or explicit `--target`. - features.extend( - sess.target - .features - .split(',') - .filter(|v| !v.is_empty() && backend_feature_name(v).is_some()) - .map(String::from), - ); + features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from)); // -Ctarget-features let known_features = sess.target.rust_target_features(); let mut featsmap = FxHashMap::default(); - let feats = sess - .opts - .cg - .target_feature - .split(',') - .filter_map(|s| { - let enable_disable = match s.chars().next() { - None => 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_feature_constraints = sess.target.abi_required_features(); + let abi_incompatible_set = + FxHashSet::from_iter(abi_feature_constraints.incompatible.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 <https://github.com/rust-lang/rust/issues/134792>. + 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 codegen. + 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_incompatible_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "enabled", + reason: "this feature is incompatible with the target ABI", + }); + } + } else { + // FIXME: we have to request implied features here since + // negative features do not handle implied features above. + for &required in abi_feature_constraints.required.iter() { + let implied = sess.target.implied_target_features(std::iter::once(required)); + if implied.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.) + // We add these at the beginning of the list so that `-Ctarget-features` can + // still override it... that's unsound, but more compatible with past behavior. + all_rust_features.splice( + 0..0, + abi_feature_constraints + .required + .iter() + .map(|&f| (true, f)) + .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), + ); + + // 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 +203,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri features } -/// Returns a feature name for the given `+feature` or `-feature` string. -/// -/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) -fn backend_feature_name(s: &str) -> 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"], diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3982c37528d..9585848cbf0 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -10,7 +10,7 @@ codegen_llvm_dynamic_linking_with_lto = codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_forbidden_ctarget_feature = - target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} + target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344> @@ -24,8 +24,6 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two = codegen_llvm_invalid_minimum_alignment_too_large = invalid minimum global alignment: {$align} is too large -codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`" - codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index f340b06e876..f4c9491f758 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -37,6 +37,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[note(codegen_llvm_forbidden_ctarget_feature_issue)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } @@ -214,12 +215,6 @@ pub(crate) struct MismatchedDataLayout<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_invalid_target_feature_prefix)] -pub(crate) struct InvalidTargetFeaturePrefix<'a> { - pub feature: &'a str, -} - -#[derive(Diagnostic)] #[diag(codegen_llvm_fixed_x18_invalid_arch)] pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 628c0b1c29c..e18e91e569f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU use crate::back::write::create_informational_target_machine; use crate::errors::{ - FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature, - UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, + FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, + UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm; @@ -348,7 +348,16 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { if enabled { // Also add all transitively implied features. - features.extend(sess.target.implied_target_features(std::iter::once(feature))); + + // We don't care about the order in `features` since the only thing we use it for is the + // `features.contains` below. + #[allow(rustc::potential_query_instability)] + features.extend( + sess.target + .implied_target_features(std::iter::once(feature.as_str())) + .iter() + .map(|s| Symbol::intern(s)), + ); } else { // Remove transitively reverse-implied features. @@ -356,7 +365,11 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> // `features.contains` below. #[allow(rustc::potential_query_instability)] features.retain(|f| { - if sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) { + if sess + .target + .implied_target_features(std::iter::once(f.as_str())) + .contains(&feature.as_str()) + { // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to // remove `f`. (This is the standard logical contraposition principle.) false @@ -638,7 +651,7 @@ pub(crate) fn global_llvm_features( sess.target .features .split(',') - .filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some()) + .filter(|v| !v.is_empty()) // Drop +v8plus feature introduced in LLVM 20. .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) .map(String::from), @@ -651,89 +664,136 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { let known_features = sess.target.rust_target_features(); + // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // insert implied features + // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones + // are disabled. + let abi_feature_constraints = sess.target.abi_required_features(); + let abi_incompatible_set = + FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); + + // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { - match feature.strip_prefix('+') { - Some(feature) => all_rust_features.extend( - UnordSet::from( - sess.target - .implied_target_features(std::iter::once(Symbol::intern(feature))), - ) - .to_sorted_stable_ord() - .iter() - .map(|s| format!("+{}", s.as_str())), - ), - _ => all_rust_features.push(feature.to_string()), + 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 <https://github.com/rust-lang/rust/issues/134792>. + all_rust_features.push((false, feature)); + } else if !feature.is_empty() { + if diagnostics { + 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) + }); - let feats = all_rust_features - .iter() - .filter_map(|s| { - let enable_disable = match s.chars().next() { - None => 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, which do not get passed to LLVM. - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - 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 llvm_features = to_llvm_features(sess, rust_feature)?; - if llvm_features.contains(feature) - && !llvm_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 }, + // 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 llvm_features = to_llvm_features(sess, rust_feature)?; + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None } - } 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(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 }); + }); + 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_incompatible_set.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "enabled", + reason: "this feature is incompatible with the target ABI", + }); + } + } else { + // FIXME: we have to request implied features here since + // negative features do not handle implied features above. + for &required in abi_feature_constraints.required.iter() { + let implied = + sess.target.implied_target_features(std::iter::once(required)); + if implied.contains(feature) { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: "disabled", + reason: "this feature is required by the target ABI", + }); + } + } } + // 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 LLVM by default so we can't be fully sure about that check.) + // We add these at the beginning of the list so that `-Ctarget-features` can + // still override it... that's unsound, but more compatible with past behavior. + all_rust_features.splice( + 0..0, + abi_feature_constraints + .required + .iter() + .map(|&f| (true, f)) + .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), + ); + + // Translate this into LLVM features. + let feats = all_rust_features + .iter() + .filter_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; // We run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two @@ -746,9 +806,9 @@ pub(crate) fn global_llvm_features( enable_disable, llvm_feature.llvm_feature_name )) .chain(llvm_feature.dependency.into_iter().filter_map( - move |feat| match (enable_disable, feat) { - ('-' | '+', TargetFeatureFoldStrength::Both(f)) - | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { + move |feat| match (enable, feat) { + (_, TargetFeatureFoldStrength::Both(f)) + | (true, TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, @@ -780,22 +840,6 @@ pub(crate) fn global_llvm_features( features } -/// Returns a feature name for the given `+feature` or `-feature` string. -/// -/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) -fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> { - // features must start with a `+` or `-`. - let feature = s - .strip_prefix(&['+', '-'][..]) - .unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s })); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to LLVM. - if s.is_empty() || RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { let name = sess.opts.unstable_opts.tune_cpu.as_ref()?; Some(handle_native(name)) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 56188714b44..484f467068a 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -67,7 +67,7 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error} codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` codegen_ssa_forbidden_target_feature_attr = - target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason} + target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason} codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7e80d014ea2..d8b9bdb55da 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -19,7 +19,7 @@ use crate::errors; pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, attr: &hir::Attribute, - rust_target_features: &UnordMap<String, target_features::StabilityComputed>, + rust_target_features: &UnordMap<String, target_features::Stability>, target_features: &mut Vec<TargetFeature>, ) { let Some(list) = attr.meta_item_list() else { return }; @@ -32,7 +32,7 @@ pub(crate) fn from_target_feature_attr( .emit(); }; let rust_features = tcx.features(); - let mut added_target_features = Vec::new(); + let abi_feature_constraints = tcx.sess.target.abi_required_features(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -47,7 +47,7 @@ pub(crate) fn from_target_feature_attr( }; // We allow comma separation to enable multiple features. - added_target_features.extend(value.as_str().split(',').filter_map(|feature| { + for feature in value.as_str().split(',') { let Some(stability) = rust_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -59,12 +59,12 @@ pub(crate) fn from_target_feature_attr( } } err.emit(); - return None; + continue; }; // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.toggle_allowed(/*enable*/ true) { + if let Err(reason) = stability.toggle_allowed() { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, @@ -80,31 +80,25 @@ pub(crate) fn from_target_feature_attr( format!("the target feature `{feature}` is currently unstable"), ) .emit(); + } else { + // Add this and the implied features. + let feature_sym = Symbol::intern(feature); + for &name in tcx.implied_target_features(feature_sym) { + // But ensure the ABI does not forbid enabling this. + // Here we do assume that LLVM doesn't add even more implied features + // we don't know about, at least no features that would have ABI effects! + if abi_feature_constraints.incompatible.contains(&name.as_str()) { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature: name.as_str(), + reason: "this feature is incompatible with the target ABI", + }); + } + target_features.push(TargetFeature { name, implied: name != feature_sym }) + } } - Some(Symbol::intern(feature)) - })); - } - - // Add explicit features - target_features.extend( - added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), - ); - - // Add implied features - let mut implied_target_features = UnordSet::new(); - for feature in added_target_features.iter() { - implied_target_features.extend(tcx.implied_target_features(*feature).clone()); - } - for feature in added_target_features.iter() { - implied_target_features.remove(feature); + } } - target_features.extend( - implied_target_features - .into_sorted_stable_ord() - .iter() - .copied() - .map(|name| TargetFeature { name, implied: true }), - ) } /// Computes the set of target features used in a function for the purposes of @@ -147,25 +141,28 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - let target = &tcx.sess.target; if tcx.sess.opts.actually_rustdoc { // rustdoc needs to be able to document functions that use all the features, so // whitelist them all rustc_target::target_features::all_rust_features() - .map(|(a, b)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b)| (a.to_string(), b)) .collect() } else { tcx.sess .target .rust_target_features() .iter() - .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b, _)| (a.to_string(), *b)) .collect() } }, - implied_target_features: |tcx, feature| { + implied_target_features: |tcx, feature: Symbol| { + let feature = feature.as_str(); UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) .into_sorted_stable_ord() + .into_iter() + .map(|s| Symbol::intern(s)) + .collect() }, asm_target_features, ..*providers diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 241767fe249..16d868300db 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,6 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option<u16>, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). + /// Implied target features have already been applied. pub target_features: Vec<TargetFeature>, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option<Linkage>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c960f03cf06..95995b956cd 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2357,7 +2357,7 @@ rustc_queries! { } /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! - query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::StabilityComputed> { + query rust_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> { arena_cache eval_always desc { "looking up Rust target features" } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index bfe6f444147..ddfa6e248c3 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2653,10 +2653,6 @@ impl TargetOptions { 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)) } - - pub(crate) fn has_neg_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 { @@ -3203,7 +3199,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "riscv64" => { @@ -3211,7 +3208,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "arm" => { @@ -3245,6 +3243,26 @@ impl Target { )); } } + // Check that we don't mis-set any of the ABI-relevant features. + let abi_feature_constraints = self.abi_required_features(); + for feat in abi_feature_constraints.required { + // The feature might be enabled by default so we can't *require* it to show up. + // But it must not be *disabled*. + if features_disabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is required by the ABI but gets disabled in target spec" + )); + } + } + for feat in abi_feature_constraints.incompatible { + // The feature might be disabled by default so we can't *require* it to show up. + // But it must not be *enabled*. + if features_enabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is incompatible with the ABI but gets enabled in target spec" + )); + } + } } Ok(()) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5372437b0d2..f594d20f928 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::Target; +use crate::spec::{FloatAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -17,60 +17,34 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// Stability information for target features. -/// `Toggleability` is the type storing whether (un)stable features can be toggled: -/// this is initially a function since it can depend on `Target`, but for stable hashing -/// it needs to be something hashable to we have to make the type generic. -#[derive(Debug, Clone)] -pub enum Stability<Toggleability> { +#[derive(Debug, Copy, Clone)] +pub enum Stability { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. - Stable { - /// When enabling/disabling the feature via `-Ctarget-feature` or `#[target_feature]`, - /// determine if that is allowed. - allow_toggle: Toggleability, - }, + Stable, /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature. - Unstable { + Unstable( /// This must be a *language* feature, or else rustc will ICE when reporting a missing /// feature gate! - nightly_feature: Symbol, - /// See `Stable::allow_toggle` comment above. - allow_toggle: Toggleability, - }, + Symbol, + ), /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be /// set in the target spec. It is never set in `cfg(target_feature)`. Used in - /// particular for features that change the floating-point ABI. + /// particular for features are actually ABI configuration flags (not all targets are as nice as + /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, } +use Stability::*; -/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not. -/// The `bool` indicates whether the feature is being enabled (`true`) or disabled. -pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>; - -/// The computed result of whether a feature can be enabled/disabled on the current target. -#[derive(Debug, Clone)] -pub struct AllowToggleComputed { - enable: Result<(), &'static str>, - disable: Result<(), &'static str>, -} - -/// `Stability` where `allow_toggle` has not been computed yet. -pub type StabilityUncomputed = Stability<AllowToggleUncomputed>; -/// `Stability` where `allow_toggle` has already been computed. -pub type StabilityComputed = Stability<AllowToggleComputed>; - -impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleability> { +impl<CTX> HashStable<CTX> for Stability { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - Stability::Stable { allow_toggle } => { - allow_toggle.hash_stable(hcx, hasher); - } - Stability::Unstable { nightly_feature, allow_toggle } => { + Stability::Stable => {} + Stability::Unstable(nightly_feature) => { nightly_feature.hash_stable(hcx, hasher); - allow_toggle.hash_stable(hcx, hasher); } Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); @@ -79,16 +53,7 @@ impl<CTX, Toggleability: HashStable<CTX>> HashStable<CTX> for Stability<Toggleab } } -impl<CTX> HashStable<CTX> for AllowToggleComputed { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - let AllowToggleComputed { enable, disable } = self; - enable.hash_stable(hcx, hasher); - disable.hash_stable(hcx, hasher); - } -} - -impl<Toggleability> Stability<Toggleability> { +impl Stability { /// Returns whether the feature can be used in `cfg(target_feature)` ever. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) @@ -106,59 +71,23 @@ impl<Toggleability> Stability<Toggleability> { /// - for `cfg(target_feature)`, check `in_cfg` pub fn requires_nightly(&self) -> Option<Symbol> { match *self { - Stability::Unstable { nightly_feature, .. } => Some(nightly_feature), + Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), } } -} -impl StabilityUncomputed { - pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed { - use Stability::*; - let compute = |f: AllowToggleUncomputed| AllowToggleComputed { - enable: f(target, true), - disable: f(target, false), - }; - match *self { - Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) }, - Unstable { nightly_feature, allow_toggle } => { - Unstable { nightly_feature, allow_toggle: compute(allow_toggle) } - } - Forbidden { reason } => Forbidden { reason }, - } - } - - pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> { - use Stability::*; - match *self { - Stable { allow_toggle } => allow_toggle(target, enable), - Unstable { allow_toggle, .. } => allow_toggle(target, enable), - Forbidden { reason } => Err(reason), - } - } -} - -impl StabilityComputed { /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> { - let allow_toggle = match self { - Stability::Stable { allow_toggle } => allow_toggle, - Stability::Unstable { allow_toggle, .. } => allow_toggle, - Stability::Forbidden { reason } => return Err(reason), - }; - if enable { allow_toggle.enable } else { allow_toggle.disable } + pub fn toggle_allowed(&self) -> Result<(), &'static str> { + match self { + Stability::Forbidden { reason } => Err(reason), + _ => Ok(()), + } } } -// Constructors for the list below, defaulting to "always allow toggle". -const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) }; -const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { - Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) } -} - // Here we list target features that rustc "understands": they can be used in `#[target_feature]` // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // `-Ctarget-feature`. @@ -204,218 +133,182 @@ const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { // Both of these are also applied transitively. type ImpliedFeatures = &'static [&'static str]; -const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", unstable(sym::arm_target_feature), &[]), - ("aes", unstable(sym::arm_target_feature), &["neon"]), - ("crc", unstable(sym::arm_target_feature), &[]), - ("d32", unstable(sym::arm_target_feature), &[]), - ("dotprod", unstable(sym::arm_target_feature), &["neon"]), - ("dsp", unstable(sym::arm_target_feature), &[]), - ("fp-armv8", unstable(sym::arm_target_feature), &["vfp4"]), - ( - "fpregs", - Stability::Unstable { - nightly_feature: sym::arm_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), - ("i8mm", unstable(sym::arm_target_feature), &["neon"]), - ("mclass", unstable(sym::arm_target_feature), &[]), - ("neon", unstable(sym::arm_target_feature), &["vfp3"]), - ("rclass", unstable(sym::arm_target_feature), &[]), - ("sha2", unstable(sym::arm_target_feature), &["neon"]), - ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("fpregs", Unstable(sym::arm_target_feature), &[]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", unstable(sym::arm_target_feature), &[]), - ("thumb2", unstable(sym::arm_target_feature), &[]), - ("trustzone", unstable(sym::arm_target_feature), &[]), - ("v5te", unstable(sym::arm_target_feature), &[]), - ("v6", unstable(sym::arm_target_feature), &["v5te"]), - ("v6k", unstable(sym::arm_target_feature), &["v6"]), - ("v6t2", unstable(sym::arm_target_feature), &["v6k", "thumb2"]), - ("v7", unstable(sym::arm_target_feature), &["v6t2"]), - ("v8", unstable(sym::arm_target_feature), &["v7"]), - ("vfp2", unstable(sym::arm_target_feature), &[]), - ("vfp3", unstable(sym::arm_target_feature), &["vfp2", "d32"]), - ("vfp4", unstable(sym::arm_target_feature), &["vfp3"]), - ("virtualization", unstable(sym::arm_target_feature), &[]), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", STABLE, &["neon"]), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", STABLE, &[]), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", STABLE, &[]), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", STABLE, &[]), + ("crc", Stable, &[]), // FEAT_CSSC - ("cssc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_DIT - ("dit", STABLE, &[]), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", STABLE, &["neon"]), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", STABLE, &[]), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", STABLE, &["dpb"]), + ("dpb2", Stable, &["dpb"]), // FEAT_ECV - ("ecv", unstable(sym::aarch64_unstable_target_feature), &[]), + ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_F32MM - ("f32mm", STABLE, &["sve"]), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", STABLE, &["sve"]), + ("f64mm", Stable, &["sve"]), // FEAT_FAMINMAX - ("faminmax", unstable(sym::aarch64_unstable_target_feature), &[]), + ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_FCMA - ("fcma", STABLE, &["neon"]), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", STABLE, &["fp16"]), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", STABLE, &[]), + ("flagm", Stable, &[]), // FEAT_FLAGM2 - ("flagm2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), // FEAT_FP16 // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("fp16", STABLE, &["neon"]), + ("fp16", Stable, &["neon"]), // FEAT_FP8 - ("fp8", unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 - ("fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), // FEAT_FP8DOT4 - ("fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), // FEAT_FP8FMA - ("fp8fma", unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), // FEAT_FRINTTS - ("frintts", STABLE, &[]), + ("frintts", Stable, &[]), // FEAT_HBC - ("hbc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_I8MM - ("i8mm", STABLE, &[]), + ("i8mm", Stable, &[]), // FEAT_JSCVT // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("jsconv", STABLE, &["neon"]), + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", STABLE, &[]), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", STABLE, &[]), + ("lse", Stable, &[]), // FEAT_LSE128 - ("lse128", unstable(sym::aarch64_unstable_target_feature), &["lse"]), + ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), // FEAT_LSE2 - ("lse2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_LUT - ("lut", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MOPS - ("mops", unstable(sym::aarch64_unstable_target_feature), &[]), + ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", STABLE, &[]), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ( - "neon", - Stability::Stable { - allow_toggle: |target, enable| { - if target.abi == "softfloat" { - // `neon` has no ABI implications for softfloat targets, we can allow this. - Ok(()) - } else if enable - && !target.has_neg_feature("fp-armv8") - && !target.has_neg_feature("neon") - { - // neon is enabled by default, and has not been disabled, so enabling it again - // is redundant and we can permit it. Forbidding this would be a breaking change - // since this feature is stable. - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", STABLE, &[]), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", STABLE, &[]), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", STABLE, &[]), + ("pan", Stable, &[]), // FEAT_PAuth_LR - ("pauth-lr", unstable(sym::aarch64_unstable_target_feature), &[]), + ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PMUv3 - ("pmuv3", STABLE, &[]), + ("pmuv3", Stable, &[]), // FEAT_RNG - ("rand", STABLE, &[]), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", STABLE, &[]), + ("ras", Stable, &[]), // FEAT_LRCPC - ("rcpc", STABLE, &[]), + ("rcpc", Stable, &[]), // FEAT_LRCPC2 - ("rcpc2", STABLE, &["rcpc"]), + ("rcpc2", Stable, &["rcpc"]), // FEAT_LRCPC3 - ("rcpc3", unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM - ("rdm", STABLE, &["neon"]), + ("rdm", Stable, &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled globally using -Zfixed-x18, not // #[target_feature]. // Note that cfg(target_feature = "reserve-x18") is currently not set for // targets that reserve x18 by default. - ("reserve-x18", unstable(sym::aarch64_unstable_target_feature), &[]), + ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SB - ("sb", STABLE, &[]), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", STABLE, &["neon"]), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", STABLE, &["sha2"]), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", STABLE, &["neon"]), + ("sm4", Stable, &["neon"]), // FEAT_SME - ("sme", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SME_B16B16 - ("sme-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), // FEAT_SME_F16F16 - ("sme-f16f16", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SME_F64F64 - ("sme-f64f64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_F8F16 - ("sme-f8f16", unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), // FEAT_SME_F8F32 - ("sme-f8f32", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SME_FA64 - ("sme-fa64", unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), // FEAT_SME_I16I64 - ("sme-i16i64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_LUTv2 - ("sme-lutv2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SME2 - ("sme2", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME2p1 - ("sme2p1", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SPE - ("spe", STABLE, &[]), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", STABLE, &[]), + ("ssbs", Stable, &[]), // FEAT_SSVE_FP8FDOT2 - ("ssve-fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), // FEAT_SSVE_FP8FDOT4 - ("ssve-fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), // FEAT_SSVE_FP8FMA - ("ssve-fp8fma", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SVE // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 // @@ -423,46 +316,46 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 // // "For backwards compatibility, Neon and VFP are required in the latest architectures." - ("sve", STABLE, &["neon"]), + ("sve", Stable, &["neon"]), // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - ("sve-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SVE2 - ("sve2", STABLE, &["sve"]), + ("sve2", Stable, &["sve"]), // FEAT_SVE_AES & FEAT_SVE_PMULL128 - ("sve2-aes", STABLE, &["sve2", "aes"]), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", STABLE, &["sve2"]), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", STABLE, &["sve2", "sha3"]), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", STABLE, &["sve2", "sm4"]), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_SVE2p1 - ("sve2p1", unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), // FEAT_TME - ("tme", STABLE, &[]), - ("v8.1a", unstable(sym::aarch64_ver_target_feature), &[ + ("tme", Stable, &[]), + ("v8.1a", Unstable(sym::aarch64_ver_target_feature), &[ "crc", "lse", "rdm", "pan", "lor", "vh", ]), - ("v8.2a", unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), - ("v8.3a", unstable(sym::aarch64_ver_target_feature), &[ + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ("v8.3a", Unstable(sym::aarch64_ver_target_feature), &[ "v8.2a", "rcpc", "paca", "pacg", "jsconv", ]), - ("v8.4a", unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), - ("v8.5a", unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), - ("v8.6a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), - ("v8.7a", unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), - ("v8.8a", unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), - ("v8.9a", unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), - ("v9.1a", unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), - ("v9.2a", unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), - ("v9.3a", unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), - ("v9.4a", unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), - ("v9.5a", unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), - ("v9a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), + ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), + ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), + ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), + ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), + ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), + ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), + ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), + ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), // FEAT_VHE - ("vh", STABLE, &[]), + ("vh", Stable, &[]), // FEAT_WFxT - ("wfxt", unstable(sym::aarch64_unstable_target_feature), &[]), + ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), // tidy-alphabetical-end ]; @@ -470,337 +363,260 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", STABLE, &[]), - ("aes", STABLE, &["sse2"]), - ("amx-bf16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-complex", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-fp16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-int8", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-tile", unstable(sym::x86_amx_intrinsics), &[]), - ("avx", STABLE, &["sse4.2"]), - ("avx2", STABLE, &["avx"]), - ("avx512bf16", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", unstable(sym::avx512_target_feature), &["avx2"]), - ("bmi1", STABLE, &[]), - ("bmi2", STABLE, &[]), - ("cmpxchg16b", STABLE, &[]), - ("ermsb", unstable(sym::ermsb_target_feature), &[]), - ("f16c", STABLE, &["avx"]), - ("fma", STABLE, &["avx"]), - ("fxsr", STABLE, &[]), - ("gfni", unstable(sym::avx512_target_feature), &["sse2"]), - ("lahfsahf", unstable(sym::lahfsahf_target_feature), &[]), - ("lzcnt", STABLE, &[]), - ("movbe", STABLE, &[]), - ("pclmulqdq", STABLE, &["sse2"]), - ("popcnt", STABLE, &[]), - ("prfchw", unstable(sym::prfchw_target_feature), &[]), - ("rdrand", STABLE, &[]), - ("rdseed", STABLE, &[]), - ("rtm", unstable(sym::rtm_target_feature), &[]), - ("sha", STABLE, &["sse2"]), - ("sha512", unstable(sym::sha512_sm_x86), &["avx2"]), - ("sm3", unstable(sym::sha512_sm_x86), &["avx"]), - ("sm4", unstable(sym::sha512_sm_x86), &["avx2"]), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &["sse2"]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), - ("sse", STABLE, &[]), - ("sse2", STABLE, &["sse"]), - ("sse3", STABLE, &["sse2"]), - ("sse4.1", STABLE, &["ssse3"]), - ("sse4.2", STABLE, &["sse4.1"]), - ("sse4a", unstable(sym::sse4a_target_feature), &["sse3"]), - ("ssse3", STABLE, &["sse3"]), - ("tbm", unstable(sym::tbm_target_feature), &[]), - ("vaes", unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), - ( - "x87", - Stability::Unstable { - nightly_feature: sym::x87_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), - ("xop", unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), - ("xsave", STABLE, &[]), - ("xsavec", STABLE, &["xsave"]), - ("xsaveopt", STABLE, &["xsave"]), - ("xsaves", STABLE, &["xsave"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("x87", Unstable(sym::x87_target_feature), &[]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", unstable(sym::hexagon_target_feature), &[]), - ("hvx-length128b", unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", unstable(sym::powerpc_target_feature), &[]), - ("partword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("power10-vector", unstable(sym::powerpc_target_feature), &["power9-vector"]), - ("power8-altivec", unstable(sym::powerpc_target_feature), &["altivec"]), - ("power8-crypto", unstable(sym::powerpc_target_feature), &["power8-altivec"]), - ("power8-vector", unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), - ("power9-altivec", unstable(sym::powerpc_target_feature), &["power8-altivec"]), - ("power9-vector", unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), - ("quadword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("vsx", unstable(sym::powerpc_target_feature), &["altivec"]), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-crypto", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", unstable(sym::mips_target_feature), &[]), - ("msa", unstable(sym::mips_target_feature), &[]), - ("virt", unstable(sym::mips_target_feature), &[]), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", STABLE, &["zaamo", "zalrsc"]), - ("c", STABLE, &[]), - ( - "d", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| match &*target.llvm_abiname { - "ilp32d" | "lp64d" if !enable => { - // The ABI requires the `d` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - "ilp32e" if enable => { - // ilp32e is incompatible with features that need aligned load/stores > 32 bits, - // like `d`. - Err("feature is incompatible with ABI") - } - _ => Ok(()), - }, - }, - &["f"], - ), - ( - "e", - Stability::Unstable { - // Given that this is a negative feature, consider this before stabilizing: - // does it really make sense to enable this feature in an individual - // function with `#[target_feature]`? - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - _ if !enable => { - // Disabling this feature means we can use more registers (x16-x31). - // The "e" ABIs treat them 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. - // On other ABIs, the feature is already disabled anyway. - Ok(()) - } - "ilp32e" | "lp64e" => { - // Embedded ABIs should already have the feature anyway, it's fine to enable - // it again from an ABI perspective. - Ok(()) - } - _ => { - // *Not* an embedded ABI. Enabling `e` is invalid. - Err("feature is incompatible with ABI") - } - } - }, - }, - &[], - ), - ( - "f", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - "ilp32f" | "ilp32d" | "lp64f" | "lp64d" if !enable => { - // The ABI requires the `f` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - _ => Ok(()), - } - }, - }, - &[], - ), + ("a", Stable, &["zaamo", "zalrsc"]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), ( "forced-atomics", Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, &[], ), - ("m", STABLE, &[]), - ("relax", unstable(sym::riscv_target_feature), &[]), - ("unaligned-scalar-mem", unstable(sym::riscv_target_feature), &[]), - ("v", unstable(sym::riscv_target_feature), &[]), - ("zaamo", unstable(sym::riscv_target_feature), &[]), - ("zabha", unstable(sym::riscv_target_feature), &["zaamo"]), - ("zalrsc", unstable(sym::riscv_target_feature), &[]), - ("zba", STABLE, &[]), - ("zbb", STABLE, &[]), - ("zbc", STABLE, &[]), - ("zbkb", STABLE, &[]), - ("zbkc", STABLE, &[]), - ("zbkx", STABLE, &[]), - ("zbs", STABLE, &[]), - ("zdinx", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zfh", unstable(sym::riscv_target_feature), &["zfhmin"]), - ("zfhmin", unstable(sym::riscv_target_feature), &["f"]), - ("zfinx", unstable(sym::riscv_target_feature), &[]), - ("zhinx", unstable(sym::riscv_target_feature), &["zhinxmin"]), - ("zhinxmin", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zk", STABLE, &["zkn", "zkr", "zkt"]), - ("zkn", STABLE, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zknd", STABLE, &[]), - ("zkne", STABLE, &[]), - ("zknh", STABLE, &[]), - ("zkr", STABLE, &[]), - ("zks", STABLE, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), - ("zksed", STABLE, &[]), - ("zksh", STABLE, &[]), - ("zkt", STABLE, &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zaamo", Unstable(sym::riscv_target_feature), &[]), + ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), + ("zalrsc", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", unstable(sym::wasm_target_feature), &[]), - ("bulk-memory", STABLE, &[]), - ("exception-handling", unstable(sym::wasm_target_feature), &[]), - ("extended-const", STABLE, &[]), - ("multivalue", STABLE, &[]), - ("mutable-globals", STABLE, &[]), - ("nontrapping-fptoint", STABLE, &[]), - ("reference-types", STABLE, &[]), - ("relaxed-simd", STABLE, &["simd128"]), - ("sign-ext", STABLE, &[]), - ("simd128", STABLE, &[]), - ("tail-call", STABLE, &[]), - ("wide-arithmetic", unstable(sym::wasm_target_feature), &[]), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Stable, &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Stable, &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), + ("tail-call", Stable, &[]), + ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), // tidy-alphabetical-end ]; -const BPF_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = - &[("alu32", unstable(sym::bpf_target_feature), &[])]; +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", unstable(sym::csky_target_feature), &["7e10"]), - ("2e3", unstable(sym::csky_target_feature), &["e2"]), - ("3e3r1", unstable(sym::csky_target_feature), &[]), - ("3e3r2", unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), - ("3e3r3", unstable(sym::csky_target_feature), &["doloop"]), - ("3e7", unstable(sym::csky_target_feature), &["2e3"]), - ("7e10", unstable(sym::csky_target_feature), &["3e7"]), - ("cache", unstable(sym::csky_target_feature), &[]), - ("doloop", unstable(sym::csky_target_feature), &[]), - ("dsp1e2", unstable(sym::csky_target_feature), &[]), - ("dspe60", unstable(sym::csky_target_feature), &[]), - ("e1", unstable(sym::csky_target_feature), &["elrw"]), - ("e2", unstable(sym::csky_target_feature), &["e2"]), - ("edsp", unstable(sym::csky_target_feature), &[]), - ("elrw", unstable(sym::csky_target_feature), &[]), - ("float1e2", unstable(sym::csky_target_feature), &[]), - ("float1e3", unstable(sym::csky_target_feature), &[]), - ("float3e4", unstable(sym::csky_target_feature), &[]), - ("float7e60", unstable(sym::csky_target_feature), &[]), - ("floate1", unstable(sym::csky_target_feature), &[]), - ("hard-tp", unstable(sym::csky_target_feature), &[]), - ("high-registers", unstable(sym::csky_target_feature), &[]), - ("hwdiv", unstable(sym::csky_target_feature), &[]), - ("mp", unstable(sym::csky_target_feature), &["2e3"]), - ("mp1e2", unstable(sym::csky_target_feature), &["3e7"]), - ("nvic", unstable(sym::csky_target_feature), &[]), - ("trust", unstable(sym::csky_target_feature), &[]), - ("vdsp2e60f", unstable(sym::csky_target_feature), &[]), - ("vdspv1", unstable(sym::csky_target_feature), &[]), - ("vdspv2", unstable(sym::csky_target_feature), &[]), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", unstable(sym::csky_target_feature), &[]), - ("fpuv2_df", unstable(sym::csky_target_feature), &[]), - ("fpuv2_sf", unstable(sym::csky_target_feature), &[]), - ("fpuv3_df", unstable(sym::csky_target_feature), &[]), - ("fpuv3_hf", unstable(sym::csky_target_feature), &[]), - ("fpuv3_hi", unstable(sym::csky_target_feature), &[]), - ("fpuv3_sf", unstable(sym::csky_target_feature), &[]), - ("hard-float", unstable(sym::csky_target_feature), &[]), - ("hard-float-abi", unstable(sym::csky_target_feature), &[]), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", unstable(sym::loongarch_target_feature), &["f"]), - ("f", unstable(sym::loongarch_target_feature), &[]), - ("frecipe", unstable(sym::loongarch_target_feature), &[]), - ("lasx", unstable(sym::loongarch_target_feature), &["lsx"]), - ("lbt", unstable(sym::loongarch_target_feature), &[]), - ("lsx", unstable(sym::loongarch_target_feature), &["d"]), - ("lvz", unstable(sym::loongarch_target_feature), &[]), - ("relax", unstable(sym::loongarch_target_feature), &[]), - ("ual", unstable(sym::loongarch_target_feature), &[]), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", unstable(sym::s390x_target_feature), &[]), - ("vector", unstable(sym::s390x_target_feature), &[]), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; -const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("leoncasa", unstable(sym::sparc_target_feature), &[]), - ("v8plus", unstable(sym::sparc_target_feature), &[]), - ("v9", unstable(sym::sparc_target_feature), &[]), + ("leoncasa", Unstable(sym::sparc_target_feature), &[]), + ("v8plus", Unstable(sym::sparc_target_feature), &[]), + ("v9", Unstable(sym::sparc_target_feature), &[]), // tidy-alphabetical-end ]; -const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("isa-68000", unstable(sym::m68k_target_feature), &[]), - ("isa-68010", unstable(sym::m68k_target_feature), &["isa-68000"]), - ("isa-68020", unstable(sym::m68k_target_feature), &["isa-68010"]), - ("isa-68030", unstable(sym::m68k_target_feature), &["isa-68020"]), - ("isa-68040", unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), - ("isa-68060", unstable(sym::m68k_target_feature), &["isa-68040"]), + ("isa-68000", Unstable(sym::m68k_target_feature), &[]), + ("isa-68010", Unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", Unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", Unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", Unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", Unstable(sym::m68k_target_feature), &["isa-68040"]), // FPU - ("isa-68881", unstable(sym::m68k_target_feature), &[]), - ("isa-68882", unstable(sym::m68k_target_feature), &["isa-68881"]), + ("isa-68881", Unstable(sym::m68k_target_feature), &[]), + ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]), // tidy-alphabetical-end ]; @@ -808,7 +624,7 @@ const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_rust_features() -> impl Iterator<Item = (&'static str, StabilityUncomputed)> { +pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> { std::iter::empty() .chain(ARM_FEATURES.iter()) .chain(AARCH64_FEATURES.iter()) @@ -853,10 +669,16 @@ const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[( const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "lsx"), (256, "lasx")]; +#[derive(Copy, Clone, Debug)] +pub struct FeatureConstraints { + /// Features that must be enabled. + pub required: &'static [&'static str], + /// Features that must be disabled. + pub incompatible: &'static [&'static str], +} + impl Target { - pub fn rust_target_features( - &self, - ) -> &'static [(&'static str, StabilityUncomputed, ImpliedFeatures)] { + pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_FEATURES, "aarch64" | "arm64ec" => AARCH64_FEATURES, @@ -904,27 +726,130 @@ impl Target { } } - pub fn implied_target_features( + pub fn implied_target_features<'a>( &self, - base_features: impl Iterator<Item = Symbol>, - ) -> FxHashSet<Symbol> { - let implied_features = self - .rust_target_features() - .iter() - .map(|(f, _, i)| (Symbol::intern(f), i)) - .collect::<FxHashMap<_, _>>(); + base_features: impl Iterator<Item = &'a str>, + ) -> FxHashSet<&'a str> { + let implied_features = + self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::<FxHashMap<_, _>>(); // implied target features have their own implied target features, so we traverse the // map until there are no more features to add let mut features = FxHashSet::default(); - let mut new_features = base_features.collect::<Vec<Symbol>>(); + let mut new_features = base_features.collect::<Vec<&str>>(); while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + new_features.extend(implied_features.iter().copied()) } } } features } + + /// Returns two lists of features: + /// the first list contains target features that must be enabled for ABI reasons, + /// and the second list contains target feature that must be disabled for ABI reasons. + /// + /// These features are automatically appended to whatever the target spec sats as default + /// features for the 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) -> 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 + // 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: &[] } + } + } + "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: &[] } + } + } + "arm" => { + // On ARM, ABI handling is reasonably sane; we use `llvm_floatabi` to indicate + // to LLVM which ABI we are going for. + match self.llvm_floatabi.unwrap() { + FloatAbi::Soft => { + // Nothing special required, will use soft-float ABI throughout. + NOTHING + } + FloatAbi::Hard => { + // Must have `fpregs` and must not have `soft-float`. + FeatureConstraints { required: &["fpregs"], incompatible: &["soft-float"] } + } + } + } + "aarch64" | "arm64ec" => { + // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force + // the use of soft-float, so all we can do here is some crude hacks. + match &*self.abi { + "softfloat" => { + // This is not fully correct, LLVM actually doesn't let us enforce the softfloat + // ABI properly... see <https://github.com/rust-lang/rust/issues/134375>. + // FIXME: should we forbid "neon" here? But that would be a breaking change. + NOTHING + } + _ => { + // 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. + FeatureConstraints { required: &["neon"], incompatible: &[] } + } + } + } + "riscv32" | "riscv64" => { + // RISC-V handles ABI in a very sane way, being fully explicit via `llvm_abiname` + // about what the intended ABI is. + match &*self.llvm_abiname { + "ilp32d" | "lp64d" => { + // Requires d (which implies f), incompatible with e. + FeatureConstraints { required: &["d"], incompatible: &["e"] } + } + "ilp32f" | "lp64f" => { + // Requires f, incompatible with e. + FeatureConstraints { required: &["f"], incompatible: &["e"] } + } + "ilp32" | "lp64" => { + // Requires nothing, incompatible with e. + FeatureConstraints { required: &[], incompatible: &["e"] } + } + "ilp32e" => { + // ilp32e is documented to be incompatible with features that need aligned + // load/stores > 32 bits, like `d`. (One could also just generate more + // complicated code to align the stack when needed, but the RISCV + // architecture manual just explicitly rules out this combination so we + // might as well.) + // 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. + FeatureConstraints { required: &[], incompatible: &["d"] } + } + "lp64e" => { + // As above, `e` is not required. + NOTHING + } + _ => unreachable!(), + } + } + _ => NOTHING, + } + } } diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index f38a1ae72de..e7f70a1e24a 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="-avx2,-avx,+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" +// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 1b2b63c3d1a..6daa5cd7d5e 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -11,10 +11,11 @@ // ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 -// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?))*}}" } +// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0 -// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-fp-armv8,?)|(-neon,?))*}}" } +// `neon` and `fp-armv8` get enabled as target base features, but then disabled again at the end of the list. +// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}},-neon,-fp-armv8{{(,\+fpmr)?}}" } //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" } diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs index 04990c8bdf4..ef71dfa4c30 100644 --- a/tests/run-make/simd-ffi/rmake.rs +++ b/tests/run-make/simd-ffi/rmake.rs @@ -56,7 +56,7 @@ fn main() { .target(&target) .emit("llvm-ir,asm") .input("simd.rs") - .arg("-Ctarget-feature=+neon,+sse") + .arg("-Ctarget-feature=-soft-float,+neon,+sse") .arg(&format!("-Cextra-filename=-{target}")) .run(); } diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr index d5d7d7aa627..b6c3ccdedfb 100644 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `neon` 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 <https://github.com/rust-lang/rust/issues/116344> diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs index b3171d52c51..dab01179c0b 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs @@ -1,11 +1,11 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -#![feature(no_core, lang_items)] +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature)] #![no_core] #[lang = "sized"] pub trait Sized {} -#[target_feature(enable = "x87")] -//~^ERROR: cannot be toggled with +#[target_feature(enable = "d")] +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr index 3ebbe69d8ae..9df56d86729 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr @@ -1,8 +1,8 @@ -error: target feature `x87` cannot be toggled with `#[target_feature]`: unsound on hard-float targets because it changes float ABI +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:9:18 | -LL | #[target_feature(enable = "x87")] - | ^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "d")] + | ^^^^^^^^^^^^ error: aborting due to 1 previous error 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 <https://github.com/rust-lang/rust/issues/116344> + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index d5d7d7aa627..b6c3ccdedfb 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `neon` 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 <https://github.com/rust-lang/rust/issues/116344> diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 604ad2f991a..6191681286a 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,7 +1,11 @@ -warning: target feature `x87` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: target feature `x87` 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 <https://github.com/rust-lang/rust/issues/116344> -warning: 1 warning emitted +warning: 2 warnings emitted diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-target-feature-attribute.rs index f13cdd17da6..2ba5f2215c7 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.rs @@ -7,5 +7,5 @@ pub trait Sized {} #[target_feature(enable = "soft-float")] -//~^ERROR: cannot be toggled with +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr index 27ac4aaf960..f3d54cc19d3 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr @@ -1,4 +1,4 @@ -error: target feature `soft-float` cannot be toggled with `#[target_feature]`: unsound because it changes float ABI +error: target feature `soft-float` cannot be enabled with `#[target_feature]`: unsound because it changes float ABI --> $DIR/forbidden-target-feature-attribute.rs:9:18 | LL | #[target_feature(enable = "soft-float")] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr index 508e1fe0cf4..797cd4be5c2 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `soft-float` cannot be disabled with `-Ctarget-feature`: unsound because it changes float 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 <https://github.com/rust-lang/rust/issues/116344> diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-target-feature-flag.stderr index 508e1fe0cf4..075666fbf66 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `soft-float` cannot be enabled with `-Ctarget-feature`: unsound because it changes float 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 <https://github.com/rust-lang/rust/issues/116344> |
