@@ -3,6 +3,7 @@ use crate::pass_manager as pm;
33use rustc_attr:: InlineAttr ;
44use rustc_hir:: def:: DefKind ;
55use rustc_hir:: def_id:: LocalDefId ;
6+ use rustc_hir:: LangItem ;
67use rustc_middle:: mir:: visit:: Visitor ;
78use rustc_middle:: mir:: * ;
89use rustc_middle:: query:: Providers ;
@@ -16,24 +17,34 @@ pub fn provide(providers: &mut Providers) {
1617}
1718
1819fn cross_crate_inlinable ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> bool {
20+ // Bail quickly for DefIds that wouldn't be inlinable anyway, such as statics.
21+ if !matches ! (
22+ tcx. def_kind( def_id) ,
23+ DefKind :: Fn | DefKind :: AssocFn | DefKind :: Closure | DefKind :: Ctor ( ..)
24+ ) {
25+ return false ;
26+ }
27+
28+ // The program entrypoint is never inlinable in any sense.
29+ if let Some ( ( entry_fn, _) ) = tcx. entry_fn ( ( ) ) {
30+ if def_id == entry_fn. expect_local ( ) {
31+ return false ;
32+ }
33+ }
34+
1935 let codegen_fn_attrs = tcx. codegen_fn_attrs ( def_id) ;
2036 // If this has an extern indicator, then this function is globally shared and thus will not
2137 // generate cgu-internal copies which would make it cross-crate inlinable.
2238 if codegen_fn_attrs. contains_extern_indicator ( ) {
2339 return false ;
2440 }
2541
26- // This just reproduces the logic from Instance::requires_inline.
27- match tcx. def_kind ( def_id) {
28- DefKind :: Ctor ( ..) | DefKind :: Closure => return true ,
29- DefKind :: Fn | DefKind :: AssocFn => { }
30- _ => return false ,
31- }
32-
33- // From this point on, it is valid to return true or false.
34- if tcx. sess . opts . unstable_opts . cross_crate_inline_threshold == InliningThreshold :: Always {
35- return true ;
36- }
42+ // From this point on, it is technically valid to return true or false.
43+ let threshold = match tcx. sess . opts . unstable_opts . cross_crate_inline_threshold {
44+ InliningThreshold :: Always => return true ,
45+ InliningThreshold :: Sometimes ( threshold) => threshold,
46+ InliningThreshold :: Never => return false ,
47+ } ;
3748
3849 if tcx. has_attr ( def_id, sym:: rustc_intrinsic) {
3950 // Intrinsic fallback bodies are always cross-crate inlineable.
@@ -43,42 +54,55 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4354 return true ;
4455 }
4556
57+ // Don't do any inference when incremental compilation is enabled; the additional inlining that
58+ // inference permits also creates more work for small edits.
59+ if tcx. sess . opts . incremental . is_some ( ) {
60+ return false ;
61+ }
62+
4663 // Obey source annotations first; this is important because it means we can use
4764 // #[inline(never)] to force code generation.
4865 match codegen_fn_attrs. inline {
4966 InlineAttr :: Never => return false ,
5067 InlineAttr :: Hint | InlineAttr :: Always => return true ,
51- _ => { }
52- }
53-
54- // Don't do any inference when incremental compilation is enabled; the additional inlining that
55- // inference permits also creates more work for small edits.
56- if tcx. sess . opts . incremental . is_some ( ) {
57- return false ;
68+ InlineAttr :: None => { }
5869 }
5970
6071 // Don't do any inference if codegen optimizations are disabled and also MIR inlining is not
6172 // enabled. This ensures that we do inference even if someone only passes -Zinline-mir,
6273 // which is less confusing than having to also enable -Copt-level=1.
6374 if matches ! ( tcx. sess. opts. optimize, OptLevel :: No ) && !pm:: should_run_pass ( tcx, & inline:: Inline )
6475 {
65- return false ;
76+ if !matches ! ( tcx. def_kind( def_id) , DefKind :: Ctor ( ..) ) {
77+ return false ;
78+ }
6679 }
6780
68- if !tcx. is_mir_available ( def_id) {
69- return false ;
81+ if tcx. is_lang_item ( def_id. into ( ) , LangItem :: DropInPlace )
82+ || tcx. is_lang_item ( def_id. into ( ) , LangItem :: AsyncDropInPlace )
83+ {
84+ return true ;
7085 }
7186
72- let threshold = match tcx. sess . opts . unstable_opts . cross_crate_inline_threshold {
73- InliningThreshold :: Always => return true ,
74- InliningThreshold :: Sometimes ( threshold) => threshold,
75- InliningThreshold :: Never => return false ,
76- } ;
87+ // If there is no MIR for the DefId, we can't analyze the body. But also, this only arises in
88+ // two relevant cases: extern functions and MIR shims. So here we recognize the MIR shims by a
89+ // DefId that has no MIR and whose parent is one of the shimmed traits.
90+ // Everything else is extern functions, and thus not a candidate for inlining.
91+ if !tcx. is_mir_available ( def_id) {
92+ let parent = tcx. parent ( def_id. into ( ) ) ;
93+ match tcx. lang_items ( ) . from_def_id ( parent. into ( ) ) {
94+ Some ( LangItem :: Clone | LangItem :: FnOnce | LangItem :: Fn | LangItem :: FnMut ) => {
95+ return true ;
96+ }
97+ _ => return false ,
98+ }
99+ }
77100
78101 let mir = tcx. optimized_mir ( def_id) ;
79102 let mut checker =
80103 CostChecker { tcx, callee_body : mir, calls : 0 , statements : 0 , landing_pads : 0 , resumes : 0 } ;
81104 checker. visit_body ( mir) ;
105+
82106 checker. calls == 0
83107 && checker. resumes == 0
84108 && checker. landing_pads == 0
0 commit comments