|
1 | 1 | //! Shared utilities for dealing with target features that haven't (yet) found a better home.
|
2 | 2 | use rustc_attr_data_structures::InstructionSetAttr;
|
3 |
| -use rustc_data_structures::fx::FxIndexSet; |
| 3 | +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; |
4 | 4 | use rustc_data_structures::unord::{UnordMap, UnordSet};
|
5 | 5 | use rustc_errors::Applicability;
|
6 | 6 | use rustc_hir as hir;
|
7 | 7 | use rustc_hir::def::DefKind;
|
8 | 8 | use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
| 9 | +use rustc_session::Session; |
9 | 10 | use rustc_session::parse::feature_err;
|
10 | 11 | use rustc_span::{Span, Symbol, sym};
|
11 |
| -use rustc_target::target_features::{self, Stability}; |
| 12 | +use rustc_target::target_features::{self, RUSTC_SPECIAL_FEATURES, Stability}; |
12 | 13 |
|
13 | 14 | use crate::error;
|
14 | 15 | use crate::middle::codegen_fn_attrs::TargetFeature;
|
@@ -141,6 +142,100 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
|
141 | 142 | }
|
142 | 143 | }
|
143 | 144 |
|
| 145 | +/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically, |
| 146 | +/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and |
| 147 | +/// 2nd component of the return value, respectively). |
| 148 | +/// |
| 149 | +/// `target_feature_flag` is the value of `-Ctarget-feature` (giving the caller a chance to override it). |
| 150 | +/// `target_base_has_feature` should check whether the given feature (a Rust feature name!) is enabled |
| 151 | +/// in the "base" target machine, i.e., without applying `-Ctarget-feature`. |
| 152 | +/// |
| 153 | +/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled elsewhere. |
| 154 | +pub fn cfg( |
| 155 | + sess: &Session, |
| 156 | + target_feature_flag: &str, |
| 157 | + mut is_feature_enabled: impl FnMut(&str) -> bool, |
| 158 | +) -> (Vec<Symbol>, Vec<Symbol>) { |
| 159 | + // Compute which of the known target features are enabled in the 'base' target machine. We only |
| 160 | + // consider "supported" features; "forbidden" features are not reflected in `cfg` as of now. |
| 161 | + let mut features: FxHashSet<Symbol> = sess |
| 162 | + .target |
| 163 | + .rust_target_features() |
| 164 | + .iter() |
| 165 | + .filter(|(feature, _, _)| { |
| 166 | + // Skip checking special features, those are not known to the backend. |
| 167 | + if RUSTC_SPECIAL_FEATURES.contains(feature) { |
| 168 | + return true; |
| 169 | + } |
| 170 | + is_feature_enabled(feature) |
| 171 | + }) |
| 172 | + .map(|(feature, _, _)| Symbol::intern(feature)) |
| 173 | + .collect(); |
| 174 | + |
| 175 | + // Add enabled and remove disabled features. |
| 176 | + for (enabled, feature) in |
| 177 | + target_feature_flag.split(',').filter_map(|s| match s.chars().next() { |
| 178 | + Some('+') => Some((true, Symbol::intern(&s[1..]))), |
| 179 | + Some('-') => Some((false, Symbol::intern(&s[1..]))), |
| 180 | + _ => None, |
| 181 | + }) |
| 182 | + { |
| 183 | + if enabled { |
| 184 | + // Also add all transitively implied features. |
| 185 | + |
| 186 | + // We don't care about the order in `features` since the only thing we use it for is the |
| 187 | + // `features.contains` below. |
| 188 | + #[allow(rustc::potential_query_instability)] |
| 189 | + features.extend( |
| 190 | + sess.target |
| 191 | + .implied_target_features(feature.as_str()) |
| 192 | + .iter() |
| 193 | + .map(|s| Symbol::intern(s)), |
| 194 | + ); |
| 195 | + } else { |
| 196 | + // Remove transitively reverse-implied features. |
| 197 | + |
| 198 | + // We don't care about the order in `features` since the only thing we use it for is the |
| 199 | + // `features.contains` below. |
| 200 | + #[allow(rustc::potential_query_instability)] |
| 201 | + features.retain(|f| { |
| 202 | + if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) { |
| 203 | + // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to |
| 204 | + // remove `f`. (This is the standard logical contraposition principle.) |
| 205 | + false |
| 206 | + } else { |
| 207 | + // We can keep `f`. |
| 208 | + true |
| 209 | + } |
| 210 | + }); |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + // Filter enabled features based on feature gates. |
| 215 | + let f = |allow_unstable| { |
| 216 | + sess.target |
| 217 | + .rust_target_features() |
| 218 | + .iter() |
| 219 | + .filter_map(|(feature, gate, _)| { |
| 220 | + // The `allow_unstable` set is used by rustc internally to determine which target |
| 221 | + // features are truly available, so we want to return even perma-unstable |
| 222 | + // "forbidden" features. |
| 223 | + if allow_unstable |
| 224 | + || (gate.in_cfg() |
| 225 | + && (sess.is_nightly_build() || gate.requires_nightly().is_none())) |
| 226 | + { |
| 227 | + Some(Symbol::intern(feature)) |
| 228 | + } else { |
| 229 | + None |
| 230 | + } |
| 231 | + }) |
| 232 | + .filter(|feature| features.contains(&feature)) |
| 233 | + .collect() |
| 234 | + }; |
| 235 | + |
| 236 | + (f(true), f(false)) |
| 237 | +} |
| 238 | + |
144 | 239 | pub(crate) fn provide(providers: &mut Providers) {
|
145 | 240 | *providers = Providers {
|
146 | 241 | rust_target_features: |tcx, cnum| {
|
|
0 commit comments