Skip to content

Commit 177eb79

Browse files
committed
Implement struct_target_features for non-generic functions.
1 parent f559d61 commit 177eb79

File tree

27 files changed

+632
-38
lines changed

27 files changed

+632
-38
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+89-18
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
use rustc_ast::{MetaItemKind, NestedMetaItem, ast, attr};
2-
use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name};
1+
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
2+
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
33
use rustc_errors::codes::*;
4-
use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
4+
use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage};
55
use rustc_hir as hir;
66
use rustc_hir::def::DefKind;
7-
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
7+
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
88
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
9-
use rustc_hir::{LangItem, lang_items};
9+
use rustc_hir::{lang_items, LangItem};
1010
use rustc_middle::middle::codegen_fn_attrs::{
11-
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
11+
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, TargetFeature,
1212
};
1313
use rustc_middle::mir::mono::Linkage;
1414
use rustc_middle::query::Providers;
15-
use rustc_middle::ty::{self as ty, TyCtxt};
15+
use rustc_middle::ty::{self as ty, Ty, TyCtxt};
1616
use rustc_session::lint;
1717
use rustc_session::parse::feature_err;
1818
use rustc_span::symbol::Ident;
19-
use rustc_span::{Span, sym};
20-
use rustc_target::spec::{SanitizerSet, abi};
19+
use rustc_span::{sym, Span};
20+
use rustc_target::spec::{abi, SanitizerSet};
2121

2222
use crate::errors;
2323
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
@@ -78,23 +78,30 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7878
let mut link_ordinal_span = None;
7979
let mut no_sanitize_span = None;
8080

81+
let fn_sig_outer = || {
82+
use DefKind::*;
83+
84+
let def_kind = tcx.def_kind(did);
85+
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
86+
Some(tcx.fn_sig(did))
87+
} else {
88+
None
89+
}
90+
};
91+
8192
for attr in attrs.iter() {
8293
// In some cases, attribute are only valid on functions, but it's the `check_attr`
8394
// pass that check that they aren't used anywhere else, rather this module.
8495
// In these cases, we bail from performing further checks that are only meaningful for
8596
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
8697
// report a delayed bug, just in case `check_attr` isn't doing its job.
8798
let fn_sig = || {
88-
use DefKind::*;
89-
90-
let def_kind = tcx.def_kind(did);
91-
if let Fn | AssocFn | Variant | Ctor(..) = def_kind {
92-
Some(tcx.fn_sig(did))
93-
} else {
99+
let sig = fn_sig_outer();
100+
if sig.is_none() {
94101
tcx.dcx()
95102
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
96-
None
97103
}
104+
sig
98105
};
99106

100107
let Some(Ident { name, .. }) = attr.ident() else {
@@ -595,7 +602,30 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
595602
}
596603
}
597604

598-
// If a function uses #[target_feature] it can't be inlined into general
605+
if let Some(sig) = fn_sig_outer() {
606+
for ty in sig.skip_binder().inputs().skip_binder() {
607+
let additional_tf =
608+
tcx.struct_reachable_target_features(tcx.param_env(did.to_def_id()).and(*ty));
609+
// FIXME(struct_target_features): is this really necessary?
610+
if !additional_tf.is_empty() && sig.skip_binder().abi() != abi::Abi::Rust {
611+
tcx.dcx().span_err(
612+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
613+
"cannot use a struct with target features in a function with non-Rust ABI",
614+
);
615+
}
616+
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
617+
tcx.dcx().span_err(
618+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
619+
"cannot use a struct with target features in a #[inline(always)] function",
620+
);
621+
}
622+
codegen_fn_attrs
623+
.target_features
624+
.extend(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf }));
625+
}
626+
}
627+
628+
// If a function uses non-default target_features it can't be inlined into general
599629
// purpose functions as they wouldn't have the right target features
600630
// enabled. For that reason we also forbid #[inline(always)] as it can't be
601631
// respected.
@@ -738,6 +768,47 @@ fn check_link_name_xor_ordinal(
738768
}
739769
}
740770

771+
fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeature] {
772+
let mut features = vec![];
773+
let supported_features = tcx.supported_target_features(LOCAL_CRATE);
774+
for attr in tcx.get_attrs(def_id, sym::target_feature) {
775+
from_target_feature(tcx, attr, supported_features, &mut features);
776+
}
777+
tcx.arena.alloc_slice(&features)
778+
}
779+
780+
fn struct_reachable_target_features<'tcx>(
781+
tcx: TyCtxt<'tcx>,
782+
env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
783+
) -> &'tcx [TargetFeature] {
784+
// Collect target features from types reachable from `env.value` by dereferencing a certain
785+
// number of references and resolving aliases.
786+
787+
let mut ty = env.value;
788+
if matches!(ty.kind(), ty::Alias(..)) {
789+
ty = match tcx.try_normalize_erasing_regions(env.param_env, ty) {
790+
Ok(ty) => ty,
791+
Err(_) => return tcx.arena.alloc_slice(&[]),
792+
};
793+
}
794+
while let ty::Ref(_, inner, _) = ty.kind() {
795+
ty = *inner;
796+
}
797+
798+
let tf = if let ty::Adt(adt_def, ..) = ty.kind() {
799+
tcx.struct_target_features(adt_def.did())
800+
} else {
801+
&[]
802+
};
803+
tcx.arena.alloc_slice(tf)
804+
}
805+
741806
pub(crate) fn provide(providers: &mut Providers) {
742-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
807+
*providers = Providers {
808+
codegen_fn_attrs,
809+
should_inherit_track_caller,
810+
struct_target_features,
811+
struct_reachable_target_features,
812+
..*providers
813+
};
743814
}

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,8 @@ declare_features! (
598598
(unstable, strict_provenance, "1.61.0", Some(95228)),
599599
/// Allows string patterns to dereference values to match them.
600600
(unstable, string_deref_patterns, "1.67.0", Some(87121)),
601+
/// Allows structs to carry target_feature information.
602+
(incomplete, struct_target_features, "CURRENT_RUSTC_VERSION", Some(129107)),
601603
/// Allows the use of `#[target_feature]` on safe functions.
602604
(unstable, target_feature_11, "1.45.0", Some(69098)),
603605
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_hir/src/def.rs

+37
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,43 @@ impl DefKind {
329329
| DefKind::ExternCrate => false,
330330
}
331331
}
332+
333+
/// Whether `query struct_target_features` should be used with this definition.
334+
pub fn has_struct_target_features(self) -> bool {
335+
match self {
336+
DefKind::Struct => true,
337+
DefKind::Fn
338+
| DefKind::Union
339+
| DefKind::Enum
340+
| DefKind::AssocFn
341+
| DefKind::Ctor(..)
342+
| DefKind::Closure
343+
| DefKind::Static { .. }
344+
| DefKind::Mod
345+
| DefKind::Variant
346+
| DefKind::Trait
347+
| DefKind::TyAlias
348+
| DefKind::ForeignTy
349+
| DefKind::TraitAlias
350+
| DefKind::AssocTy
351+
| DefKind::Const
352+
| DefKind::AssocConst
353+
| DefKind::Macro(..)
354+
| DefKind::Use
355+
| DefKind::ForeignMod
356+
| DefKind::OpaqueTy
357+
| DefKind::Impl { .. }
358+
| DefKind::Field
359+
| DefKind::TyParam
360+
| DefKind::ConstParam
361+
| DefKind::LifetimeParam
362+
| DefKind::AnonConst
363+
| DefKind::InlineConst
364+
| DefKind::SyntheticCoroutineBody
365+
| DefKind::GlobalAsm
366+
| DefKind::ExternCrate => false,
367+
}
368+
}
332369
}
333370

334371
/// The resolution of a path or export.

compiler/rustc_hir_typeck/src/coercion.rs

+2
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
910910
}
911911

912912
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
913+
// FIXME(struct_target_features): should this be true also for functions that inherit
914+
// target features from structs?
913915

914916
if b_hdr.safety == hir::Safety::Safe
915917
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ provide! { tcx, def_id, other, cdata,
254254
variances_of => { table }
255255
fn_sig => { table }
256256
codegen_fn_attrs => { table }
257+
struct_target_features => { table_defaulted_array }
257258
impl_trait_header => { table }
258259
const_param_default => { table }
259260
object_lifetime_default => { table }

compiler/rustc_metadata/src/rmeta/encoder.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
13981398
if def_kind.has_codegen_attrs() {
13991399
record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id));
14001400
}
1401+
if def_kind.has_struct_target_features() {
1402+
record_defaulted_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id));
1403+
}
14011404
if should_encode_visibility(def_kind) {
14021405
let vis =
14031406
self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index);

compiler/rustc_metadata/src/rmeta/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_macros::{
1919
Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable,
2020
};
2121
use rustc_middle::metadata::ModChild;
22-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
22+
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
2323
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
2424
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
2525
use rustc_middle::middle::lib_features::FeatureStability;
@@ -404,6 +404,7 @@ define_tables! {
404404
// individually instead of `DefId`s.
405405
module_children_reexports: Table<DefIndex, LazyArray<ModChild>>,
406406
cross_crate_inlinable: Table<DefIndex, bool>,
407+
struct_target_features: Table<DefIndex, LazyArray<TargetFeature>>,
407408

408409
- optional:
409410
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ pub struct CodegenFnAttrs {
2626
/// be set when `link_name` is set. This is for foreign items with the
2727
/// "raw-dylib" kind.
2828
pub link_ordinal: Option<u16>,
29-
/// The `#[target_feature(enable = "...")]` attribute and the enabled
30-
/// features (only enabled features are supported right now).
29+
/// All the target features that are enabled for this function. Some features might be enabled
30+
/// implicitly.
3131
pub target_features: Vec<TargetFeature>,
3232
/// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found.
3333
pub linkage: Option<Linkage>,
@@ -55,8 +55,8 @@ pub struct CodegenFnAttrs {
5555
pub struct TargetFeature {
5656
/// The name of the target feature (e.g. "avx")
5757
pub name: Symbol,
58-
/// The feature is implied by another feature, rather than explicitly added by the
59-
/// `#[target_feature]` attribute
58+
/// The feature is implied by another feature or by an argument, rather than explicitly
59+
/// added by the `#[target_feature]` attribute
6060
pub implied: bool,
6161
}
6262

compiler/rustc_middle/src/query/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
4848
use crate::infer::canonical::{self, Canonical};
4949
use crate::lint::LintExpectation;
5050
use crate::metadata::ModChild;
51-
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
51+
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
5252
use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
5353
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
5454
use crate::middle::lib_features::LibFeatures;
@@ -1256,6 +1256,15 @@ rustc_queries! {
12561256
feedable
12571257
}
12581258

1259+
query struct_target_features(def_id: DefId) -> &'tcx [TargetFeature] {
1260+
separate_provide_extern
1261+
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
1262+
}
1263+
1264+
query struct_reachable_target_features(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx [TargetFeature] {
1265+
desc { |tcx| "computing target features reachable from {}", env.value }
1266+
}
1267+
12591268
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
12601269
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
12611270
}

compiler/rustc_middle/src/ty/parameterized.rs

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ trivially_parameterized_over_tcx! {
5959
std::string::String,
6060
crate::metadata::ModChild,
6161
crate::middle::codegen_fn_attrs::CodegenFnAttrs,
62+
crate::middle::codegen_fn_attrs::TargetFeature,
6263
crate::middle::debugger_visualizer::DebuggerVisualizerFile,
6364
crate::middle::exported_symbols::SymbolExportInfo,
6465
crate::middle::lib_features::FeatureStability,

compiler/rustc_mir_build/messages.ftl

+49-3
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,56 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
116116
mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
117117
118118
mir_build_initializing_type_with_requires_unsafe =
119-
initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block
120-
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
121-
.label = initializing type with `rustc_layout_scalar_valid_range` attr
119+
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
120+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
121+
[1] feature
122+
*[count] features
123+
}: {$missing_target_features}
124+
.note = the {$build_target_features} target {$build_target_features_count ->
125+
[1] feature
126+
*[count] features
127+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
128+
[1] it
129+
*[count] them
130+
} in `#[target_feature]`
131+
.label = call to function with `#[target_feature]`
122132
123133
mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
124134
initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block
125135
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
126136
.label = initializing type with `rustc_layout_scalar_valid_range` attr
127137
138+
mir_build_initializing_type_with_target_feature_requires_unsafe =
139+
initializing type `{$adt}` with `#[target_feature]` is unsafe and requires unsafe block
140+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
141+
[1] feature
142+
*[count] features
143+
}: {$missing_target_features}
144+
.note = the {$build_target_features} target {$build_target_features_count ->
145+
[1] feature
146+
*[count] features
147+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
148+
[1] it
149+
*[count] them
150+
} in `#[target_feature]`
151+
.label = call to function with `#[target_feature]`
152+
153+
mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
154+
initializing type `{$adt}` with `#[target_feature]` is unsafe and requires unsafe function or block
155+
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
156+
[1] feature
157+
*[count] features
158+
}: {$missing_target_features}
159+
.note = the {$build_target_features} target {$build_target_features_count ->
160+
[1] feature
161+
*[count] features
162+
} being enabled in the build configuration does not remove the requirement to list {$build_target_features_count ->
163+
[1] it
164+
*[count] them
165+
} in `#[target_feature]`
166+
.label = call to function with `#[target_feature]`
167+
168+
128169
mir_build_inline_assembly_requires_unsafe =
129170
use of inline assembly is unsafe and requires unsafe block
130171
.note = inline assembly is entirely unchecked and can cause undefined behavior
@@ -388,6 +429,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
388429
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
389430
.label = initializing type with `rustc_layout_scalar_valid_range` attr
390431
432+
mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe =
433+
initializing type with `target_feature` attr is unsafe and requires unsafe block
434+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
435+
.label = initializing type with `target_feature` attr
436+
391437
mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
392438
use of inline assembly is unsafe and requires unsafe block
393439
.note = inline assembly is entirely unchecked and can cause undefined behavior

0 commit comments

Comments
 (0)