Skip to content

Commit 294ee31

Browse files
committed
Auto merge of rust-lang#129783 - veluca93:struct-tf-query, r=<try>
struct_target_features: cache feature computation. This commit moves the type-recursion to a query, causing it to be cached and (hopefully!) fixing the instruction-count regression from rust-lang#127537. r? compiler-errors Tracking issue: rust-lang#129107
2 parents 0d63418 + 047d34e commit 294ee31

File tree

2 files changed

+93
-81
lines changed

2 files changed

+93
-81
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+89-81
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_middle::middle::codegen_fn_attrs::{
1313
};
1414
use rustc_middle::mir::mono::Linkage;
1515
use rustc_middle::query::Providers;
16-
use rustc_middle::ty::{self as ty, TyCtxt};
16+
use rustc_middle::ty::{self as ty, Ty, TyCtxt};
1717
use rustc_session::lint;
1818
use rustc_session::parse::feature_err;
1919
use rustc_span::symbol::Ident;
@@ -619,89 +619,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
619619
}
620620

621621
if let Some(sig) = fn_sig_outer() {
622-
// Collect target features from types reachable from arguments.
623-
// We define a type as "reachable" if:
624-
// - it is a function argument
625-
// - it is a field of a reachable struct
626-
// - there is a reachable reference to it
627-
// FIXME(struct_target_features): we may want to cache the result of this computation.
628-
let mut visited_types = FxHashSet::default();
629-
let mut reachable_types: Vec<_> = sig.skip_binder().inputs().skip_binder().to_owned();
630-
let mut additional_tf = vec![];
631-
632-
while let Some(ty) = reachable_types.pop() {
633-
if visited_types.contains(&ty) {
634-
continue;
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+
);
635631
}
636-
visited_types.insert(ty);
637-
match ty.kind() {
638-
ty::Alias(..) => {
639-
if let Ok(t) =
640-
tcx.try_normalize_erasing_regions(tcx.param_env(did.to_def_id()), ty)
641-
{
642-
reachable_types.push(t)
643-
}
644-
}
645-
646-
ty::Ref(_, inner, _) => reachable_types.push(*inner),
647-
ty::Tuple(tys) => reachable_types.extend(tys.iter()),
648-
ty::Adt(adt_def, args) => {
649-
additional_tf.extend_from_slice(tcx.struct_target_features(adt_def.did()));
650-
// This only recurses into structs as i.e. an Option<TargetFeature> is an ADT
651-
// that doesn't actually always contain a TargetFeature.
652-
if adt_def.is_struct() {
653-
reachable_types.extend(
654-
adt_def
655-
.variant(VariantIdx::from_usize(0))
656-
.fields
657-
.iter()
658-
.map(|field| field.ty(tcx, args)),
659-
);
660-
}
661-
}
662-
ty::Bool
663-
| ty::Char
664-
| ty::Int(..)
665-
| ty::Uint(..)
666-
| ty::Float(..)
667-
| ty::Foreign(..)
668-
| ty::Str
669-
| ty::Array(..)
670-
| ty::Pat(..)
671-
| ty::Slice(..)
672-
| ty::RawPtr(..)
673-
| ty::FnDef(..)
674-
| ty::FnPtr(..)
675-
| ty::Dynamic(..)
676-
| ty::Closure(..)
677-
| ty::CoroutineClosure(..)
678-
| ty::Coroutine(..)
679-
| ty::CoroutineWitness(..)
680-
| ty::Never
681-
| ty::Param(..)
682-
| ty::Bound(..)
683-
| ty::Placeholder(..)
684-
| ty::Infer(..)
685-
| ty::Error(..) => (),
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+
);
686637
}
638+
codegen_fn_attrs
639+
.target_features
640+
.extend(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf }));
687641
}
688-
689-
// FIXME(struct_target_features): is this really necessary?
690-
if !additional_tf.is_empty() && sig.skip_binder().abi() != abi::Abi::Rust {
691-
tcx.dcx().span_err(
692-
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
693-
"cannot use a struct with target features in a function with non-Rust ABI",
694-
);
695-
}
696-
if !additional_tf.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always {
697-
tcx.dcx().span_err(
698-
tcx.hir().span(tcx.local_def_id_to_hir_id(did)),
699-
"cannot use a struct with target features in a #[inline(always)] function",
700-
);
701-
}
702-
codegen_fn_attrs
703-
.target_features
704-
.extend(additional_tf.iter().map(|tf| TargetFeature { implied: true, ..*tf }));
705642
}
706643

707644
// If a function uses non-default target_features it can't be inlined into general
@@ -858,11 +795,82 @@ fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeatur
858795
tcx.arena.alloc_slice(&features)
859796
}
860797

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+
861868
pub fn provide(providers: &mut Providers) {
862869
*providers = Providers {
863870
codegen_fn_attrs,
864871
should_inherit_track_caller,
865872
struct_target_features,
873+
struct_reachable_target_features,
866874
..*providers
867875
};
868876
}

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,10 @@ rustc_queries! {
12501250
desc { |tcx| "computing target features for struct `{}`", tcx.def_path_str(def_id) }
12511251
}
12521252

1253+
query struct_reachable_target_features(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx [TargetFeature] {
1254+
desc { |tcx| "computing target features reachable from {}", env.value }
1255+
}
1256+
12531257
query asm_target_features(def_id: DefId) -> &'tcx FxIndexSet<Symbol> {
12541258
desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) }
12551259
}

0 commit comments

Comments
 (0)