X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_codegen_ssa%2Fsrc%2Ftarget_features.rs;h=0dabe96b60277e032ad0214d2260277915f6a02d;hb=dc30b92cc576ed9e097db7fece0af13662de8d8d;hp=301683e8e85449370652ce4825c666715b488e8d;hpb=aec60c6b7cc511979b176f9935b9f9d7c807ecd2;p=rust.git diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 301683e8e85..0dabe96b602 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,8 +1,19 @@ +use rustc_ast::ast; +use rustc_attr::InstructionSetAttr; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; +use rustc_span::Span; /// Features that control behaviour of rustc, rather than the codegen. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -322,15 +333,148 @@ pub fn tied_target_features(sess: &Session) -> &'static [&'static [&'static str] } } -pub(crate) fn provide(providers: &mut Providers) { - providers.supported_target_features = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // whitelist them all - all_known_features().map(|(a, b)| (a.to_string(), b)).collect() - } else { - supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect() - } +pub fn from_target_feature( + tcx: TyCtxt<'_>, + attr: &ast::Attribute, + supported_target_features: &FxHashMap>, + target_features: &mut Vec, +) { + let Some(list) = attr.meta_item_list() else { return }; + let bad_item = |span| { + let msg = "malformed `target_feature` attribute input"; + let code = "enable = \"..\""; + tcx.sess + .struct_span_err(span, msg) + .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) + .emit(); }; + let rust_features = tcx.features(); + for item in list { + // Only `enable = ...` is accepted in the meta-item list. + if !item.has_name(sym::enable) { + bad_item(item.span()); + continue; + } + + // Must be of the form `enable = "..."` (a string). + let Some(value) = item.value_str() else { + bad_item(item.span()); + continue; + }; + + // We allow comma separation to enable multiple features. + target_features.extend(value.as_str().split(',').filter_map(|feature| { + let Some(feature_gate) = supported_target_features.get(feature) else { + let msg = + format!("the feature named `{}` is not valid for this target", feature); + let mut err = tcx.sess.struct_span_err(item.span(), &msg); + err.span_label( + item.span(), + format!("`{}` is not valid for this target", feature), + ); + if let Some(stripped) = feature.strip_prefix('+') { + let valid = supported_target_features.contains_key(stripped); + if valid { + err.help("consider removing the leading `+` in the feature name"); + } + } + err.emit(); + return None; + }; + + // Only allow features whose feature gates have been enabled. + let allowed = match feature_gate.as_ref().copied() { + Some(sym::arm_target_feature) => rust_features.arm_target_feature, + Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, + Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, + Some(sym::mips_target_feature) => rust_features.mips_target_feature, + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, + Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, + Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, + Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, + Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, + Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, + Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, + Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, + Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, + Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, + Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, + Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, + Some(name) => bug!("unknown target feature gate {}", name), + None => true, + }; + if !allowed { + feature_err( + &tcx.sess.parse_sess, + feature_gate.unwrap(), + item.span(), + &format!("the target feature `{}` is currently unstable", feature), + ) + .emit(); + } + Some(Symbol::intern(feature)) + })); + } +} + +/// Computes the set of target features used in a function for the purposes of +/// inline assembly. +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet { + let mut target_features = tcx.sess.unstable_target_features.clone(); + if tcx.def_kind(did).has_codegen_attrs() { + let attrs = tcx.codegen_fn_attrs(did); + target_features.extend(&attrs.target_features); + match attrs.instruction_set { + None => {} + Some(InstructionSetAttr::ArmA32) => { + target_features.remove(&sym::thumb_mode); + } + Some(InstructionSetAttr::ArmT32) => { + target_features.insert(sym::thumb_mode); + } + } + } + + tcx.arena.alloc(target_features) +} + +/// Checks the function annotated with `#[target_feature]` is not a safe +/// trait method implementation, reporting an error if it is. +pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { + let hir_id = tcx.hir().local_def_id_to_hir_id(id); + let node = tcx.hir().get(hir_id); + if let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { + let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_item = tcx.hir().expect_item(parent_id.def_id); + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { + tcx.sess + .struct_span_err( + attr_span, + "`#[target_feature(..)]` cannot be applied to safe trait method", + ) + .span_label(attr_span, "cannot be applied to safe trait method") + .span_label(tcx.def_span(id), "not an `unsafe` function") + .emit(); + } + } +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { + supported_target_features: |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.sess.opts.actually_rustdoc { + // rustdoc needs to be able to document functions that use all the features, so + // whitelist them all + all_known_features().map(|(a, b)| (a.to_string(), b)).collect() + } else { + supported_target_features(tcx.sess) + .iter() + .map(|&(a, b)| (a.to_string(), b)) + .collect() + } + }, + asm_target_features, + ..*providers + } }