1use rustc_attr_data_structures::InstructionSetAttr;
2use rustc_data_structures::fx::{FxHashMap, FxHashSet, 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::Session;
12use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
13use rustc_session::parse::feature_err;
14use rustc_span::{Span, Symbol, sym};
15use rustc_target::target_features::{self, RUSTC_SPECIFIC_FEATURES, Stability};
16use smallvec::SmallVec;
17
18use crate::errors;
19
20pub(crate) fn from_target_feature_attr(
23 tcx: TyCtxt<'_>,
24 did: LocalDefId,
25 attr: &hir::Attribute,
26 rust_target_features: &UnordMap<String, target_features::Stability>,
27 target_features: &mut Vec<TargetFeature>,
28) {
29 let Some(list) = attr.meta_item_list() else { return };
30 let bad_item = |span| {
31 let msg = "malformed `target_feature` attribute input";
32 let code = "enable = \"..\"";
33 tcx.dcx()
34 .struct_span_err(span, msg)
35 .with_span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
36 .emit();
37 };
38 let rust_features = tcx.features();
39 let abi_feature_constraints = tcx.sess.target.abi_required_features();
40 for item in list {
41 if !item.has_name(sym::enable) {
43 bad_item(item.span());
44 continue;
45 }
46
47 let Some(value) = item.value_str() else {
49 bad_item(item.span());
50 continue;
51 };
52
53 for feature in value.as_str().split(',') {
55 let Some(stability) = rust_target_features.get(feature) else {
56 let msg = format!("the feature named `{feature}` is not valid for this target");
57 let mut err = tcx.dcx().struct_span_err(item.span(), msg);
58 err.span_label(item.span(), format!("`{feature}` is not valid for this target"));
59 if let Some(stripped) = feature.strip_prefix('+') {
60 let valid = rust_target_features.contains_key(stripped);
61 if valid {
62 err.help("consider removing the leading `+` in the feature name");
63 }
64 }
65 err.emit();
66 continue;
67 };
68
69 if let Err(reason) = stability.toggle_allowed() {
72 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
73 span: item.span(),
74 feature,
75 reason,
76 });
77 } else if let Some(nightly_feature) = stability.requires_nightly()
78 && !rust_features.enabled(nightly_feature)
79 {
80 feature_err(
81 &tcx.sess,
82 nightly_feature,
83 item.span(),
84 format!("the target feature `{feature}` is currently unstable"),
85 )
86 .emit();
87 } else {
88 let feature_sym = Symbol::intern(feature);
90 for &name in tcx.implied_target_features(feature_sym) {
91 if !tcx.sess.opts.actually_rustdoc {
98 if abi_feature_constraints.incompatible.contains(&name.as_str()) {
99 if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" {
102 tcx.emit_node_span_lint(
103 AARCH64_SOFTFLOAT_NEON,
104 tcx.local_def_id_to_hir_id(did),
105 item.span(),
106 errors::Aarch64SoftfloatNeon,
107 );
108 } else {
109 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
110 span: item.span(),
111 feature: name.as_str(),
112 reason: "this feature is incompatible with the target ABI",
113 });
114 }
115 }
116 }
117 target_features.push(TargetFeature { name, implied: name != feature_sym })
118 }
119 }
120 }
121 }
122}
123
124fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
127 let mut target_features = tcx.sess.unstable_target_features.clone();
128 if tcx.def_kind(did).has_codegen_attrs() {
129 let attrs = tcx.codegen_fn_attrs(did);
130 target_features.extend(attrs.target_features.iter().map(|feature| feature.name));
131 match attrs.instruction_set {
132 None => {}
133 Some(InstructionSetAttr::ArmA32) => {
134 target_features.swap_remove(&sym::thumb_mode);
136 }
137 Some(InstructionSetAttr::ArmT32) => {
138 target_features.insert(sym::thumb_mode);
139 }
140 }
141 }
142
143 tcx.arena.alloc(target_features)
144}
145
146pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
149 if let DefKind::AssocFn = tcx.def_kind(id) {
150 let parent_id = tcx.local_parent(id);
151 if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
152 tcx.dcx().emit_err(errors::TargetFeatureSafeTrait {
153 span: attr_span,
154 def: tcx.def_span(id),
155 });
156 }
157 }
158}
159
160fn parse_rust_feature_flag<'a>(
164 sess: &'a Session,
165 err_callback: impl Fn(&'a str),
166 mut callback: impl FnMut(
167 &'a str,
168 FxHashSet<&'a str>,
169 bool,
170 ),
171) {
172 let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
174
175 for feature in sess.opts.cg.target_feature.split(',') {
176 if let Some(base_feature) = feature.strip_prefix('+') {
177 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
179 return;
180 }
181
182 callback(base_feature, sess.target.implied_target_features(base_feature), true)
183 } else if let Some(base_feature) = feature.strip_prefix('-') {
184 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
186 return;
187 }
188
189 let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
194 let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
195 for (f, _, is) in sess.target.rust_target_features() {
196 for i in is.iter() {
197 set.entry(i).or_default().insert(f);
198 }
199 }
200 set
201 });
202
203 let mut features = FxHashSet::default();
206 let mut new_features = vec![base_feature];
207 while let Some(new_feature) = new_features.pop() {
208 if features.insert(new_feature) {
209 if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
210 new_features.extend(implied_features)
211 }
212 }
213 }
214
215 callback(base_feature, features, false)
216 } else if !feature.is_empty() {
217 err_callback(feature)
218 }
219 }
220}
221
222pub fn cfg_target_feature(
231 sess: &Session,
232 mut target_base_has_feature: impl FnMut(&str) -> bool,
233) -> (Vec<Symbol>, Vec<Symbol>) {
234 let mut features: UnordSet<Symbol> = sess
237 .target
238 .rust_target_features()
239 .iter()
240 .filter(|(feature, _, _)| target_base_has_feature(feature))
241 .map(|(feature, _, _)| Symbol::intern(feature))
242 .collect();
243
244 parse_rust_feature_flag(
246 sess,
247 |_| {
249 },
251 |_base_feature, new_features, enabled| {
252 #[allow(rustc::potential_query_instability)]
254 if enabled {
255 features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
256 } else {
257 for new in new_features {
259 features.remove(&Symbol::intern(new));
260 }
261 }
262 },
263 );
264
265 let f = |allow_unstable| {
267 sess.target
268 .rust_target_features()
269 .iter()
270 .filter_map(|(feature, gate, _)| {
271 if allow_unstable
275 || (gate.in_cfg()
276 && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
277 {
278 Some(Symbol::intern(feature))
279 } else {
280 None
281 }
282 })
283 .filter(|feature| features.contains(&feature))
284 .collect()
285 };
286
287 (f(true), f(false))
288}
289
290pub fn check_tied_features(
293 sess: &Session,
294 features: &FxHashMap<&str, bool>,
295) -> Option<&'static [&'static str]> {
296 if !features.is_empty() {
297 for tied in sess.target.tied_target_features() {
298 let mut tied_iter = tied.iter();
300 let enabled = features.get(tied_iter.next().unwrap());
301 if tied_iter.any(|f| enabled != features.get(f)) {
302 return Some(tied);
303 }
304 }
305 }
306 None
307}
308
309pub fn flag_to_backend_features<'a, const N: usize>(
317 sess: &'a Session,
318 diagnostics: bool,
319 to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
320 mut extend_backend_features: impl FnMut(&'a str, bool),
321) {
322 let known_features = sess.target.rust_target_features();
323
324 let mut rust_features = vec![];
326 parse_rust_feature_flag(
327 sess,
328 |feature| {
330 if diagnostics {
331 sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
332 }
333 },
334 |base_feature, new_features, enable| {
335 rust_features.extend(
336 UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
337 );
338 if diagnostics {
340 let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
341 match feature_state {
342 None => {
343 let rust_feature =
346 known_features.iter().find_map(|&(rust_feature, _, _)| {
347 let backend_features = to_backend_features(rust_feature);
348 if backend_features.contains(&base_feature)
349 && !backend_features.contains(&rust_feature)
350 {
351 Some(rust_feature)
352 } else {
353 None
354 }
355 });
356 let unknown_feature = if let Some(rust_feature) = rust_feature {
357 errors::UnknownCTargetFeature {
358 feature: base_feature,
359 rust_feature: errors::PossibleFeature::Some { rust_feature },
360 }
361 } else {
362 errors::UnknownCTargetFeature {
363 feature: base_feature,
364 rust_feature: errors::PossibleFeature::None,
365 }
366 };
367 sess.dcx().emit_warn(unknown_feature);
368 }
369 Some((_, stability, _)) => {
370 if let Err(reason) = stability.toggle_allowed() {
371 sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
372 feature: base_feature,
373 enabled: if enable { "enabled" } else { "disabled" },
374 reason,
375 });
376 } else if stability.requires_nightly().is_some() {
377 sess.dcx().emit_warn(errors::UnstableCTargetFeature {
381 feature: base_feature,
382 });
383 }
384 }
385 }
386 }
387 },
388 );
389
390 if diagnostics {
391 if let Some(f) = check_tied_features(
393 sess,
394 &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))),
395 ) {
396 sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
397 features: f,
398 span: None,
399 missing_features: None,
400 });
401 }
402 }
403
404 for (enable, feature) in rust_features {
406 extend_backend_features(feature, enable);
407 }
408}
409
410pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
413 let unstable_opts = &sess.opts.unstable_opts;
416 if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
417 features.push("+retpoline-indirect-branches".into());
418 features.push("+retpoline-indirect-calls".into());
419 }
420 if unstable_opts.retpoline_external_thunk {
424 features.push("+retpoline-external-thunk".into());
425 features.push("+retpoline-indirect-branches".into());
426 features.push("+retpoline-indirect-calls".into());
427 }
428}
429
430pub(crate) fn provide(providers: &mut Providers) {
431 *providers = Providers {
432 rust_target_features: |tcx, cnum| {
433 assert_eq!(cnum, LOCAL_CRATE);
434 if tcx.sess.opts.actually_rustdoc {
435 let mut result: UnordMap<String, Stability> = Default::default();
441 for (name, stability) in rustc_target::target_features::all_rust_features() {
442 use std::collections::hash_map::Entry;
443 match result.entry(name.to_owned()) {
444 Entry::Vacant(vacant_entry) => {
445 vacant_entry.insert(stability);
446 }
447 Entry::Occupied(mut occupied_entry) => {
448 match (occupied_entry.get(), stability) {
450 (Stability::Stable, _)
451 | (
452 Stability::Unstable { .. },
453 Stability::Unstable { .. } | Stability::Forbidden { .. },
454 )
455 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
456 }
459 _ => {
460 occupied_entry.insert(stability);
462 }
463 }
464 }
465 }
466 }
467 result
468 } else {
469 tcx.sess
470 .target
471 .rust_target_features()
472 .iter()
473 .map(|(a, b, _)| (a.to_string(), *b))
474 .collect()
475 }
476 },
477 implied_target_features: |tcx, feature: Symbol| {
478 let feature = feature.as_str();
479 UnordSet::from(tcx.sess.target.implied_target_features(feature))
480 .into_sorted_stable_ord()
481 .into_iter()
482 .map(|s| Symbol::intern(s))
483 .collect()
484 },
485 asm_target_features,
486 ..*providers
487 }
488}