@@ -17,6 +17,8 @@ use crate::transform::MirPass;
1717use std:: iter;
1818use std:: ops:: { Range , RangeFrom } ;
1919
20+ crate mod cycle;
21+
2022const INSTR_COST : usize = 5 ;
2123const CALL_PENALTY : usize = 25 ;
2224const LANDINGPAD_PENALTY : usize = 50 ;
@@ -50,6 +52,8 @@ impl<'tcx> MirPass<'tcx> for Inline {
5052 return ;
5153 }
5254
55+ let span = trace_span ! ( "inline" , body = %tcx. def_path_str( body. source. def_id( ) ) ) ;
56+ let _guard = span. enter ( ) ;
5357 if inline ( tcx, body) {
5458 debug ! ( "running simplify cfg on {:?}" , body. source) ;
5559 CfgSimplifier :: new ( body) . simplify ( ) ;
@@ -90,8 +94,8 @@ struct Inliner<'tcx> {
9094 codegen_fn_attrs : & ' tcx CodegenFnAttrs ,
9195 /// Caller HirID.
9296 hir_id : hir:: HirId ,
93- /// Stack of inlined instances .
94- history : Vec < Instance < ' tcx > > ,
97+ /// Stack of inlined Instances .
98+ history : Vec < ty :: Instance < ' tcx > > ,
9599 /// Indicates that the caller body has been modified.
96100 changed : bool ,
97101}
@@ -103,13 +107,28 @@ impl Inliner<'tcx> {
103107 None => continue ,
104108 Some ( it) => it,
105109 } ;
110+ let span = trace_span ! ( "process_blocks" , %callsite. callee, ?bb) ;
111+ let _guard = span. enter ( ) ;
112+
113+ trace ! (
114+ "checking for self recursion ({:?} vs body_source: {:?})" ,
115+ callsite. callee. def_id( ) ,
116+ caller_body. source. def_id( )
117+ ) ;
118+ if callsite. callee . def_id ( ) == caller_body. source . def_id ( ) {
119+ debug ! ( "Not inlining a function into itself" ) ;
120+ continue ;
121+ }
106122
107- if !self . is_mir_available ( & callsite. callee , caller_body) {
123+ if !self . is_mir_available ( callsite. callee , caller_body) {
108124 debug ! ( "MIR unavailable {}" , callsite. callee) ;
109125 continue ;
110126 }
111127
128+ let span = trace_span ! ( "instance_mir" , %callsite. callee) ;
129+ let instance_mir_guard = span. enter ( ) ;
112130 let callee_body = self . tcx . instance_mir ( callsite. callee . def ) ;
131+ drop ( instance_mir_guard) ;
113132 if !self . should_inline ( callsite, callee_body) {
114133 continue ;
115134 }
@@ -137,28 +156,61 @@ impl Inliner<'tcx> {
137156 }
138157 }
139158
140- fn is_mir_available ( & self , callee : & Instance < ' tcx > , caller_body : & Body < ' tcx > ) -> bool {
141- if let InstanceDef :: Item ( _) = callee. def {
142- if !self . tcx . is_mir_available ( callee. def_id ( ) ) {
143- return false ;
159+ #[ instrument( skip( self , caller_body) ) ]
160+ fn is_mir_available ( & self , callee : Instance < ' tcx > , caller_body : & Body < ' tcx > ) -> bool {
161+ match callee. def {
162+ InstanceDef :: Item ( _) => {
163+ // If there is no MIR available (either because it was not in metadata or
164+ // because it has no MIR because it's an extern function), then the inliner
165+ // won't cause cycles on this.
166+ if !self . tcx . is_mir_available ( callee. def_id ( ) ) {
167+ return false ;
168+ }
144169 }
170+ // These have no own callable MIR.
171+ InstanceDef :: Intrinsic ( _) | InstanceDef :: Virtual ( ..) => return false ,
172+ // This cannot result in an immediate cycle since the callee MIR is a shim, which does
173+ // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we
174+ // do not need to catch this here, we can wait until the inliner decides to continue
175+ // inlining a second time.
176+ InstanceDef :: VtableShim ( _)
177+ | InstanceDef :: ReifyShim ( _)
178+ | InstanceDef :: FnPtrShim ( ..)
179+ | InstanceDef :: ClosureOnceShim { .. }
180+ | InstanceDef :: DropGlue ( ..)
181+ | InstanceDef :: CloneShim ( ..) => return true ,
182+ }
183+
184+ if self . tcx . is_constructor ( callee. def_id ( ) ) {
185+ trace ! ( "constructors always have MIR" ) ;
186+ // Constructor functions cannot cause a query cycle.
187+ return true ;
145188 }
146189
147190 if let Some ( callee_def_id) = callee. def_id ( ) . as_local ( ) {
148191 let callee_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( callee_def_id) ;
149- // Avoid a cycle here by only using `instance_mir` only if we have
150- // a lower `HirId` than the callee. This ensures that the callee will
151- // not inline us. This trick only works without incremental compilation.
152- // So don't do it if that is enabled. Also avoid inlining into generators,
192+ // Avoid inlining into generators,
153193 // since their `optimized_mir` is used for layout computation, which can
154194 // create a cycle, even when no attempt is made to inline the function
155195 // in the other direction.
156- !self . tcx . dep_graph . is_fully_enabled ( )
196+ caller_body. generator_kind . is_none ( )
197+ && (
198+ // Avoid a cycle here by only using `instance_mir` only if we have
199+ // a lower `HirId` than the callee. This ensures that the callee will
200+ // not inline us. This trick only works without incremental compilation.
201+ // So don't do it if that is enabled.
202+ !self . tcx . dep_graph . is_fully_enabled ( )
157203 && self . hir_id < callee_hir_id
158- && caller_body. generator_kind . is_none ( )
204+ // If we know for sure that the function we're calling will itself try to
205+ // call us, then we avoid inlining that function.
206+ || !self . tcx . mir_callgraph_reachable ( ( callee, caller_body. source . def_id ( ) . expect_local ( ) ) )
207+ )
159208 } else {
160- // This cannot result in a cycle since the callee MIR is from another crate
161- // and is already optimized.
209+ // This cannot result in an immediate cycle since the callee MIR is from another crate
210+ // and is already optimized. Any subsequent inlining may cause cycles, but we do
211+ // not need to catch this here, we can wait until the inliner decides to continue
212+ // inlining a second time.
213+ trace ! ( "functions from other crates always have MIR" ) ;
162214 true
163215 }
164216 }
@@ -203,8 +255,8 @@ impl Inliner<'tcx> {
203255 None
204256 }
205257
258+ #[ instrument( skip( self , callee_body) ) ]
206259 fn should_inline ( & self , callsite : CallSite < ' tcx > , callee_body : & Body < ' tcx > ) -> bool {
207- debug ! ( "should_inline({:?})" , callsite) ;
208260 let tcx = self . tcx ;
209261
210262 if callsite. fn_sig . c_variadic ( ) {
@@ -333,7 +385,9 @@ impl Inliner<'tcx> {
333385 if let Ok ( Some ( instance) ) =
334386 Instance :: resolve ( self . tcx , self . param_env , def_id, substs)
335387 {
336- if callsite. callee == instance || self . history . contains ( & instance) {
388+ if callsite. callee . def_id ( ) == instance. def_id ( )
389+ || self . history . contains ( & instance)
390+ {
337391 debug ! ( "`callee is recursive - not inlining" ) ;
338392 return false ;
339393 }
0 commit comments