Add implied target features to target_feature attribute

This commit is contained in:
Caleb Zulawski 2024-07-26 00:05:20 -04:00
parent 6696447f78
commit 74653b61a6
5 changed files with 113 additions and 31 deletions

View File

@ -646,22 +646,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
} }
} }
// This is a workaround for a LLVM bug that doesn't implicitly enable
// `simd128` when `relaxed-simd` is.
// See <https://github.com/llvm/llvm-project/pull/99803>, which didn't make
// it into a released version of LLVM yet.
//
// This doesn't use the "implicit target feature" system because it is only
// used for function attributes in other targets, which fixes this bug as
// well on the function attribute level.
if sess.target.families.contains(&"wasm".into()) {
if features.iter().any(|f| f == "+relaxed-simd")
&& !features.iter().any(|f| f == "+simd128")
{
features.push("+simd128".into());
}
}
if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) {
sess.dcx().emit_err(TargetFeatureDisableOrEnable { sess.dcx().emit_err(TargetFeatureDisableOrEnable {
features: f, features: f,

View File

@ -1,7 +1,7 @@
use rustc_ast::ast; use rustc_ast::ast;
use rustc_attr::InstructionSetAttr; use rustc_attr::InstructionSetAttr;
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_data_structures::unord::UnordMap; use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
@ -30,6 +30,7 @@ pub fn from_target_feature(
.emit(); .emit();
}; };
let rust_features = tcx.features(); let rust_features = tcx.features();
let mut added_target_features = Vec::new();
for item in list { for item in list {
// Only `enable = ...` is accepted in the meta-item list. // Only `enable = ...` is accepted in the meta-item list.
if !item.has_name(sym::enable) { if !item.has_name(sym::enable) {
@ -44,7 +45,7 @@ pub fn from_target_feature(
}; };
// We allow comma separation to enable multiple features. // We allow comma separation to enable multiple features.
target_features.extend(value.as_str().split(',').filter_map(|feature| { added_target_features.extend(value.as_str().split(',').filter_map(|feature| {
let Some(feature_gate) = supported_target_features.get(feature) else { let Some(feature_gate) = supported_target_features.get(feature) else {
let msg = format!("the feature named `{feature}` is not valid for this target"); let msg = format!("the feature named `{feature}` is not valid for this target");
let mut err = tcx.dcx().struct_span_err(item.span(), msg); let mut err = tcx.dcx().struct_span_err(item.span(), msg);
@ -98,13 +99,12 @@ pub fn from_target_feature(
})); }));
} }
for (feature, requires) in tcx.sess.target.implicit_target_features() { // Add implied features
if target_features.iter().any(|f| f.as_str() == *feature) for feature in added_target_features.iter() {
&& !target_features.iter().any(|f| f.as_str() == *requires) target_features
{ .extend(tcx.implied_target_features(*feature).clone().into_sorted_stable_ord());
target_features.push(Symbol::intern(requires));
}
} }
target_features.extend(added_target_features)
} }
/// Computes the set of target features used in a function for the purposes of /// Computes the set of target features used in a function for the purposes of
@ -162,6 +162,28 @@ pub(crate) fn provide(providers: &mut Providers) {
.collect() .collect()
} }
}, },
implied_target_features: |tcx, feature| {
let implied_features = tcx
.sess
.target
.implied_target_features()
.iter()
.map(|(f, i)| (Symbol::intern(f), i))
.collect::<FxHashMap<_, _>>();
// 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 = UnordSet::new();
let mut new_features = vec![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().map(Symbol::intern))
}
}
}
features
},
asm_target_features, asm_target_features,
..*providers ..*providers
} }

View File

@ -2183,6 +2183,12 @@ rustc_queries! {
desc { "looking up supported target features" } desc { "looking up supported target features" }
} }
query implied_target_features(feature: Symbol) -> &'tcx UnordSet<Symbol> {
arena_cache
eval_always
desc { "looking up implied target features" }
}
query features_query(_: ()) -> &'tcx rustc_feature::Features { query features_query(_: ()) -> &'tcx rustc_feature::Features {
feedable feedable
desc { "looking up enabled feature gates" } desc { "looking up enabled feature gates" }

View File

@ -339,8 +339,6 @@ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const WASM_IMPLICIT_FEATURES: &[(&str, &str)] = &[("relaxed-simd", "simd128")];
const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))];
const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[
@ -411,6 +409,54 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[
// tidy-alphabetical-end // tidy-alphabetical-end
]; ];
const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
// tidy-alphabetical-start
("aes", &["sse2"]),
("avx", &["sse4.2"]),
("avx2", &["avx"]),
("f16c", &["avx"]),
("fma", &["avx"]),
("pclmulqdq", &["sse2"]),
("sha", &["sse2"]),
("sse2", &["sse"]),
("sse3", &["sse2"]),
("sse4.1", &["ssse3"]),
("sse4.2", &["sse4.1"]),
("ssse3", &["sse3"]),
// tidy-alphabetical-end
];
const AARCH64_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
// tidy-alphabetical-start
("aes", &["neon"]),
("f32mm", &["sve"]),
("f64mm", &["sve"]),
("fcma", &["neon"]),
("fhm", &["fp16"]),
("fp16", &["neon"]),
("jsconv", &["neon"]),
("rcpc2", &["rcpc"]),
("sha2", &["neon"]),
("sha3", &["sha2"]),
("sm4", &["neon"]),
("sve", &["fp16"]),
("sve2", &["sve"]),
("sve2-aes", &["sve2", "aes"]),
("sve2-bitperm", &["sve2"]),
("sve2-sha3", &["sve2", "sha3"]),
("sve2-sm4", &["sve2", "sm4"]),
// tidy-alphabetical-end
];
const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[
// tidy-alphabetical-start
("zb", &["zba", "zbc", "zbs"]),
("zk", &["zkn", "zkr", "zks", "zkt", "zbkb", "zbkc", "zkbx"]),
("zkn", &["zknd", "zkne", "zknh", "zbkb", "zbkc", "zkbx"]),
("zks", &["zksed", "zksh", "zbkb", "zbkc", "zkbx"]),
// tidy-alphabetical-end
];
/// When rustdoc is running, provide a list of all known features so that all their respective /// When rustdoc is running, provide a list of all known features so that all their respective
/// primitives may be documented. /// primitives may be documented.
/// ///
@ -458,11 +504,11 @@ impl super::spec::Target {
} }
} }
/// Returns a list of target features. Each items first target feature pub fn implied_target_features(&self) -> &'static [(&'static str, &'static [&'static str])] {
/// implicitly enables the second one.
pub fn implicit_target_features(&self) -> &'static [(&'static str, &'static str)] {
match &*self.arch { match &*self.arch {
"wasm32" | "wasm64" => WASM_IMPLICIT_FEATURES, "aarch4" => AARCH64_IMPLIED_FEATURES,
"riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES,
"x86" | "x86_64" => X86_IMPLIED_FEATURES,
_ => &[], _ => &[],
} }
} }

View File

@ -0,0 +1,24 @@
//@ only-x86_64
//@ run-pass
#![feature(target_feature_11)]
#![allow(dead_code)]
#[target_feature(enable = "ssse3")]
fn call_ssse3() {}
#[target_feature(enable = "avx")]
fn call_avx() {}
#[target_feature(enable = "avx2")]
fn test_avx2() {
call_ssse3();
call_avx();
}
#[target_feature(enable = "fma")]
fn test_fma() {
call_ssse3();
call_avx();
}
fn main() {}