Skip to content

Commit 15965d5

Browse files
committed
Auto merge of #129881 - veluca93:struct_tf, r=<try>
Implement struct_target_features for non-generic functions. This PR implements a first version of RFC 3525. In particular, the current code does not handle structs with target features being passed to generic functions correctly. This is a roll-up of #129764, #129783 and #129764, which will hopefully result in a PR that does not introduce perf regressions in the first place. r? Kobzol Tracking issue: #129107
2 parents 9b82580 + 8bc1c85 commit 15965d5

File tree

27 files changed

+555
-28
lines changed

27 files changed

+555
-28
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+124-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
22
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
3+
use rustc_data_structures::fx::FxHashSet;
34
use rustc_errors::codes::*;
45
use rustc_errors::{struct_span_code_err, DiagMessage, SubdiagMessage};
56
use rustc_hir as hir;
@@ -8,15 +9,16 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
89
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
910
use rustc_hir::{lang_items, LangItem};
1011
use rustc_middle::middle::codegen_fn_attrs::{
11-
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
12+
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, TargetFeature,
1213
};
1314
use rustc_middle::mir::mono::Linkage;
1415
use rustc_middle::query::Providers;
15-
use rustc_middle::ty::{self as ty, TyCtxt};
16+
use rustc_middle::ty::{self as ty, Ty, TyCtxt};
1617
use rustc_session::lint;
1718
use rustc_session::parse::feature_err;
1819
use rustc_span::symbol::Ident;
1920
use rustc_span::{sym, Span};
21+
use rustc_target::abi::VariantIdx;
2022
use rustc_target::spec::{abi, SanitizerSet};
2123

2224
use crate::errors;
@@ -78,23 +80,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7880
let mut link_ordinal_span = None;
7981
let mut no_sanitize_span = None;
8082

83+
let fn_sig_outer = || {
84+
use DefKind::*;
85+
86+
let def_kind = tcx.def_kind(did);
87+
if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { None }
88+
};
89+
8190
for attr in attrs.iter() {
8291
// In some cases, attribute are only valid on functions, but it's the `check_attr`
8392
// pass that check that they aren't used anywhere else, rather this module.
8493
// In these cases, we bail from performing further checks that are only meaningful for
8594
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
8695
// report a delayed bug, just in case `check_attr` isn't doing its job.
8796
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 {
97+
let sig = fn_sig_outer();
98+
if sig.is_none() {
9499
tcx.dcx()
95100
.span_delayed_bug(attr.span, "this attribute can only be applied to functions");
96-
None
97101
}
102+
sig
98103
};
99104

100105
let Some(Ident { name, .. }) = attr.ident() else {
@@ -613,7 +618,30 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613618
}
614619
}
615620

616-
// If a function uses #[target_feature] it can't be inlined into general
621+
if let Some(sig) = fn_sig_outer() {
622+
for ty in sig.skip_binder().inputs().skip_binder() {
623+
let additional_tf =
624+
tcx.struct_reachable_target_features(tcx.param_env(did.to_def_id()).and(*ty));
625+
// FIXME(struct_target_features): is this really necessary?
626+
if !additional_tf.is_empty() && sig.skip_binder().abi() != abi::Abi::Rust {
627+
tcx.dcx().span_err(
628+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
629+
"cannot use a struct with target features in a function with non-Rust ABI",
630+
);
631+
}
632+
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
633+
tcx.dcx().span_err(
634+
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
635+
"cannot use a struct with target features in a #[inline(always)] function",
636+
);
637+
}
638+
codegen_fn_attrs
639+
.target_features
640+
.extend(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf }));
641+
}
642+
}
643+
644+
// If a function uses non-default target_features it can't be inlined into general
617645
// purpose functions as they wouldn't have the right target features
618646
// enabled. For that reason we also forbid #[inline(always)] as it can't be
619647
// respected.
@@ -758,6 +786,91 @@ fn check_link_name_xor_ordinal(
758786
}
759787
}
760788

789+
fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeature] {
790+
let mut features = vec![];
791+
let supported_features = tcx.supported_target_features(LOCAL_CRATE);
792+
for attr in tcx.get_attrs(def_id, sym::target_feature) {
793+
from_target_feature(tcx, attr, supported_features, &mut features);
794+
}
795+
tcx.arena.alloc_slice(&features)
796+
}
797+
798+
fn struct_reachable_target_features<'tcx>(
799+
tcx: TyCtxt<'tcx>,
800+
env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
801+
) -> &'tcx [TargetFeature] {
802+
// Collect target features from types reachable from `env.value`.
803+
// We define a type as "reachable" if:
804+
// - it is a function argument
805+
// - it is a field of a reachable struct
806+
// - there is a reachable reference to it
807+
let mut visited_types = FxHashSet::default();
808+
let mut reachable_types = vec![env.value];
809+
let mut reachable_tf = vec![];
810+
811+
while let Some(ty) = reachable_types.pop() {
812+
if visited_types.contains(&ty) {
813+
continue;
814+
}
815+
visited_types.insert(ty);
816+
match ty.kind() {
817+
ty::Alias(..) => {
818+
if let Ok(t) = tcx.try_normalize_erasing_regions(env.param_env, ty) {
819+
reachable_types.push(t)
820+
}
821+
}
822+
823+
ty::Ref(_, inner, _) => reachable_types.push(*inner),
824+
ty::Tuple(tys) => reachable_types.extend(tys.iter()),
825+
ty::Adt(adt_def, args) => {
826+
reachable_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
827+
// This only recurses into structs as i.e. an Option<TargetFeature> is an ADT
828+
// that doesn't actually always contain a TargetFeature.
829+
if adt_def.is_struct() {
830+
reachable_types.extend(
831+
adt_def
832+
.variant(VariantIdx::from_usize(0))
833+
.fields
834+
.iter()
835+
.map(|field| field.ty(tcx, args)),
836+
);
837+
}
838+
}
839+
ty::Bool
840+
| ty::Char
841+
| ty::Int(..)
842+
| ty::Uint(..)
843+
| ty::Float(..)
844+
| ty::Foreign(..)
845+
| ty::Str
846+
| ty::Array(..)
847+
| ty::Pat(..)
848+
| ty::Slice(..)
849+
| ty::RawPtr(..)
850+
| ty::FnDef(..)
851+
| ty::FnPtr(..)
852+
| ty::Dynamic(..)
853+
| ty::Closure(..)
854+
| ty::CoroutineClosure(..)
855+
| ty::Coroutine(..)
856+
| ty::CoroutineWitness(..)
857+
| ty::Never
858+
| ty::Param(..)
859+
| ty::Bound(..)
860+
| ty::Placeholder(..)
861+
| ty::Infer(..)
862+
| ty::Error(..) => (),
863+
}
864+
}
865+
tcx.arena.alloc_slice(&reachable_tf)
866+
}
867+
761868
pub fn provide(providers: &mut Providers) {
762-
*providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers };
869+
*providers = Providers {
870+
codegen_fn_attrs,
871+
should_inherit_track_caller,
872+
struct_target_features,
873+
struct_reachable_target_features,
874+
..*providers
875+
};
763876
}

compiler/rustc_feature/src/unstable.rs

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

compiler/rustc_hir/src/def.rs

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

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

compiler/rustc_hir_typeck/src/coercion.rs

+2
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
851851
}
852852

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

855857
if b_hdr.safety == hir::Safety::Safe
856858
&& !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
@@ -1392,6 +1392,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
13921392
if def_kind.has_codegen_attrs() {
13931393
record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id));
13941394
}
1395+
if def_kind.has_struct_target_features() {
1396+
record_defaulted_array!(self.tables.struct_target_features[def_id] <- self.tcx.struct_target_features(def_id));
1397+
}
13951398
if should_encode_visibility(def_kind) {
13961399
let vis =
13971400
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;
@@ -402,6 +402,7 @@ define_tables! {
402402
// individually instead of `DefId`s.
403403
module_children_reexports: Table<DefIndex, LazyArray<ModChild>>,
404404
cross_crate_inlinable: Table<DefIndex, bool>,
405+
struct_target_features: Table<DefIndex, LazyArray<TargetFeature>>,
405406

406407
- optional:
407408
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
@@ -47,7 +47,7 @@ use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
4747
use crate::infer::canonical::{self, Canonical};
4848
use crate::lint::LintExpectation;
4949
use crate::metadata::ModChild;
50-
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
50+
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
5151
use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
5252
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
5353
use crate::middle::lib_features::LibFeatures;
@@ -1249,6 +1249,15 @@ rustc_queries! {
12491249
feedable
12501250
}
12511251

1252+
query struct_target_features(def_id: DefId) -> &'tcx [TargetFeature] {
1253+
separate_provide_extern
1254+
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
1255+
}
1256+
1257+
query struct_reachable_target_features(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx [TargetFeature] {
1258+
desc { |tcx| "computing target features reachable from {}", env.value }
1259+
}
1260+
12521261
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
12531262
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
12541263
}

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

+16
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed
125125
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
126126
.label = initializing type with `rustc_layout_scalar_valid_range` attr
127127
128+
mir_build_initializing_type_with_target_feature_requires_unsafe =
129+
initializing type with `target_feature` attr is unsafe and requires unsafe block
130+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
131+
.label = initializing type with `target_feature` attr
132+
133+
mir_build_initializing_type_with_target_feature_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
134+
initializing type with `target_feature` attr is unsafe and requires unsafe function or block
135+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
136+
.label = initializing type with `target_feature` attr
137+
138+
128139
mir_build_inline_assembly_requires_unsafe =
129140
use of inline assembly is unsafe and requires unsafe block
130141
.note = inline assembly is entirely unchecked and can cause undefined behavior
@@ -387,6 +398,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
387398
.note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
388399
.label = initializing type with `rustc_layout_scalar_valid_range` attr
389400
401+
mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_target_feature_requires_unsafe =
402+
initializing type with `target_feature` attr is unsafe and requires unsafe block
403+
.note = this struct can only be constructed if the corresponding `target_feature`s are available
404+
.label = initializing type with `target_feature` attr
405+
390406
mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
391407
use of inline assembly is unsafe and requires unsafe block
392408
.note = inline assembly is entirely unchecked and can cause undefined behavior

0 commit comments

Comments
 (0)