1
1
//! Inlining pass for MIR functions
2
2
use crate :: deref_separator:: deref_finder;
3
3
use rustc_attr:: InlineAttr ;
4
+ use rustc_const_eval:: transform:: validate:: equal_up_to_regions;
4
5
use rustc_index:: bit_set:: BitSet ;
5
6
use rustc_index:: vec:: Idx ;
6
7
use rustc_middle:: middle:: codegen_fn_attrs:: { CodegenFnAttrFlags , CodegenFnAttrs } ;
7
8
use rustc_middle:: mir:: visit:: * ;
8
9
use rustc_middle:: mir:: * ;
9
- use rustc_middle:: traits:: ObligationCause ;
10
10
use rustc_middle:: ty:: subst:: Subst ;
11
11
use rustc_middle:: ty:: { self , ConstKind , Instance , InstanceDef , ParamEnv , Ty , TyCtxt } ;
12
+ use rustc_session:: config:: OptLevel ;
12
13
use rustc_span:: { hygiene:: ExpnKind , ExpnData , LocalExpnId , Span } ;
13
14
use rustc_target:: spec:: abi:: Abi ;
14
15
@@ -43,7 +44,15 @@ impl<'tcx> MirPass<'tcx> for Inline {
43
44
return enabled;
44
45
}
45
46
46
- sess. opts . mir_opt_level ( ) >= 3
47
+ match sess. mir_opt_level ( ) {
48
+ 0 | 1 => false ,
49
+ 2 => {
50
+ ( sess. opts . optimize == OptLevel :: Default
51
+ || sess. opts . optimize == OptLevel :: Aggressive )
52
+ && sess. opts . incremental == None
53
+ }
54
+ _ => true ,
55
+ }
47
56
}
48
57
49
58
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
@@ -76,13 +85,6 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
76
85
}
77
86
78
87
let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
79
- let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def_id) ;
80
- let param_env = rustc_trait_selection:: traits:: normalize_param_env_or_error (
81
- tcx,
82
- def_id. to_def_id ( ) ,
83
- param_env,
84
- ObligationCause :: misc ( body. span , hir_id) ,
85
- ) ;
86
88
87
89
let mut this = Inliner {
88
90
tcx,
@@ -166,6 +168,45 @@ impl<'tcx> Inliner<'tcx> {
166
168
return Err ( "failed to normalize callee body" ) ;
167
169
} ;
168
170
171
+ // Check call signature compatibility.
172
+ // Normally, this shouldn't be required, but trait normalization failure can create a
173
+ // validation ICE.
174
+ let terminator = caller_body[ callsite. block ] . terminator . as_ref ( ) . unwrap ( ) ;
175
+ let TerminatorKind :: Call { args, destination, .. } = & terminator. kind else { bug ! ( ) } ;
176
+ let destination_ty = destination. ty ( & caller_body. local_decls , self . tcx ) . ty ;
177
+ let output_type = callee_body. return_ty ( ) ;
178
+ if !equal_up_to_regions ( self . tcx , self . param_env , output_type, destination_ty) {
179
+ trace ! ( ?output_type, ?destination_ty) ;
180
+ return Err ( "failed to normalize return type" ) ;
181
+ }
182
+ if callsite. fn_sig . abi ( ) == Abi :: RustCall {
183
+ let mut args = args. into_iter ( ) ;
184
+ let _ = args. next ( ) ; // Skip `self` argument.
185
+ let arg_tuple_ty = args. next ( ) . unwrap ( ) . ty ( & caller_body. local_decls , self . tcx ) ;
186
+ assert ! ( args. next( ) . is_none( ) ) ;
187
+
188
+ let ty:: Tuple ( arg_tuple_tys) = arg_tuple_ty. kind ( ) else {
189
+ bug ! ( "Closure arguments are not passed as a tuple" ) ;
190
+ } ;
191
+
192
+ for ( arg_ty, input) in arg_tuple_tys. iter ( ) . zip ( callee_body. args_iter ( ) . skip ( 1 ) ) {
193
+ let input_type = callee_body. local_decls [ input] . ty ;
194
+ if !equal_up_to_regions ( self . tcx , self . param_env , arg_ty, input_type) {
195
+ trace ! ( ?arg_ty, ?input_type) ;
196
+ return Err ( "failed to normalize tuple argument type" ) ;
197
+ }
198
+ }
199
+ } else {
200
+ for ( arg, input) in args. iter ( ) . zip ( callee_body. args_iter ( ) ) {
201
+ let input_type = callee_body. local_decls [ input] . ty ;
202
+ let arg_ty = arg. ty ( & caller_body. local_decls , self . tcx ) ;
203
+ if !equal_up_to_regions ( self . tcx , self . param_env , arg_ty, input_type) {
204
+ trace ! ( ?arg_ty, ?input_type) ;
205
+ return Err ( "failed to normalize argument type" ) ;
206
+ }
207
+ }
208
+ }
209
+
169
210
let old_blocks = caller_body. basic_blocks ( ) . next_index ( ) ;
170
211
self . inline_call ( caller_body, & callsite, callee_body) ;
171
212
let new_blocks = old_blocks..caller_body. basic_blocks ( ) . next_index ( ) ;
@@ -263,6 +304,10 @@ impl<'tcx> Inliner<'tcx> {
263
304
return None ;
264
305
}
265
306
307
+ if self . history . contains ( & callee) {
308
+ return None ;
309
+ }
310
+
266
311
let fn_sig = self . tcx . bound_fn_sig ( def_id) . subst ( self . tcx , substs) ;
267
312
268
313
return Some ( CallSite {
@@ -285,8 +330,14 @@ impl<'tcx> Inliner<'tcx> {
285
330
callsite : & CallSite < ' tcx > ,
286
331
callee_attrs : & CodegenFnAttrs ,
287
332
) -> Result < ( ) , & ' static str > {
288
- if let InlineAttr :: Never = callee_attrs. inline {
289
- return Err ( "never inline hint" ) ;
333
+ match callee_attrs. inline {
334
+ InlineAttr :: Never => return Err ( "never inline hint" ) ,
335
+ InlineAttr :: Always | InlineAttr :: Hint => { }
336
+ InlineAttr :: None => {
337
+ if self . tcx . sess . mir_opt_level ( ) <= 2 {
338
+ return Err ( "at mir-opt-level=2, only #[inline] is inlined" ) ;
339
+ }
340
+ }
290
341
}
291
342
292
343
// Only inline local functions if they would be eligible for cross-crate
@@ -407,22 +458,9 @@ impl<'tcx> Inliner<'tcx> {
407
458
}
408
459
409
460
TerminatorKind :: Call { func : Operand :: Constant ( ref f) , cleanup, .. } => {
410
- if let ty:: FnDef ( def_id, substs ) =
461
+ if let ty:: FnDef ( def_id, _ ) =
411
462
* callsite. callee . subst_mir ( self . tcx , & f. literal . ty ( ) ) . kind ( )
412
463
{
413
- if let Ok ( substs) =
414
- self . tcx . try_normalize_erasing_regions ( self . param_env , substs)
415
- {
416
- if let Ok ( Some ( instance) ) =
417
- Instance :: resolve ( self . tcx , self . param_env , def_id, substs)
418
- {
419
- if callsite. callee . def_id ( ) == instance. def_id ( ) {
420
- return Err ( "self-recursion" ) ;
421
- } else if self . history . contains ( & instance) {
422
- return Err ( "already inlined" ) ;
423
- }
424
- }
425
- }
426
464
// Don't give intrinsics the extra penalty for calls
427
465
if tcx. is_intrinsic ( def_id) {
428
466
cost += INSTR_COST ;
@@ -482,14 +520,12 @@ impl<'tcx> Inliner<'tcx> {
482
520
if let InlineAttr :: Always = callee_attrs. inline {
483
521
debug ! ( "INLINING {:?} because inline(always) [cost={}]" , callsite, cost) ;
484
522
Ok ( ( ) )
523
+ } else if cost <= threshold {
524
+ debug ! ( "INLINING {:?} [cost={} <= threshold={}]" , callsite, cost, threshold) ;
525
+ Ok ( ( ) )
485
526
} else {
486
- if cost <= threshold {
487
- debug ! ( "INLINING {:?} [cost={} <= threshold={}]" , callsite, cost, threshold) ;
488
- Ok ( ( ) )
489
- } else {
490
- debug ! ( "NOT inlining {:?} [cost={} > threshold={}]" , callsite, cost, threshold) ;
491
- Err ( "cost above threshold" )
492
- }
527
+ debug ! ( "NOT inlining {:?} [cost={} > threshold={}]" , callsite, cost, threshold) ;
528
+ Err ( "cost above threshold" )
493
529
}
494
530
}
495
531
0 commit comments