1
Fork 0

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`
This commit is contained in:
bors 2025-01-05 23:21:06 +00:00
commit feb32c6546
25 changed files with 837 additions and 793 deletions

View file

@ -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