@@ -17,6 +17,8 @@ use crate::transform::MirPass;
17
17
use std:: iter;
18
18
use std:: ops:: { Range , RangeFrom } ;
19
19
20
+ crate mod cycle;
21
+
20
22
const INSTR_COST : usize = 5 ;
21
23
const CALL_PENALTY : usize = 25 ;
22
24
const LANDINGPAD_PENALTY : usize = 50 ;
@@ -50,6 +52,8 @@ impl<'tcx> MirPass<'tcx> for Inline {
50
52
return ;
51
53
}
52
54
55
+ let span = trace_span ! ( "inline" , body = %tcx. def_path_str( body. source. def_id( ) ) ) ;
56
+ let _guard = span. enter ( ) ;
53
57
if inline ( tcx, body) {
54
58
debug ! ( "running simplify cfg on {:?}" , body. source) ;
55
59
CfgSimplifier :: new ( body) . simplify ( ) ;
@@ -90,8 +94,8 @@ struct Inliner<'tcx> {
90
94
codegen_fn_attrs : & ' tcx CodegenFnAttrs ,
91
95
/// Caller HirID.
92
96
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 > > ,
95
99
/// Indicates that the caller body has been modified.
96
100
changed : bool ,
97
101
}
@@ -103,13 +107,28 @@ impl Inliner<'tcx> {
103
107
None => continue ,
104
108
Some ( it) => it,
105
109
} ;
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
+ }
106
122
107
- if !self . is_mir_available ( & callsite. callee , caller_body) {
123
+ if !self . is_mir_available ( callsite. callee , caller_body) {
108
124
debug ! ( "MIR unavailable {}" , callsite. callee) ;
109
125
continue ;
110
126
}
111
127
128
+ let span = trace_span ! ( "instance_mir" , %callsite. callee) ;
129
+ let instance_mir_guard = span. enter ( ) ;
112
130
let callee_body = self . tcx . instance_mir ( callsite. callee . def ) ;
131
+ drop ( instance_mir_guard) ;
113
132
if !self . should_inline ( callsite, callee_body) {
114
133
continue ;
115
134
}
@@ -137,28 +156,61 @@ impl Inliner<'tcx> {
137
156
}
138
157
}
139
158
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
+ }
144
169
}
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 ;
145
188
}
146
189
147
190
if let Some ( callee_def_id) = callee. def_id ( ) . as_local ( ) {
148
191
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,
153
193
// since their `optimized_mir` is used for layout computation, which can
154
194
// create a cycle, even when no attempt is made to inline the function
155
195
// 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 ( )
157
203
&& 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
+ )
159
208
} 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" ) ;
162
214
true
163
215
}
164
216
}
@@ -203,8 +255,8 @@ impl Inliner<'tcx> {
203
255
None
204
256
}
205
257
258
+ #[ instrument( skip( self , callee_body) ) ]
206
259
fn should_inline ( & self , callsite : CallSite < ' tcx > , callee_body : & Body < ' tcx > ) -> bool {
207
- debug ! ( "should_inline({:?})" , callsite) ;
208
260
let tcx = self . tcx ;
209
261
210
262
if callsite. fn_sig . c_variadic ( ) {
@@ -333,7 +385,9 @@ impl Inliner<'tcx> {
333
385
if let Ok ( Some ( instance) ) =
334
386
Instance :: resolve ( self . tcx , self . param_env , def_id, substs)
335
387
{
336
- if callsite. callee == instance || self . history . contains ( & instance) {
388
+ if callsite. callee . def_id ( ) == instance. def_id ( )
389
+ || self . history . contains ( & instance)
390
+ {
337
391
debug ! ( "`callee is recursive - not inlining" ) ;
338
392
return false ;
339
393
}
0 commit comments