@@ -26,6 +26,7 @@ use crate::{check_inline, util};
2626
2727pub ( crate ) mod cycle;
2828
29+ const HISTORY_DEPTH_LIMIT : usize = 20 ;
2930const TOP_DOWN_DEPTH_LIMIT : usize = 5 ;
3031
3132#[ derive( Clone , Debug ) ]
@@ -128,10 +129,6 @@ trait Inliner<'tcx> {
128129 callee_attrs : & CodegenFnAttrs ,
129130 ) -> Result < ( ) , & ' static str > ;
130131
131- // How many callsites in a body are we allowed to inline? We need to limit this in order
132- // to prevent super-linear growth in MIR size.
133- fn inline_limit_for_block ( & self ) -> Option < usize > ;
134-
135132 /// Called when inlining succeeds.
136133 fn on_inline_success (
137134 & mut self ,
@@ -142,9 +139,6 @@ trait Inliner<'tcx> {
142139
143140 /// Called when inlining failed or was not performed.
144141 fn on_inline_failure ( & self , callsite : & CallSite < ' tcx > , reason : & ' static str ) ;
145-
146- /// Called when the inline limit for a body is reached.
147- fn on_inline_limit_reached ( & self ) -> bool ;
148142}
149143
150144struct ForceInliner < ' tcx > {
@@ -224,10 +218,6 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
224218 }
225219 }
226220
227- fn inline_limit_for_block ( & self ) -> Option < usize > {
228- Some ( usize:: MAX )
229- }
230-
231221 fn on_inline_success (
232222 & mut self ,
233223 callsite : & CallSite < ' tcx > ,
@@ -261,10 +251,6 @@ impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> {
261251 justification : justification. map ( |sym| crate :: errors:: ForceInlineJustification { sym } ) ,
262252 } ) ;
263253 }
264-
265- fn on_inline_limit_reached ( & self ) -> bool {
266- false
267- }
268254}
269255
270256struct NormalInliner < ' tcx > {
@@ -278,6 +264,10 @@ struct NormalInliner<'tcx> {
278264 /// The number of `DefId`s is finite, so checking history is enough
279265 /// to ensure that we do not loop endlessly while inlining.
280266 history : Vec < DefId > ,
267+ /// How many (multi-call) callsites have we inlined for the top-level call?
268+ ///
269+ /// We need to limit this in order to prevent super-linear growth in MIR size.
270+ top_down_counter : usize ,
281271 /// Indicates that the caller body has been modified.
282272 changed : bool ,
283273 /// Indicates that the caller is #[inline] and just calls another function,
@@ -295,6 +285,7 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
295285 typing_env,
296286 def_id,
297287 history : Vec :: new ( ) ,
288+ top_down_counter : 0 ,
298289 changed : false ,
299290 caller_is_inline_forwarder : matches ! (
300291 codegen_fn_attrs. inline,
@@ -351,6 +342,15 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
351342 return Err ( "body has errors" ) ;
352343 }
353344
345+ if callee_body. basic_blocks . len ( ) > 1 {
346+ if self . history . len ( ) > HISTORY_DEPTH_LIMIT {
347+ return Err ( "Too many blocks for deep history" ) ;
348+ }
349+ if self . top_down_counter > TOP_DOWN_DEPTH_LIMIT {
350+ return Err ( "Too many blocks for more top-down inlining" ) ;
351+ }
352+ }
353+
354354 let mut threshold = if self . caller_is_inline_forwarder {
355355 tcx. sess . opts . unstable_opts . inline_mir_forwarder_threshold . unwrap_or ( 30 )
356356 } else if tcx. cross_crate_inlinable ( callsite. callee . def_id ( ) ) {
@@ -431,14 +431,6 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
431431 }
432432 }
433433
434- fn inline_limit_for_block ( & self ) -> Option < usize > {
435- match self . history . len ( ) {
436- 0 => Some ( usize:: MAX ) ,
437- 1 ..=TOP_DOWN_DEPTH_LIMIT => Some ( 1 ) ,
438- _ => None ,
439- }
440- }
441-
442434 fn on_inline_success (
443435 & mut self ,
444436 callsite : & CallSite < ' tcx > ,
@@ -447,13 +439,26 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> {
447439 ) {
448440 self . changed = true ;
449441
442+ let new_calls_count = new_blocks
443+ . clone ( )
444+ . filter ( |& bb| {
445+ matches ! (
446+ caller_body. basic_blocks[ bb] . terminator( ) . kind,
447+ TerminatorKind :: Call { .. } ,
448+ )
449+ } )
450+ . count ( ) ;
451+ if new_calls_count > 1 {
452+ self . top_down_counter += 1 ;
453+ }
454+
450455 self . history . push ( callsite. callee . def_id ( ) ) ;
451456 process_blocks ( self , caller_body, new_blocks) ;
452457 self . history . pop ( ) ;
453- }
454458
455- fn on_inline_limit_reached ( & self ) -> bool {
456- true
459+ if self . history . is_empty ( ) {
460+ self . top_down_counter = 0 ;
461+ }
457462 }
458463
459464 fn on_inline_failure ( & self , _: & CallSite < ' tcx > , _: & ' static str ) { }
@@ -482,8 +487,6 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
482487 caller_body : & mut Body < ' tcx > ,
483488 blocks : Range < BasicBlock > ,
484489) {
485- let Some ( inline_limit) = inliner. inline_limit_for_block ( ) else { return } ;
486- let mut inlined_count = 0 ;
487490 for bb in blocks {
488491 let bb_data = & caller_body[ bb] ;
489492 if bb_data. is_cleanup {
@@ -505,13 +508,6 @@ fn process_blocks<'tcx, I: Inliner<'tcx>>(
505508 Ok ( new_blocks) => {
506509 debug ! ( "inlined {}" , callsite. callee) ;
507510 inliner. on_inline_success ( & callsite, caller_body, new_blocks) ;
508-
509- inlined_count += 1 ;
510- if inlined_count == inline_limit {
511- if inliner. on_inline_limit_reached ( ) {
512- return ;
513- }
514- }
515511 }
516512 }
517513 }
0 commit comments