rustc_codegen_ssa/
target_features.rs
1use rustc_attr_parsing::InstructionSetAttr;
2use rustc_data_structures::fx::FxIndexSet;
3use rustc_data_structures::unord::{UnordMap, UnordSet};
4use rustc_errors::Applicability;
5use rustc_hir as hir;
6use rustc_hir::def::DefKind;
7use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
8use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
9use rustc_middle::query::Providers;
10use rustc_middle::ty::TyCtxt;
11use rustc_session::parse::feature_err;
12use rustc_span::{Span, Symbol, sym};
13use rustc_target::target_features::{self, Stability};
14
15use crate::errors;
16
17pub(crate) fn from_target_feature_attr(
20 tcx: TyCtxt<'_>,
21 attr: &hir::Attribute,
22 rust_target_features: &UnordMap<String, target_features::Stability>,
23 target_features: &mut Vec<TargetFeature>,
24) {
25 let Some(list) = attr.meta_item_list() else { return };
26 let bad_item = |span| {
27 let msg = "malformed `target_feature` attribute input";
28 let code = "enable = \"..\"";
29 tcx.dcx()
30 .struct_span_err(span, msg)
31 .with_span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
32 .emit();
33 };
34 let rust_features = tcx.features();
35 let abi_feature_constraints = tcx.sess.target.abi_required_features();
36 for item in list {
37 if !item.has_name(sym::enable) {
39 bad_item(item.span());
40 continue;
41 }
42
43 let Some(value) = item.value_str() else {
45 bad_item(item.span());
46 continue;
47 };
48
49 for feature in value.as_str().split(',') {
51 let Some(stability) = rust_target_features.get(feature) else {
52 let msg = format!("the feature named `{feature}` is not valid for this target");
53 let mut err = tcx.dcx().struct_span_err(item.span(), msg);
54 err.span_label(item.span(), format!("`{feature}` is not valid for this target"));
55 if let Some(stripped) = feature.strip_prefix('+') {
56 let valid = rust_target_features.contains_key(stripped);
57 if valid {
58 err.help("consider removing the leading `+` in the feature name");
59 }
60 }
61 err.emit();
62 continue;
63 };
64
65 if let Err(reason) = stability.toggle_allowed() {
68 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
69 span: item.span(),
70 feature,
71 reason,
72 });
73 } else if let Some(nightly_feature) = stability.requires_nightly()
74 && !rust_features.enabled(nightly_feature)
75 {
76 feature_err(
77 &tcx.sess,
78 nightly_feature,
79 item.span(),
80 format!("the target feature `{feature}` is currently unstable"),
81 )
82 .emit();
83 } else {
84 let feature_sym = Symbol::intern(feature);
86 for &name in tcx.implied_target_features(feature_sym) {
87 if !tcx.sess.opts.actually_rustdoc {
94 if abi_feature_constraints.incompatible.contains(&name.as_str()) {
95 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
96 span: item.span(),
97 feature: name.as_str(),
98 reason: "this feature is incompatible with the target ABI",
99 });
100 }
101 }
102 target_features.push(TargetFeature { name, implied: name != feature_sym })
103 }
104 }
105 }
106 }
107}
108
109fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
112 let mut target_features = tcx.sess.unstable_target_features.clone();
113 if tcx.def_kind(did).has_codegen_attrs() {
114 let attrs = tcx.codegen_fn_attrs(did);
115 target_features.extend(attrs.target_features.iter().map(|feature| feature.name));
116 match attrs.instruction_set {
117 None => {}
118 Some(InstructionSetAttr::ArmA32) => {
119 target_features.swap_remove(&sym::thumb_mode);
121 }
122 Some(InstructionSetAttr::ArmT32) => {
123 target_features.insert(sym::thumb_mode);
124 }
125 }
126 }
127
128 tcx.arena.alloc(target_features)
129}
130
131pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
134 if let DefKind::AssocFn = tcx.def_kind(id) {
135 let parent_id = tcx.local_parent(id);
136 if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
137 tcx.dcx().emit_err(errors::TargetFeatureSafeTrait {
138 span: attr_span,
139 def: tcx.def_span(id),
140 });
141 }
142 }
143}
144
145pub(crate) fn provide(providers: &mut Providers) {
146 *providers = Providers {
147 rust_target_features: |tcx, cnum| {
148 assert_eq!(cnum, LOCAL_CRATE);
149 if tcx.sess.opts.actually_rustdoc {
150 let mut result: UnordMap<String, Stability> = Default::default();
156 for (name, stability) in rustc_target::target_features::all_rust_features() {
157 use std::collections::hash_map::Entry;
158 match result.entry(name.to_owned()) {
159 Entry::Vacant(vacant_entry) => {
160 vacant_entry.insert(stability);
161 }
162 Entry::Occupied(mut occupied_entry) => {
163 match (occupied_entry.get(), stability) {
165 (Stability::Stable, _)
166 | (
167 Stability::Unstable { .. },
168 Stability::Unstable { .. } | Stability::Forbidden { .. },
169 )
170 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
171 }
173 _ => {
174 occupied_entry.insert(stability);
176 }
177 }
178 }
179 }
180 }
181 result
182 } else {
183 tcx.sess
184 .target
185 .rust_target_features()
186 .iter()
187 .map(|(a, b, _)| (a.to_string(), *b))
188 .collect()
189 }
190 },
191 implied_target_features: |tcx, feature: Symbol| {
192 let feature = feature.as_str();
193 UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature)))
194 .into_sorted_stable_ord()
195 .into_iter()
196 .map(|s| Symbol::intern(s))
197 .collect()
198 },
199 asm_target_features,
200 ..*providers
201 }
202}