diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index c6124fdfff38c..fd3a00d56fe0b 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -93,7 +93,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match asm::InlineAsmClobberAbi::parse( asm_arch, &self.tcx.sess.target, - &self.tcx.sess.unstable_target_features, + &self.tcx.sess.internal_target_features, *abi_name, ) { Ok(abi) => { diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 03fd11afa3f10..0b8eb75972ec0 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -404,7 +404,7 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { let abi_clobber = InlineAsmClobberAbi::parse( self.arch, &self.tcx.sess.target, - &self.tcx.sess.unstable_target_features, + &self.tcx.sess.internal_target_features, sym::C, ) .unwrap() diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index ba586f83ba30d..c59b77eae4611 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -39,6 +39,7 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::settings::{self, Configurable}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; +use rustc_data_structures::unord::UnordSet; use rustc_log::tracing::info; use rustc_middle::dep_graph::WorkProductMap; use rustc_session::Session; @@ -170,8 +171,6 @@ impl CodegenBackend for CraneliftCodegenBackend { }, _ => vec![], }; - // FIXME do `unstable_target_features` properly - let unstable_target_features = target_features.clone(); // FIXME(f16_f128): `rustc_codegen_llvm` currently disables support on Windows GNU // targets due to GCC using a different ABI than LLVM. Therefore `f16` and `f128` @@ -186,8 +185,7 @@ impl CodegenBackend for CraneliftCodegenBackend { let has_reliable_f128_math = has_reliable_f16_f128 && sess.target.env == Env::Gnu; TargetConfig { - target_features, - unstable_target_features, + internal_target_features: UnordSet::from_iter(target_features), // `rustc_codegen_cranelift` polyfills functionality not yet // available in Cranelift. has_reliable_f16: has_reliable_f16_f128, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 55c721a9706a6..621ee4ce27636 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -85,7 +85,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, }; use rustc_codegen_ssa::base::codegen_crate; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods}; use rustc_codegen_ssa::{CompiledModule, CompiledModules, CrateInfo, ModuleCodegen, TargetConfig}; use rustc_data_structures::profiling::SelfProfilerRef; @@ -531,7 +531,7 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { /// Returns the features that should be set in `cfg(target_feature)`. fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig { - let (unstable_target_features, target_features) = cfg_target_feature( + let internal_target_features = internal_target_features( sess, |feature| to_gcc_features(sess, feature), |feature| { @@ -555,8 +555,7 @@ fn target_config(sess: &Session, target_info: &LockedTargetInfo) -> TargetConfig let has_reliable_f128 = target_info.supports_target_dependent_type(CType::Float128); TargetConfig { - target_features, - unstable_target_features, + internal_target_features, // There are no known bugs with GCC support for f16 or f128 has_reliable_f16, has_reliable_f16_math: has_reliable_f16, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index d2dfa9a45de8b..fba43bba737e0 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -970,14 +970,14 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' Hexagon(HexagonInlineAsmRegClass::vreg) => { // HVX vector register size depends on the HVX mode. // LLVM's "v" constraint requires the exact vector width. - if cx.tcx.sess.unstable_target_features.contains(&sym::hvx_length128b) { + if cx.tcx.sess.internal_target_features.contains(&sym::hvx_length128b) { cx.type_vector(cx.type_i32(), 32) // 1024-bit for 128B mode } else { cx.type_vector(cx.type_i32(), 16) // 512-bit for 64B mode } } Hexagon(HexagonInlineAsmRegClass::vreg_pair) => { - if cx.tcx.sess.unstable_target_features.contains(&sym::hvx_length128b) { + if cx.tcx.sess.internal_target_features.contains(&sym::hvx_length128b) { cx.type_vector(cx.type_i32(), 64) // 2048-bit for 128B mode } else { cx.type_vector(cx.type_i32(), 32) // 1024-bit for 64B mode diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index deef323a2e1f8..a8a0de1c8d347 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -383,9 +383,9 @@ fn packed_stack_attr<'ll>( // The backchain and softfloat flags can be set via -Ctarget-features=... // or via #[target_features(enable = ...)] so we have to check both possibilities - let have_backchain = sess.unstable_target_features.contains(&sym::backchain) + let have_backchain = sess.internal_target_features.contains(&sym::backchain) || function_attributes.iter().any(|feature| feature.name == sym::backchain); - let have_softfloat = sess.unstable_target_features.contains(&sym::soft_float) + let have_softfloat = sess.internal_target_features.contains(&sym::soft_float) || function_attributes.iter().any(|feature| feature.name == sym::soft_float); // If both, backchain and packedstack, are enabled LLVM cannot generate valid function entry points diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 94883a94f089a..843589fcb265f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -210,7 +210,7 @@ pub(crate) fn target_machine_factory( let code_model = to_llvm_code_model(sess.code_model()); // This is used to set cfg_has_threads, so all logic must be in this method. - let singlethread = sess.target.singlethread(&sess.target_features); + let singlethread = sess.target.singlethread(&sess.internal_target_features); let triple = SmallCStr::new(&versioned_llvm_target(sess)); let cpu = SmallCStr::new(llvm_util::target_cpu(sess)); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9ad14925afb14..ff710cd9f738f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -7,7 +7,7 @@ use std::{ptr, slice, str}; use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::{TargetConfig, target_features}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::small_c_str::SmallCStr; @@ -314,7 +314,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option TargetConfig { let target_machine = create_informational_target_machine(sess, true); - let (unstable_target_features, target_features) = cfg_target_feature( + let internal_target_features = internal_target_features( sess, |feature| { to_llvm_features(sess, feature) @@ -322,9 +322,9 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { .unwrap_or_default() }, |feature| { - // This closure determines whether the target CPU has the feature according to LLVM. We do - // *not* consider the `-Ctarget-feature`s here, as that will be handled later in - // `cfg_target_feature`. + // This closure determines whether the target CPU has the feature according to LLVM. We + // do *not* consider the `-Ctarget-feature`s here, as that will be handled later in + // `internal_target_features`. if let Some(feat) = to_llvm_features(sess, feature) { // All the LLVM features this expands to must be enabled. for llvm_feature in feat { @@ -344,8 +344,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { ); let mut cfg = TargetConfig { - target_features, - unstable_target_features, + internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, has_reliable_f128: true, @@ -730,7 +729,10 @@ pub(crate) fn global_llvm_features(sess: &Session, only_base_features: bool) -> target_features::flag_to_backend_features(sess, extend_backend_features); } - // We add this in the "base target" so that these show up in `sess.unstable_target_features`. + // `-C` flags that map to LLVM target features. + // We need to include them even with `only_base_features` as this is used to populate + // `sess.internal_target_features` where we very much want them to be present (e.g. the inline + // asm logic uses that to check which registers may be used). llvm_features_by_flags(sess, &mut features); features diff --git a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs index dbc0abdb50da8..f8cc07201d10f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs +++ b/compiler/rustc_codegen_ssa/src/back/link/raw_dylib.rs @@ -229,7 +229,7 @@ fn create_elf_raw_dylib_stub(sess: &Session, soname: &str, symbols: &[DllImport] // It is important that the order of reservation matches the order of writing. // The object crate contains many debug asserts that fire if you get this wrong. - let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.unstable_target_features) + let Some((arch, sub_arch)) = sess.target.object_architecture(&sess.internal_target_features) else { sess.dcx().fatal(format!( "raw-dylib is not supported for the architecture `{}`", diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 951a60426b5d5..a43bf72b6a27d 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -207,7 +207,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option Endianness::Big, }; let Some((architecture, sub_architecture)) = - sess.target.object_architecture(&sess.unstable_target_features) + sess.target.object_architecture(&sess.internal_target_features) else { return None; }; @@ -328,12 +328,12 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { let mut e_flags: u32 = 0x0; // Check if compression is enabled - if sess.target_features.contains(&sym::zca) { + if sess.internal_target_features.contains(&sym::zca) { e_flags |= elf::EF_RISCV_RVC; } // Check if RVTSO is enabled - if sess.target_features.contains(&sym::ztso) { + if sess.internal_target_features.contains(&sym::ztso) { e_flags |= elf::EF_RISCV_TSO; } diff --git a/compiler/rustc_codegen_ssa/src/diagnostics.rs b/compiler/rustc_codegen_ssa/src/diagnostics.rs index 6b182d795a9ec..cb746a8eed90d 100644 --- a/compiler/rustc_codegen_ssa/src/diagnostics.rs +++ b/compiler/rustc_codegen_ssa/src/diagnostics.rs @@ -1100,7 +1100,7 @@ pub(crate) struct TargetFeatureSafeTrait { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason}")] -pub(crate) struct ForbiddenTargetFeatureAttr<'a> { +pub(crate) struct InternalOnlyTargetFeatureAttr<'a> { #[primary_span] pub span: Span, pub feature: &'a str, @@ -1233,7 +1233,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}")] -pub(crate) struct ForbiddenCTargetFeature<'a> { +pub(crate) struct InternalOnlyCTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, pub reason: &'a str, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9a42debe1dd97..02ae2d50390cc 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use rustc_abi::Size; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::unord::UnordMap; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; @@ -306,14 +306,12 @@ pub struct CrateInfo { pub exported_symbols_for_lto: Vec, } -/// Target-specific options that get set in `cfg(...)`. +/// Target-specific options that get set in `sess`/`cfg(...)`. /// /// RUSTC_SPECIFIC_FEATURES should be skipped here, those are handled outside codegen. pub struct TargetConfig { - /// Options to be set in `cfg(target_features)`. - pub target_features: Vec, - /// Options to be set in `cfg(target_features)`, but including unstable features. - pub unstable_target_features: Vec, + /// Options to be set in `sess.internal_target_features`. + pub internal_target_features: UnordSet, /// Option for `cfg(target_has_reliable_f16)`, true if `f16` basic arithmetic works. pub has_reliable_f16: bool, /// Option for `cfg(target_has_reliable_f16_math)`, true if `f16` math calls work. diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 131a345fe557d..33cc321ea6d32 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -151,7 +151,7 @@ fn prefix_and_suffix<'tcx>( let asm_binary_format = &tcx.sess.target.binary_format; let is_arm = tcx.sess.target.arch == Arch::Arm; - let is_thumb = tcx.sess.unstable_target_features.contains(&sym::thumb_mode); + let is_thumb = tcx.sess.internal_target_features.contains(&sym::thumb_mode); let function_sections = tcx.sess.opts.unstable_opts.function_sections.unwrap_or(tcx.sess.target.function_sections); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 8f459e5a218d2..69487d2039c31 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -72,7 +72,7 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. if let Err(reason) = stability.toggle_allowed() { - tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::InternalOnlyTargetFeatureAttr { span: feature_span, feature: feature_str, reason, @@ -107,7 +107,7 @@ pub(crate) fn from_target_feature_attr( diagnostics::Aarch64SoftfloatNeon, ); } else { - tcx.dcx().emit_err(diagnostics::ForbiddenTargetFeatureAttr { + tcx.dcx().emit_err(diagnostics::InternalOnlyTargetFeatureAttr { span: feature_span, feature: name.as_str(), reason: "this feature is incompatible with the target ABI", @@ -122,7 +122,17 @@ pub(crate) fn from_target_feature_attr( } else { TargetFeatureKind::Enabled }; - target_features.push(TargetFeature { name, kind }) + target_features.push(TargetFeature { name, kind }); + + if !rust_target_features + .get(name.as_str()) + .is_some_and(|s| s.toggle_allowed().is_ok()) + { + tcx.dcx().span_delayed_bug( + feature_span, + format!("internal-only feature {name} should not be toggled by `#[target_feature]`"), + ); + } } } } @@ -131,7 +141,7 @@ pub(crate) fn from_target_feature_attr( /// Computes the set of target features used in a function for the purposes of /// inline assembly. fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { - let mut target_features = tcx.sess.unstable_target_features.clone(); + let mut target_features = tcx.sess.internal_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); @@ -164,20 +174,22 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, } } -/// Parse the value of the target spec `features` field or `-Ctarget-feature`, also expanding -/// implied features, and call the closure for each (expanded) Rust feature. If the list contains -/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked. +/// Parse the value of the target spec `features` field or `-Ctarget-feature`, calling the closure +/// for each entry in the list, also expanding implied features (but only for actual Rust target +/// features). If the list contains a syntactically invalid item (not starting with `+`/`-`) , the +/// error callback is invoked. fn parse_rust_feature_list<'a>( sess: &'a Session, features: &'a str, err_callback: impl Fn(&'a str), mut callback: impl FnMut( /* base_feature */ &'a str, - /* with_implied */ FxHashSet<&'a str>, + /* with_implied */ Option>, /* enable */ bool, ), ) { - // A cache for the backwards implication map. + // A cache for the forward and backwards feature maps. + let mut features_map: Option> = None; let mut inverse_implied_features: Option>> = None; for feature in features.split(',') { @@ -187,13 +199,30 @@ fn parse_rust_feature_list<'a>( continue; } - callback(base_feature, sess.target.implied_target_features(base_feature), true) + let features_map = + features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + + if !features_map.contains_key(&base_feature) { + callback(base_feature, None, true); + continue; + } + + let implied_features = sess.target.implied_target_features(base_feature, &features_map); + callback(base_feature, Some(implied_features), true) } else if let Some(base_feature) = feature.strip_prefix('-') { // Skip features that are not target features, but rustc features. if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) { continue; } + let features_map = + features_map.get_or_insert_with(|| sess.target.rust_target_features_map()); + + if !features_map.contains_key(&base_feature) { + callback(base_feature, None, false); + continue; + } + // If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical // contraposition. So we have to find all the reverse implications of `base_feature` and // disable them, too. @@ -210,10 +239,10 @@ fn parse_rust_feature_list<'a>( // Inverse implied target features have their own inverse implied target features, so we // traverse the map until there are no more features to add. - let mut features = FxHashSet::default(); + let mut implied_features = FxHashSet::default(); let mut new_features = vec![base_feature]; while let Some(new_feature) = new_features.pop() { - if features.insert(new_feature) { + if implied_features.insert(new_feature) { if let Some(implied_features) = inverse_implied_features.get(&new_feature) { #[allow(rustc::potential_query_instability)] new_features.extend(implied_features) @@ -221,16 +250,15 @@ fn parse_rust_feature_list<'a>( } } - callback(base_feature, features, false) + callback(base_feature, Some(implied_features), false) } else if !feature.is_empty() { err_callback(feature) } } } -/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically, -/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and -/// 2nd component of the return value, respectively). +/// Utility function for a codegen backend to compute the set of all actually enabled Rust target +/// features (which will be stored in `sess.internal_target_features`). /// /// `to_backend_features` converts a Rust feature name into a list of backend feature names; this is /// used for diagnostic purposes only. @@ -242,15 +270,15 @@ fn parse_rust_feature_list<'a>( /// to target features. /// /// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. -pub fn cfg_target_feature<'a, const N: usize>( +pub fn internal_target_features<'a, const N: usize>( sess: &Session, to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>, mut target_base_has_feature: impl FnMut(&str) -> bool, -) -> (Vec, Vec) { - let known_features = sess.target.rust_target_features(); +) -> UnordSet { + let features_map = sess.target.rust_target_features_map(); - // Compute which of the known target features are enabled in the 'base' target machine. We only - // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. + // Compute which of the known target features are enabled in the 'base' target machine: for + // every Rust target feature, ask the backend if it is enabled. let mut features: UnordSet = sess .target .rust_target_features() @@ -263,10 +291,14 @@ pub fn cfg_target_feature<'a, const N: usize>( // // Iteration order is irrelevant because we're collecting into an `UnordSet`. #[allow(rustc::potential_query_instability)] - sess.target.implied_target_features(base_feature).into_iter().map(|f| Symbol::intern(f)) + sess.target + .implied_target_features(base_feature, &features_map) + .into_iter() + .map(|f| Symbol::intern(f)) }) .collect(); + // State gathered for "tied features" check. let mut enabled_disabled_features = FxHashMap::default(); // Add enabled and remove disabled features. @@ -278,37 +310,23 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(diagnostics::UnknownCTargetFeaturePrefix { feature }); }, |base_feature, new_features, enable| { - // Iteration order is irrelevant since this only influences an `FxHashMap`. - #[allow(rustc::potential_query_instability)] - enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable))); - - // Iteration order is irrelevant since this only influences an `UnordSet`. - #[allow(rustc::potential_query_instability)] - if enable { - features.extend(new_features.into_iter().map(|f| Symbol::intern(f))); - } else { - // Remove `new_features` from `features`. - for new in new_features { - features.remove(&Symbol::intern(new)); - } - } - - // Check feature validity. - let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature); - match feature_state { + match features_map.get(base_feature) { None => { - // This is definitely not a valid Rust feature name. Maybe it is a backend - // feature name? If so, give a better error message. - let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { - let backend_features = to_backend_features(rust_feature); - if backend_features.contains(&base_feature) - && !backend_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); + // This is definitely not a valid Rust feature name. We do not add it to + // `features`. Maybe it is a backend feature name? If so, give a better error + // message. + let rust_feature = sess.target.rust_target_features().iter().find_map( + |&(rust_feature, _, _)| { + let backend_features = to_backend_features(rust_feature); + if backend_features.contains(&base_feature) + && !backend_features.contains(&rust_feature) + { + Some(rust_feature) + } else { + None + } + }, + ); let unknown_feature = if let Some(rust_feature) = rust_feature { diagnostics::UnknownCTargetFeature { feature: base_feature, @@ -322,9 +340,27 @@ pub fn cfg_target_feature<'a, const N: usize>( }; sess.dcx().emit_warn(unknown_feature); } - Some((_, stability, _)) => { - if let Stability::Forbidden { reason, hard_error } = stability { - let diag = diagnostics::ForbiddenCTargetFeature { + Some((stability, _)) => { + let new_features = new_features.unwrap(); + // Add feature to our set -- only if it is actually a recognized feature. + // Iteration order is irrelevant since this only influences an `FxHashMap`. + #[allow(rustc::potential_query_instability)] + enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable))); + + // Iteration order is irrelevant since this only influences an `UnordSet`. + #[allow(rustc::potential_query_instability)] + if enable { + features.extend(new_features.into_iter().map(|f| Symbol::intern(f))); + } else { + // Remove `new_features` from `features`. + for new in new_features { + features.remove(&Symbol::intern(new)); + } + } + + // Check feature stability. + if let Stability::InternalOnly { reason, hard_error } = stability { + let diag = diagnostics::InternalOnlyCTargetFeature { feature: base_feature, enabled: if enable { "enabled" } else { "disabled" }, reason, @@ -363,34 +399,11 @@ pub fn cfg_target_feature<'a, const N: usize>( }); } - // Filter enabled features based on feature gates. - let f = |allow_unstable| { - sess.target - .rust_target_features() - .iter() - .filter_map(|(feature, gate, _)| { - // The `allow_unstable` set is used by rustc internally to determine which target - // features are truly available, so we want to return even perma-unstable - // "forbidden" features. - if allow_unstable - || (gate.in_cfg() - && (sess.is_nightly_build() - || gate.requires_nightly(/* in_cfg */ true).is_none())) - { - Some(Symbol::intern(feature)) - } else { - None - } - }) - .filter(|feature| features.contains(&feature)) - .collect() - }; - - (f(true), f(false)) + features } /// Given a map from target_features to whether they are enabled or disabled, ensure only valid -/// combinations are allowed. +/// combinations are allowed. Returns `Some` if a violation is found. pub fn check_tied_features( sess: &Session, features: &FxHashMap<&str, bool>, @@ -416,8 +429,6 @@ pub fn target_spec_to_backend_features<'a>( sess: &'a Session, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { - let mut rust_features = vec![]; - // This check handles SM versions that defaults (by LLVM) to unsupported (by Rust) PTX ISA versions. // sm_70, sm_72 and sm_75 defaults to PTX ISA versions with major version 6, while sm_80 default to 7.0 if sess.target.arch == Arch::Nvptx64 @@ -426,7 +437,7 @@ pub fn target_spec_to_backend_features<'a>( None | Some("sm_70") | Some("sm_72") | Some("sm_75") ) { - rust_features.push((true, "ptx70")); + extend_backend_features("ptx70", true); } // Compute implied features @@ -435,20 +446,18 @@ pub fn target_spec_to_backend_features<'a>( &sess.target.features, /* err_callback */ |feature| { - panic!("Target spec contains invalid feature {feature}"); + panic!("Target spec contains invalid feature {feature} (missing `+`/`-` prefix)"); }, - |_base_feature, new_features, enable| { - // FIXME emit an error for unknown features like cfg_target_feature would for -Ctarget-feature - rust_features.extend( - UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), - ); + |base_feature, new_features, enable| { + // FIXME emit an error for unknown features in the target spec like + // internal_target_features would for -Ctarget-feature. + let new_features = + new_features.unwrap_or_else(|| FxHashSet::from_iter(std::iter::once(base_feature))); + for new_feature in UnordSet::from(new_features).to_sorted_stable_ord().iter() { + extend_backend_features(new_feature, enable); + } }, ); - - // Add this to the backend features. - for (enable, feature) in rust_features { - extend_backend_features(feature, enable); - } } /// Translates the `-Ctarget-feature` flag into a backend target feature list. @@ -459,26 +468,22 @@ pub fn flag_to_backend_features<'a>( sess: &'a Session, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { - // Compute implied features - let mut rust_features = vec![]; parse_rust_feature_list( sess, &sess.opts.cg.target_feature, /* err_callback */ |_feature| { - // Errors are already emitted in `cfg_target_feature`; avoid duplicates. + // Errors are already emitted in `internal_target_features`; avoid duplicates. }, - |_base_feature, new_features, enable| { - rust_features.extend( - UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)), - ); + |base_feature, new_features, enable| { + // Forward unknown features to the backend as that's what we have always done. + let new_features = + new_features.unwrap_or_else(|| FxHashSet::from_iter(std::iter::once(base_feature))); + for new_feature in UnordSet::from(new_features).to_sorted_stable_ord().iter() { + extend_backend_features(new_feature, enable); + } }, ); - - // Add this to the backend features. - for (enable, feature) in rust_features { - extend_backend_features(feature, enable); - } } /// Computes the backend target features to be added to account for retpoline flags. @@ -533,9 +538,12 @@ pub(crate) fn provide(providers: &mut Providers) { (Stability::Stable, _) | ( Stability::Unstable { .. }, - Stability::Unstable { .. } | Stability::Forbidden { .. }, + Stability::Unstable { .. } | Stability::InternalOnly { .. }, ) - | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => { + | ( + Stability::InternalOnly { .. }, + Stability::InternalOnly { .. }, + ) => { // The stability in the entry is at least as good as the new // one, just keep it. } @@ -553,13 +561,18 @@ pub(crate) fn provide(providers: &mut Providers) { .target .rust_target_features() .iter() - .map(|(a, b, _)| (a.to_string(), *b)) + .map(|(feat, stab, _)| (feat.to_string(), *stab)) .collect() } }, implied_target_features: |tcx, feature: Symbol| { + if tcx.sess.opts.actually_rustdoc { + // We can't handle implication when we are mixing all targets. + return vec![feature]; + } + let features_map = tcx.sess.target.rust_target_features_map(); let feature = feature.as_str(); - UnordSet::from(tcx.sess.target.implied_target_features(feature)) + UnordSet::from(tcx.sess.target.implied_target_features(feature, &features_map)) .into_sorted_stable_ord() .into_iter() .map(|s| Symbol::intern(s)) diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 6014f1af4bfc3..1d63490eab654 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -44,8 +44,7 @@ pub trait CodegenBackend { /// `target_feature` and support for unstable float types. fn target_config(&self, _sess: &Session) -> TargetConfig { TargetConfig { - target_features: vec![], - unstable_target_features: vec![], + internal_target_features: Default::default(), // `true` is used as a default so backends need to acknowledge when they do not // support the float types, rather than accidentally quietly skipping all tests. has_reliable_f16: true, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 019c7ccfe979a..58a001ab5b1e9 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,7 +11,7 @@ use rustc_ast as ast; use rustc_attr_parsing::ShouldEmit; use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder}; use rustc_codegen_ssa::back::link::link_binary; -use rustc_codegen_ssa::target_features::cfg_target_feature; +use rustc_codegen_ssa::target_features::internal_target_features; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig}; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; @@ -50,10 +50,27 @@ pub(crate) fn add_configuration( let tf = sym::target_feature; let tf_cfg = codegen_backend.target_config(sess); - sess.unstable_target_features.extend(tf_cfg.unstable_target_features.iter().copied()); - sess.target_features.extend(tf_cfg.target_features.iter().copied()); + // Add some of the target features to `cfg`. + cfg.extend( + sess.target + .rust_target_features() + .iter() + .filter_map(|(feature, gate, _)| { + if gate.in_cfg() + && (sess.is_nightly_build() + || gate.requires_nightly(/* in_cfg */ true).is_none()) + { + Some(Symbol::intern(feature)) + } else { + None + } + }) + .filter(|feature| tf_cfg.internal_target_features.contains(&feature)) + .map(|feature| (sym::target_feature, Some(feature))), + ); - cfg.extend(tf_cfg.target_features.into_iter().map(|feat| (tf, Some(feat)))); + // Store all of them in the session. + sess.internal_target_features.extend(tf_cfg.internal_target_features.into_sorted_stable_ord()); if tf_cfg.has_reliable_f16 { cfg.insert((sym::target_has_reliable_f16, None)); @@ -74,10 +91,10 @@ pub(crate) fn add_configuration( } /// Ensures that all target features required by the ABI are present. -/// Must be called after `unstable_target_features` has been populated! +/// Must be called after `internal_target_features` has been populated! pub(crate) fn check_abi_required_features(sess: &Session) { let abi_feature_constraints = sess.target.abi_required_features(); - // We check this against `unstable_target_features` as that is conveniently already + // We check this against `internal_target_features` as that is conveniently already // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. // Just double-check that the features we care about are actually on our list. for feature in @@ -90,13 +107,13 @@ pub(crate) fn check_abi_required_features(sess: &Session) { } for feature in abi_feature_constraints.required { - if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + if !sess.internal_target_features.contains(&Symbol::intern(feature)) { sess.dcx() .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "enabled" }); } } for feature in abi_feature_constraints.incompatible { - if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + if sess.internal_target_features.contains(&Symbol::intern(feature)) { sess.dcx() .emit_warn(diagnostics::AbiRequiredTargetFeature { feature, enabled: "disabled" }); } @@ -374,7 +391,7 @@ impl CodegenBackend for DummyCodegenBackend { } let abi_required_features = sess.target.abi_required_features(); - let (target_features, unstable_target_features) = cfg_target_feature::<0>( + let internal_target_features = internal_target_features::<0>( sess, |_feature| Default::default(), |feature| { @@ -387,8 +404,7 @@ impl CodegenBackend for DummyCodegenBackend { ); TargetConfig { - target_features, - unstable_target_features, + internal_target_features, has_reliable_f16: true, has_reliable_f16_math: true, has_reliable_f128: true, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 70e9129ffee3f..69590fc351320 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -448,7 +448,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { let build_enabled = self .tcx .sess - .target_features + .internal_target_features .iter() .copied() .filter(|feature| missing.contains(feature)) diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 4479ce2dba08b..173595de5c8c2 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -57,7 +57,7 @@ fn do_check_simd_vector_abi<'tcx>( ) { let codegen_attrs = tcx.codegen_fn_attrs(def_id); let have_feature = |feat: Symbol| { - let target_feats = tcx.sess.unstable_target_features.contains(&feat); + let target_feats = tcx.sess.internal_target_features.contains(&feat); let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat); target_feats || fn_feats }; diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 84a26af6b54ce..e5c874503a00f 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -304,7 +304,7 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { } } - if !sess.target.singlethread(&sess.target_features) { + if !sess.target.singlethread(&sess.internal_target_features) { ins_none!(sym::target_has_threads); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index aea36bf44f28d..7babc06050335 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -375,11 +375,11 @@ pub struct Session { /// Architecture to use for interpreting asm!. pub asm_arch: Option, - /// Set of enabled features for the current target. - pub target_features: FxIndexSet, - - /// Set of enabled features for the current target, including unstable ones. - pub unstable_target_features: FxIndexSet, + /// Set of actually enabled features for the current target, including ones that are not + /// in `cfg(target_feature)` because they are unstable or internal-only. + /// This is used by the compiler itself when it needs to know which target features are actually + /// going to be enabled in the backend. + pub internal_target_features: FxIndexSet, /// The version of the rustc process, possibly including a commit hash and description. pub cfg_version: &'static str, @@ -1388,8 +1388,7 @@ pub fn build_session( ctfe_backtrace, miri_unleashed_features: Lock::new(Default::default()), asm_arch, - target_features: Default::default(), - unstable_target_features: Default::default(), + internal_target_features: Default::default(), cfg_version, using_internal_features, env_depinfo: Default::default(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a747b0aec7b28..9af73eb1e3259 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3835,7 +3835,7 @@ impl Target { pub fn object_architecture( &self, - unstable_target_features: &FxIndexSet, + internal_target_features: &FxIndexSet, ) -> Option<(object::Architecture, Option)> { use object::Architecture; Some(match self.arch { @@ -3878,7 +3878,7 @@ impl Target { Arch::RiscV32 => (Architecture::Riscv32, None), Arch::RiscV64 => (Architecture::Riscv64, None), Arch::Sparc => { - if unstable_target_features.contains(&sym::v8plus) { + if internal_target_features.contains(&sym::v8plus) { // Target uses V8+, aka EM_SPARC32PLUS, aka 64-bit V9 but in 32-bit mode (Architecture::Sparc32Plus, None) } else { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index ce09972396e56..f1dd2d8191985 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -28,8 +28,8 @@ //! call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as //! arguments, so letting people toggle that feature would be unsound. To this end, the //! [`Target::abi_required_features`] function computes which target features must and must not be -//! enabled for any given target, and individual features can also be marked as [`Forbidden`]. See -//! for some more context. +//! enabled for any given target, and individual features can also be marked as [`InternalOnly`]. +//! See for some more context. //! //! The one exception to features that change the ABI is features that enable larger vector //! registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store @@ -45,7 +45,8 @@ use rustc_span::{Symbol, sym}; use crate::spec::{Arch, FloatAbi, LlvmAbi, RustcAbi, Target}; -/// Features that control behaviour of rustc, rather than the codegen. +/// Features that control behaviour of rustc, rather than the codegen. Not to be included in +/// `cfg(target_feature)`, `sess.internal_target_features`, or the backend's feature list. /// These exist globally and are not in the target-specific lists below. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -69,17 +70,21 @@ pub enum Stability { /// feature gate! Symbol, ), + /// This is not actually something we expose as a "target feature" to our users. + /// We just manage it internally as a target feature since that's how LLVM represents it. /// 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 are actually ABI configuration flags (such as "soft-float" on many targets). - /// However, "forbidden" target features can still sometimes be enabled via `-Ctarget-cpu` or - /// target feature implications (on the Rust/LLVM level). To prevent that, ABI-relevant target - /// features are ideally pinned down (required or forbidden) in - /// [`Target::abi_required_features`]. - Forbidden { + /// + /// However, "internal" target features can still sometimes be enabled or disabled via + /// `-Ctarget-cpu` or Rust/LLVM target feature implications. Make sure nothing implies this + /// target feature and nothing is implied by this target feature (except for other internal-only + /// features). Ideally, ABI-relevant target features are pinned down (marked as required or + /// incompatible) in [`Target::abi_required_features`]. + InternalOnly { reason: &'static str, /// True if this is always an error, false if this can be reported as a warning when set via - /// `-Ctarget-feature`. + /// `-Ctarget-feature` (and a hard error when set via `#[target_feature]`). hard_error: bool, }, } @@ -120,7 +125,9 @@ impl Stability { } } Stability::Stable { .. } => None, - Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), + Stability::InternalOnly { .. } => { + panic!("internal-only features should not reach this far") + } } } @@ -138,7 +145,7 @@ impl Stability { Stability::Unstable(_) | Stability::CfgStableToggleUnstable(_) | Stability::Stable { .. } => Ok(()), - Stability::Forbidden { reason, hard_error: _ } => Err(reason), + Stability::InternalOnly { reason, hard_error: _ } => Err(reason), } } } @@ -157,7 +164,8 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", - Stability::Forbidden { + // Not implied by any CPU model or other feature. + Stability::InternalOnly { reason: "unsound because it changes the ABI of atomic operations", hard_error: false, }, @@ -244,7 +252,8 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. ( "fp-armv8", - Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + // Pinned down by [`Target::abi_required_features`] when needed. + Stability::InternalOnly { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, &[], ), // FEAT_FP8 @@ -311,7 +320,8 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdm", Stable, &["neon"]), ( "reserve-x18", - Forbidden { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + InternalOnly { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, &[], ), // FEAT_SB @@ -492,7 +502,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdseed", Stable, &[]), ( "retpoline-external-thunk", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline-external-thunk` compiler flag instead", hard_error: false, }, @@ -500,7 +511,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ( "retpoline-indirect-branches", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline` compiler flag instead", hard_error: false, }, @@ -508,7 +520,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ), ( "retpoline-indirect-calls", - Stability::Forbidden { + // Not implied by any CPU model or other feature; the compiler flag is a target modifier. + Stability::InternalOnly { reason: "use `-Zretpoline` compiler flag instead", hard_error: false, }, @@ -521,7 +534,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sm4", Stable, &["avx2"]), ( "soft-float", - Stability::Forbidden { reason: "use a soft-float target instead", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + Stability::InternalOnly { reason: "use a soft-float target instead", hard_error: false }, &[], ), ("sse", Stable, &[]), @@ -585,7 +599,8 @@ static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("altivec", Unstable(sym::powerpc_target_feature), &[]), ( "hard-float", - Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[], ), ("msync", Unstable(sym::powerpc_target_feature), &[]), @@ -597,7 +612,12 @@ static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), - ("spe", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + ( + "spe", + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, + &[], + ), ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; @@ -661,7 +681,8 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f", CfgStableToggleUnstable(sym::riscv_target_feature), &["zicsr"]), ( "forced-atomics", - Stability::Forbidden { + // Not implied by any CPU model or other feature. + Stability::InternalOnly { reason: "unsound because it changes the ABI of atomic operations", hard_error: false, }, @@ -921,7 +942,8 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Stable, &[]), ("miscellaneous-extensions-4", Stable, &[]), ("nnp-assist", Stable, &["vector"]), - ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), + // Pinned down by [`Target::abi_required_features`]. + ("soft-float", InternalOnly { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Stable, &[]), ("vector-enhancements-1", Stable, &["vector"]), @@ -975,7 +997,8 @@ static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("spmx", Unstable(sym::avr_target_feature), &[]), ( "sram", - Forbidden { reason: "devices that have no SRAM are unsupported", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { reason: "devices that have no SRAM are unsupported", hard_error: false }, &[], ), ("tinyencoding", Unstable(sym::avr_target_feature), &[]), @@ -990,7 +1013,11 @@ const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("interrupt", Unstable(sym::xtensa_target_feature), &["exception"]), ( "windowed", - Forbidden { reason: "windowed changes the Xtensa calling convention", hard_error: false }, + // Pinned down by [`Target::abi_required_features`]. + InternalOnly { + reason: "windowed changes the Xtensa calling convention", + hard_error: false, + }, &["exception"], ), ("loop", Unstable(sym::xtensa_target_feature), &[]), @@ -1017,17 +1044,17 @@ const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! pub fn all_rust_features() -> impl Iterator { std::iter::empty() - .chain(ARM_FEATURES.iter()) - .chain(AARCH64_FEATURES.iter()) - .chain(X86_FEATURES.iter()) - .chain(HEXAGON_FEATURES.iter()) - .chain(POWERPC_FEATURES.iter()) - .chain(MIPS_FEATURES.iter()) - .chain(NVPTX_FEATURES.iter()) - .chain(RISCV_FEATURES.iter()) - .chain(WASM_FEATURES.iter()) - .chain(BPF_FEATURES.iter()) - .chain(XTENSA_FEATURES.iter()) + .chain(ARM_FEATURES) + .chain(AARCH64_FEATURES) + .chain(X86_FEATURES) + .chain(HEXAGON_FEATURES) + .chain(POWERPC_FEATURES) + .chain(MIPS_FEATURES) + .chain(NVPTX_FEATURES) + .chain(RISCV_FEATURES) + .chain(WASM_FEATURES) + .chain(BPF_FEATURES) + .chain(XTENSA_FEATURES) .chain(CSKY_FEATURES) .chain(LOONGARCH_FEATURES) .chain(IBMZ_FEATURES) @@ -1152,6 +1179,16 @@ impl Target { } } + /// Computes a map mapping each Rust target feature to the features it implies. + pub fn rust_target_features_map( + &self, + ) -> FxHashMap<&'static str, (Stability, ImpliedFeatures)> { + self.rust_target_features() + .iter() + .map(|&(f, s, i)| (f, (s, i))) + .collect::>() + } + pub fn features_for_correct_fixed_length_vector_abi(&self) -> &'static [(u64, &'static str)] { match &self.arch { Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, @@ -1193,20 +1230,23 @@ impl Target { } } - // Note: the returned set includes `base_feature`. - pub fn implied_target_features<'a>(&self, base_feature: &'a str) -> FxHashSet<&'a str> { - let implied_features = - self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::>(); - + /// Note: the returned set includes `base_feature`. + #[track_caller] + pub fn implied_target_features<'a>( + &self, + base_feature: &'a str, + target_features_map: &FxHashMap<&'static str, (Stability, ImpliedFeatures)>, + ) -> FxHashSet<&'a str> { // 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 = vec![base_feature]; 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()) - } + let (_, implied_features) = target_features_map + .get(&new_feature) + .unwrap_or_else(|| panic!("encountered non-Rust target feature {new_feature}")); + new_features.extend(implied_features.iter().copied()); } } features @@ -1226,7 +1266,7 @@ impl Target { 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 + // "internal-only" in the list above to ensure that there is a consistent answer to the // questions "which ABI is used". match &self.arch { Arch::X86 => { @@ -1301,9 +1341,9 @@ impl Target { // LLVM will use float registers when `fp-armv8` is available, e.g. for // calls to built-ins. The only way to ensure a consistent softfloat ABI // on aarch64 is to never enable `fp-armv8`, so we enforce that. - // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the - // feature we have to mark as incompatible. - FeatureConstraints { required: &[], incompatible: &["neon"] } + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is also + // marked as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon", "fp-armv8"] } } None => { // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7e46b2f593e49..0512a3daac46e 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -1291,7 +1291,7 @@ fn format_integer_type(it: rustc_abi::IntegerType) -> String { pub(super) fn target(sess: &rustc_session::Session) -> Target { // Build a set of which features are enabled on this target let globally_enabled_features: FxHashSet<&str> = - sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); + sess.internal_target_features.iter().map(|name| name.as_str()).collect(); // Build a map of target feature stability by feature name use rustc_target::target_features::Stability; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 8dc6b5f07b92e..ce66a2b8b29c7 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -945,7 +945,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { target_feature: &str, ) -> InterpResult<'tcx, ()> { let this = self.eval_context_ref(); - if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) { + if !this.tcx.sess.internal_target_features.contains(&Symbol::intern(target_feature)) { throw_ub_format!( "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}" ); diff --git a/src/tools/miri/src/intrinsics/x86/mod.rs b/src/tools/miri/src/intrinsics/x86/mod.rs index d76d35cb722bc..25361a6435b0a 100644 --- a/src/tools/miri/src/intrinsics/x86/mod.rs +++ b/src/tools/miri/src/intrinsics/x86/mod.rs @@ -65,7 +65,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "sse2.pause" => { let [] = this.check_shim_sig_unadjusted(link_name, args)?; // Only exhibit the spin-loop hint behavior when SSE2 is enabled. - if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + if this.tcx.sess.internal_target_features.contains(&Symbol::intern("sse2")) { this.yield_active_thread(); } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index f476614992041..4ba10ce4612b4 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1203,14 +1203,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { if attrs .target_features .iter() - .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name)) + .any(|feature| !ecx.tcx.sess.internal_target_features.contains(&feature.name)) { let unavailable = attrs .target_features .iter() .filter(|&feature| { feature.kind != TargetFeatureKind::Implied - && !ecx.tcx.sess.target_features.contains(&feature.name) + && !ecx.tcx.sess.internal_target_features.contains(&feature.name) }) .fold(String::new(), |mut s, feature| { if !s.is_empty() {