11//! Inlining pass for MIR functions
22use crate :: deref_separator:: deref_finder;
33use rustc_attr:: InlineAttr ;
4+ use rustc_const_eval:: transform:: validate:: equal_up_to_regions;
45use rustc_index:: bit_set:: BitSet ;
56use rustc_index:: vec:: Idx ;
67use rustc_middle:: middle:: codegen_fn_attrs:: { CodegenFnAttrFlags , CodegenFnAttrs } ;
78use rustc_middle:: mir:: visit:: * ;
89use rustc_middle:: mir:: * ;
9- use rustc_middle:: traits:: ObligationCause ;
1010use rustc_middle:: ty:: subst:: Subst ;
1111use rustc_middle:: ty:: { self , ConstKind , Instance , InstanceDef , ParamEnv , Ty , TyCtxt } ;
12+ use rustc_session:: config:: OptLevel ;
1213use rustc_span:: { hygiene:: ExpnKind , ExpnData , LocalExpnId , Span } ;
1314use rustc_target:: spec:: abi:: Abi ;
1415
@@ -43,7 +44,15 @@ impl<'tcx> MirPass<'tcx> for Inline {
4344 return enabled;
4445 }
4546
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+ }
4756 }
4857
4958 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 {
7685 }
7786
7887 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- ) ;
8688
8789 let mut this = Inliner {
8890 tcx,
@@ -166,6 +168,45 @@ impl<'tcx> Inliner<'tcx> {
166168 return Err ( "failed to normalize callee body" ) ;
167169 } ;
168170
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+
169210 let old_blocks = caller_body. basic_blocks ( ) . next_index ( ) ;
170211 self . inline_call ( caller_body, & callsite, callee_body) ;
171212 let new_blocks = old_blocks..caller_body. basic_blocks ( ) . next_index ( ) ;
@@ -263,6 +304,10 @@ impl<'tcx> Inliner<'tcx> {
263304 return None ;
264305 }
265306
307+ if self . history . contains ( & callee) {
308+ return None ;
309+ }
310+
266311 let fn_sig = self . tcx . bound_fn_sig ( def_id) . subst ( self . tcx , substs) ;
267312
268313 return Some ( CallSite {
@@ -285,8 +330,14 @@ impl<'tcx> Inliner<'tcx> {
285330 callsite : & CallSite < ' tcx > ,
286331 callee_attrs : & CodegenFnAttrs ,
287332 ) -> 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+ }
290341 }
291342
292343 // Only inline local functions if they would be eligible for cross-crate
@@ -407,22 +458,9 @@ impl<'tcx> Inliner<'tcx> {
407458 }
408459
409460 TerminatorKind :: Call { func : Operand :: Constant ( ref f) , cleanup, .. } => {
410- if let ty:: FnDef ( def_id, substs ) =
461+ if let ty:: FnDef ( def_id, _ ) =
411462 * callsite. callee . subst_mir ( self . tcx , & f. literal . ty ( ) ) . kind ( )
412463 {
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- }
426464 // Don't give intrinsics the extra penalty for calls
427465 if tcx. is_intrinsic ( def_id) {
428466 cost += INSTR_COST ;
@@ -482,14 +520,12 @@ impl<'tcx> Inliner<'tcx> {
482520 if let InlineAttr :: Always = callee_attrs. inline {
483521 debug ! ( "INLINING {:?} because inline(always) [cost={}]" , callsite, cost) ;
484522 Ok ( ( ) )
523+ } else if cost <= threshold {
524+ debug ! ( "INLINING {:?} [cost={} <= threshold={}]" , callsite, cost, threshold) ;
525+ Ok ( ( ) )
485526 } 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" )
493529 }
494530 }
495531
0 commit comments