1
1
use rustc_ast:: { ast, attr, MetaItemKind , NestedMetaItem } ;
2
2
use rustc_attr:: { list_contains_name, InlineAttr , InstructionSetAttr , OptimizeAttr } ;
3
+ use rustc_data_structures:: fx:: FxHashSet ;
3
4
use rustc_errors:: codes:: * ;
4
5
use rustc_errors:: { struct_span_code_err, DiagMessage , SubdiagMessage } ;
5
6
use rustc_hir as hir;
@@ -8,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
8
9
use rustc_hir:: weak_lang_items:: WEAK_LANG_ITEMS ;
9
10
use rustc_hir:: { lang_items, LangItem } ;
10
11
use rustc_middle:: middle:: codegen_fn_attrs:: {
11
- CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry ,
12
+ CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry , TargetFeature ,
12
13
} ;
13
14
use rustc_middle:: mir:: mono:: Linkage ;
14
15
use rustc_middle:: query:: Providers ;
@@ -17,6 +18,7 @@ use rustc_session::lint;
17
18
use rustc_session:: parse:: feature_err;
18
19
use rustc_span:: symbol:: Ident ;
19
20
use rustc_span:: { sym, Span } ;
21
+ use rustc_target:: abi:: VariantIdx ;
20
22
use rustc_target:: spec:: { abi, SanitizerSet } ;
21
23
22
24
use crate :: errors;
@@ -78,23 +80,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
78
80
let mut link_ordinal_span = None ;
79
81
let mut no_sanitize_span = None ;
80
82
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
+
81
90
for attr in attrs. iter ( ) {
82
91
// In some cases, attribute are only valid on functions, but it's the `check_attr`
83
92
// pass that check that they aren't used anywhere else, rather this module.
84
93
// In these cases, we bail from performing further checks that are only meaningful for
85
94
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
86
95
// report a delayed bug, just in case `check_attr` isn't doing its job.
87
96
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 ( ) {
94
99
tcx. dcx ( )
95
100
. span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
96
- None
97
101
}
102
+ sig
98
103
} ;
99
104
100
105
let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -613,7 +618,93 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613
618
}
614
619
}
615
620
616
- // If a function uses #[target_feature] it can't be inlined into general
621
+ 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 ;
635
+ }
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 ( ..) => ( ) ,
686
+ }
687
+ }
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 } ) ) ;
705
+ }
706
+
707
+ // If a function uses non-default target_features it can't be inlined into general
617
708
// purpose functions as they wouldn't have the right target features
618
709
// enabled. For that reason we also forbid #[inline(always)] as it can't be
619
710
// respected.
@@ -758,6 +849,20 @@ fn check_link_name_xor_ordinal(
758
849
}
759
850
}
760
851
852
+ fn struct_target_features ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> & [ TargetFeature ] {
853
+ let mut features = vec ! [ ] ;
854
+ let supported_features = tcx. supported_target_features ( LOCAL_CRATE ) ;
855
+ for attr in tcx. get_attrs ( def_id, sym:: target_feature) {
856
+ from_target_feature ( tcx, attr, supported_features, & mut features) ;
857
+ }
858
+ tcx. arena . alloc_slice ( & features)
859
+ }
860
+
761
861
pub fn provide ( providers : & mut Providers ) {
762
- * providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..* providers } ;
862
+ * providers = Providers {
863
+ codegen_fn_attrs,
864
+ should_inherit_track_caller,
865
+ struct_target_features,
866
+ ..* providers
867
+ } ;
763
868
}
0 commit comments