1
1
//! Inlining pass for MIR functions
2
2
use crate :: deref_separator:: deref_finder;
3
3
use rustc_attr:: InlineAttr ;
4
+ use rustc_hir:: def_id:: DefId ;
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 } ;
@@ -27,6 +28,8 @@ const RESUME_PENALTY: usize = 45;
27
28
28
29
const UNKNOWN_SIZE_COST : usize = 10 ;
29
30
31
+ const TOP_DOWN_DEPTH_LIMIT : usize = 5 ;
32
+
30
33
pub struct Inline ;
31
34
32
35
#[ derive( Copy , Clone , Debug ) ]
@@ -86,8 +89,13 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
86
89
87
90
let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
88
91
89
- let mut this =
90
- Inliner { tcx, param_env, codegen_fn_attrs : tcx. codegen_fn_attrs ( def_id) , changed : false } ;
92
+ let mut this = Inliner {
93
+ tcx,
94
+ param_env,
95
+ codegen_fn_attrs : tcx. codegen_fn_attrs ( def_id) ,
96
+ history : Vec :: new ( ) ,
97
+ changed : false ,
98
+ } ;
91
99
let blocks = BasicBlock :: new ( 0 ) ..body. basic_blocks . next_index ( ) ;
92
100
this. process_blocks ( body, blocks) ;
93
101
this. changed
@@ -98,12 +106,26 @@ struct Inliner<'tcx> {
98
106
param_env : ParamEnv < ' tcx > ,
99
107
/// Caller codegen attributes.
100
108
codegen_fn_attrs : & ' tcx CodegenFnAttrs ,
109
+ /// Stack of inlined instances.
110
+ /// We only check the `DefId` and not the substs because we want to
111
+ /// avoid inlining cases of polymorphic recursion.
112
+ /// The number of `DefId`s is finite, so checking history is enough
113
+ /// to ensure that we do not loop endlessly while inlining.
114
+ history : Vec < DefId > ,
101
115
/// Indicates that the caller body has been modified.
102
116
changed : bool ,
103
117
}
104
118
105
119
impl < ' tcx > Inliner < ' tcx > {
106
120
fn process_blocks ( & mut self , caller_body : & mut Body < ' tcx > , blocks : Range < BasicBlock > ) {
121
+ // How many callsites in this body are we allowed to inline? We need to limit this in order
122
+ // to prevent super-linear growth in MIR size
123
+ let inline_limit = match self . history . len ( ) {
124
+ 0 => usize:: MAX ,
125
+ 1 ..=TOP_DOWN_DEPTH_LIMIT => 1 ,
126
+ _ => return ,
127
+ } ;
128
+ let mut inlined_count = 0 ;
107
129
for bb in blocks {
108
130
let bb_data = & caller_body[ bb] ;
109
131
if bb_data. is_cleanup {
@@ -122,12 +144,16 @@ impl<'tcx> Inliner<'tcx> {
122
144
debug ! ( "not-inlined {} [{}]" , callsite. callee, reason) ;
123
145
continue ;
124
146
}
125
- Ok ( _ ) => {
147
+ Ok ( new_blocks ) => {
126
148
debug ! ( "inlined {}" , callsite. callee) ;
127
149
self . changed = true ;
128
- // We could process the blocks returned by `try_inlining` here. However, that
129
- // leads to exponential compile times due to the top-down nature of this kind
130
- // of inlining.
150
+ inlined_count += 1 ;
151
+ if inlined_count == inline_limit {
152
+ return ;
153
+ }
154
+ self . history . push ( callsite. callee . def_id ( ) ) ;
155
+ self . process_blocks ( caller_body, new_blocks) ;
156
+ self . history . pop ( ) ;
131
157
}
132
158
}
133
159
}
@@ -301,6 +327,10 @@ impl<'tcx> Inliner<'tcx> {
301
327
return None ;
302
328
}
303
329
330
+ if self . history . contains ( & callee. def_id ( ) ) {
331
+ return None ;
332
+ }
333
+
304
334
let fn_sig = self . tcx . bound_fn_sig ( def_id) . subst ( self . tcx , substs) ;
305
335
let source_info = SourceInfo { span : fn_span, ..terminator. source_info } ;
306
336
0 commit comments