about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-05-10 22:53:38 +0200
committerRalf Jung <post@ralfj.de>2025-06-19 09:42:35 +0900
commitd70ec32ea7e03e3a082a45eeba6c8aa4c653efb4 (patch)
tree214833f4ba7753aec677b422eed7a911c1bfdb76 /compiler/rustc_codegen_ssa/src
parent044514eb26511d2d8aa999fdf27e85df6beb6576 (diff)
downloadrust-d70ec32ea7e03e3a082a45eeba6c8aa4c653efb4.tar.gz
rust-d70ec32ea7e03e3a082a45eeba6c8aa4c653efb4.zip
move cfg(target_feature) computation into shared place
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs99
1 files changed, 97 insertions, 2 deletions
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 640d197c219..476671d6855 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,5 +1,5 @@
 use rustc_attr_data_structures::InstructionSetAttr;
-use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -8,11 +8,12 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
+use rustc_session::Session;
 use rustc_session::features::StabilityExt;
 use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
-use rustc_target::target_features::{self, Stability};
+use rustc_target::target_features::{self, RUSTC_SPECIAL_FEATURES, Stability};
 
 use crate::errors;
 
@@ -156,6 +157,100 @@ pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId,
     }
 }
 
+/// 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).
+///
+/// `target_feature_flag` is the value of `-Ctarget-feature` (giving the caller a chance to override it).
+/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is enabled
+/// in the "base" target machine, i.e., without applying `-Ctarget-feature`.
+///
+/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere.
+pub fn cfg_target_feature(
+    sess: &Session,
+    target_feature_flag: &str,
+    mut is_feature_enabled: impl FnMut(&str) -> bool,
+) -> (Vec<Symbol>, Vec<Symbol>) {
+    // 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.
+    let mut features: FxHashSet<Symbol> = sess
+        .target
+        .rust_target_features()
+        .iter()
+        .filter(|(feature, _, _)| {
+            // Skip checking special features, those are not known to the backend.
+            if RUSTC_SPECIAL_FEATURES.contains(feature) {
+                return true;
+            }
+            is_feature_enabled(feature)
+        })
+        .map(|(feature, _, _)| Symbol::intern(feature))
+        .collect();
+
+    // Add enabled and remove disabled features.
+    for (enabled, feature) in
+        target_feature_flag.split(',').filter_map(|s| match s.chars().next() {
+            Some('+') => Some((true, Symbol::intern(&s[1..]))),
+            Some('-') => Some((false, Symbol::intern(&s[1..]))),
+            _ => None,
+        })
+    {
+        if enabled {
+            // Also add all transitively implied features.
+
+            // 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(feature.as_str())
+                    .iter()
+                    .map(|s| Symbol::intern(s)),
+            );
+        } else {
+            // Remove transitively reverse-implied features.
+
+            // 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.retain(|f| {
+                if sess.target.implied_target_features(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
+                } else {
+                    // We can keep `f`.
+                    true
+                }
+            });
+        }
+    }
+
+    // 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().is_none()))
+                {
+                    Some(Symbol::intern(feature))
+                } else {
+                    None
+                }
+            })
+            .filter(|feature| features.contains(&feature))
+            .collect()
+    };
+
+    (f(true), f(false))
+}
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         rust_target_features: |tcx, cnum| {