Skip to content

Commit 8175ae7

Browse files
committed
unify two -Ctarget-feature parsers
This does change the logic a bit: previously, we didn't forward reverse implications of negated features to the backend, instead relying on the backend to handle the implication itself.
1 parent 67f5d26 commit 8175ae7

File tree

1 file changed

+77
-52
lines changed

1 file changed

+77
-52
lines changed

compiler/rustc_middle/src/target_features.rs

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,54 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
145145
}
146146
}
147147

148+
/// Parse the value of `-Ctarget-feature`, also expanding implied features,
149+
/// and call the closure for each (expanded) Rust feature. If the list contains
150+
/// a syntactically invalid item (not starting with `+`/`-`), the error callback is invoked.
151+
fn parse_rust_feature_flag<'a>(
152+
sess: &Session,
153+
target_feature_flag: &'a str,
154+
err_callback: impl Fn(&'a str),
155+
mut callback: impl FnMut(FxHashSet<&'a str>, /* enable */ bool),
156+
) {
157+
// A cache for the backwards implication map.
158+
let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
159+
160+
for feature in target_feature_flag.split(',') {
161+
if let Some(base_feature) = feature.strip_prefix('+') {
162+
callback(sess.target.implied_target_features(base_feature), true)
163+
} else if let Some(base_feature) = feature.strip_prefix('-') {
164+
// If `f1` implies `f2`, then `!f2` implies `!f1` -- this is standard logical contraposition.
165+
// So we have to find all the reverse implications of `base_feature` and disable them, too.
166+
167+
let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
168+
let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
169+
for (f, _, is) in sess.target.rust_target_features() {
170+
for i in is.iter() {
171+
set.entry(i).or_default().insert(f);
172+
}
173+
}
174+
set
175+
});
176+
177+
// Inverse mplied target features have their own inverse implied target features, so we
178+
// traverse the map until there are no more features to add.
179+
let mut features = FxHashSet::default();
180+
let mut new_features = vec![base_feature];
181+
while let Some(new_feature) = new_features.pop() {
182+
if features.insert(new_feature) {
183+
if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
184+
new_features.extend(implied_features)
185+
}
186+
}
187+
}
188+
189+
callback(features, false)
190+
} else if !feature.is_empty() {
191+
err_callback(feature)
192+
}
193+
}
194+
}
195+
148196
/// Utility function for a codegen backend to compute `cfg(target_feature)`, or more specifically,
149197
/// to populate `sess.unstable_target_features` and `sess.target_features` (these are the first and
150198
/// 2nd component of the return value, respectively).
@@ -161,7 +209,7 @@ pub fn cfg(
161209
) -> (Vec<Symbol>, Vec<Symbol>) {
162210
// Compute which of the known target features are enabled in the 'base' target machine. We only
163211
// consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
164-
let mut features: FxHashSet<Symbol> = sess
212+
let mut features: UnordSet<Symbol> = sess
165213
.target
166214
.rust_target_features()
167215
.iter()
@@ -176,43 +224,23 @@ pub fn cfg(
176224
.collect();
177225

178226
// Add enabled and remove disabled features.
179-
for (enabled, feature) in
180-
target_feature_flag.split(',').filter_map(|s| match s.chars().next() {
181-
Some('+') => Some((true, Symbol::intern(&s[1..]))),
182-
Some('-') => Some((false, Symbol::intern(&s[1..]))),
183-
_ => None,
184-
})
185-
{
186-
if enabled {
187-
// Also add all transitively implied features.
188-
189-
// We don't care about the order in `features` since the only thing we use it for is the
190-
// `features.contains` below.
191-
#[allow(rustc::potential_query_instability)]
192-
features.extend(
193-
sess.target
194-
.implied_target_features(feature.as_str())
195-
.iter()
196-
.map(|s| Symbol::intern(s)),
197-
);
198-
} else {
199-
// Remove transitively reverse-implied features.
200-
201-
// We don't care about the order in `features` since the only thing we use it for is the
202-
// `features.contains` below.
227+
parse_rust_feature_flag(
228+
sess,
229+
target_feature_flag,
230+
/* err_callback */ |_| {},
231+
|new_features, enabled| {
232+
// Iteration order is irrelevant since this only influences an `UnordSet`.
203233
#[allow(rustc::potential_query_instability)]
204-
features.retain(|f| {
205-
if sess.target.implied_target_features(f.as_str()).contains(&feature.as_str()) {
206-
// If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
207-
// remove `f`. (This is the standard logical contraposition principle.)
208-
false
209-
} else {
210-
// We can keep `f`.
211-
true
234+
if enabled {
235+
features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
236+
} else {
237+
// Remove `new_features` from `features`.
238+
for new in new_features {
239+
features.remove(&Symbol::intern(new));
212240
}
213-
});
214-
}
215-
}
241+
}
242+
},
243+
);
216244

217245
// Filter enabled features based on feature gates.
218246
let f = |allow_unstable| {
@@ -275,25 +303,22 @@ pub fn flag_to_backend_features<'a, const N: usize>(
275303

276304
// Compute implied features
277305
let mut rust_features = vec![];
278-
for feature in sess.opts.cg.target_feature.split(',') {
279-
if let Some(feature) = feature.strip_prefix('+') {
280-
rust_features.extend(
281-
UnordSet::from(sess.target.implied_target_features(feature))
282-
.to_sorted_stable_ord()
283-
.iter()
284-
.map(|&&s| (true, s)),
285-
)
286-
} else if let Some(feature) = feature.strip_prefix('-') {
287-
// FIXME: Why do we not remove implied features on "-" here?
288-
// We do the equivalent above in `target_config`.
289-
// See <https://github.com/rust-lang/rust/issues/134792>.
290-
rust_features.push((false, feature));
291-
} else if !feature.is_empty() {
306+
parse_rust_feature_flag(
307+
sess,
308+
&sess.opts.cg.target_feature,
309+
/* err_callback */
310+
|feature| {
292311
if diagnostics {
293312
sess.dcx().emit_warn(error::UnknownCTargetFeaturePrefix { feature });
294313
}
295-
}
296-
}
314+
},
315+
|new_features, enabled| {
316+
rust_features.extend(
317+
UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enabled, s)),
318+
);
319+
},
320+
);
321+
297322
// Remove features that are meant for rustc, not the backend.
298323
rust_features.retain(|(_, feature)| {
299324
// Retain if it is not a rustc feature

0 commit comments

Comments
 (0)