@@ -14,27 +14,20 @@ use rustc::hir::def_id::DefId;
14
14
15
15
use rustc_data_structures:: bitvec:: BitVector ;
16
16
use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
17
- use rustc_data_structures:: graph;
18
17
19
- use rustc:: dep_graph:: DepNode ;
20
18
use rustc:: mir:: * ;
21
- use rustc:: mir:: transform:: { MirSource , PassId } ;
19
+ use rustc:: mir:: transform:: { MirPass , MirSource } ;
22
20
use rustc:: mir:: visit:: * ;
23
21
use rustc:: traits;
24
22
use rustc:: ty:: { self , Ty , TyCtxt } ;
25
- use rustc:: ty:: maps:: Multi ;
26
- use rustc:: ty:: steal:: Steal ;
27
23
use rustc:: ty:: subst:: { Subst , Substs } ;
28
- use rustc:: util:: nodemap:: { DefIdSet } ;
29
24
25
+ use std:: collections:: VecDeque ;
30
26
use super :: simplify:: { remove_dead_blocks, CfgSimplifier } ;
31
27
32
28
use syntax:: { attr} ;
33
29
use syntax:: abi:: Abi ;
34
30
35
- use callgraph;
36
- use transform:: interprocedural:: InterproceduralCx ;
37
-
38
31
const DEFAULT_THRESHOLD : usize = 50 ;
39
32
const HINT_THRESHOLD : usize = 100 ;
40
33
@@ -45,140 +38,92 @@ const UNKNOWN_SIZE_COST: usize = 10;
45
38
46
39
pub struct Inline ;
47
40
48
- pub trait Pass {
49
- fn run_pass < ' a , ' tcx : ' a > ( & self , tcx : TyCtxt < ' a , ' tcx , ' tcx > )
50
- -> Multi < PassId , & ' tcx Steal < Mir < ' tcx > > > ;
41
+ #[ derive( Copy , Clone ) ]
42
+ struct CallSite < ' tcx > {
43
+ callee : DefId ,
44
+ substs : & ' tcx Substs < ' tcx > ,
45
+ bb : BasicBlock ,
46
+ location : SourceInfo ,
51
47
}
52
48
53
- impl Pass for Inline {
54
- fn run_pass < ' a , ' tcx : ' a > ( & self , tcx : TyCtxt < ' a , ' tcx , ' tcx > )
55
- -> Multi < PassId , & ' tcx Steal < Mir < ' tcx > > > {
56
- let mut cx = InterproceduralCx :: new ( tcx) ;
57
-
58
- let callgraph = callgraph:: CallGraph :: build ( & mut cx) ;
59
-
60
- let mut inliner = Inliner { tcx } ;
61
-
62
- for scc in callgraph. scc_iter ( ) {
63
- inliner. inline_scc ( & mut cx, & callgraph, & scc) ;
49
+ impl MirPass for Inline {
50
+ fn run_pass < ' a , ' tcx > ( & self ,
51
+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
52
+ source : MirSource ,
53
+ mir : & mut Mir < ' tcx > ) {
54
+ if tcx. sess . opts . debugging_opts . mir_opt_level >= 2 {
55
+ Inliner { tcx, source } . run_pass ( mir) ;
64
56
}
65
-
66
- Multi :: from ( cx. into_local_mirs ( ) )
67
57
}
68
58
}
69
59
70
60
struct Inliner < ' a , ' tcx : ' a > {
71
61
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
72
- }
73
-
74
- #[ derive( Copy , Clone ) ]
75
- struct CallSite < ' tcx > {
76
- caller : DefId ,
77
- callee : DefId ,
78
- substs : & ' tcx Substs < ' tcx > ,
79
- bb : BasicBlock ,
80
- location : SourceInfo ,
62
+ source : MirSource ,
81
63
}
82
64
83
65
impl < ' a , ' tcx > Inliner < ' a , ' tcx > {
84
- fn inline_scc ( & mut self ,
85
- cx : & mut InterproceduralCx < ' a , ' tcx > ,
86
- callgraph : & callgraph:: CallGraph ,
87
- scc : & [ graph:: NodeIndex ] ) -> bool {
88
- let tcx = self . tcx ;
89
- let mut callsites = Vec :: new ( ) ;
90
- let mut in_scc = DefIdSet ( ) ;
91
-
92
- let mut inlined_into = DefIdSet ( ) ;
93
-
94
- for & node in scc {
95
- let def_id = callgraph. def_id ( node) ;
96
-
97
- // Don't inspect functions from other crates
98
- let id = if let Some ( id) = tcx. hir . as_local_node_id ( def_id) {
99
- id
100
- } else {
101
- continue ;
102
- } ;
103
- let src = MirSource :: from_node ( tcx, id) ;
104
- if let MirSource :: Fn ( _) = src {
105
- if let Some ( mir) = cx. ensure_mir_and_read ( def_id) {
106
- for ( bb, bb_data) in mir. basic_blocks ( ) . iter_enumerated ( ) {
107
- // Don't inline calls that are in cleanup blocks.
108
- if bb_data. is_cleanup { continue ; }
109
-
110
- // Only consider direct calls to functions
111
- let terminator = bb_data. terminator ( ) ;
112
- if let TerminatorKind :: Call {
113
- func : Operand :: Constant ( ref f) , .. } = terminator. kind {
114
- if let ty:: TyFnDef ( callee_def_id, substs, _) = f. ty . sty {
115
- callsites. push ( CallSite {
116
- caller : def_id,
117
- callee : callee_def_id,
118
- substs : substs,
119
- bb : bb,
120
- location : terminator. source_info
121
- } ) ;
122
- }
123
- }
66
+ fn run_pass ( & self , caller_mir : & mut Mir < ' tcx > ) {
67
+ // Keep a queue of callsites to try inlining on. We take
68
+ // advantage of the fact that queries detect cycles here to
69
+ // allow us to try and fetch the fully optimized MIR of a
70
+ // call; if it succeeds, we can inline it and we know that
71
+ // they do not call us. Otherwise, we just don't try to
72
+ // inline.
73
+ //
74
+ // We use a queue so that we inline "broadly" before we inline
75
+ // in depth. It is unclear if this is the current heuristic.
76
+
77
+ let mut callsites = VecDeque :: new ( ) ;
78
+
79
+ // Only do inlining into fn bodies.
80
+ if let MirSource :: Fn ( _) = self . source {
81
+ for ( bb, bb_data) in caller_mir. basic_blocks ( ) . iter_enumerated ( ) {
82
+ // Don't inline calls that are in cleanup blocks.
83
+ if bb_data. is_cleanup { continue ; }
84
+
85
+ // Only consider direct calls to functions
86
+ let terminator = bb_data. terminator ( ) ;
87
+ if let TerminatorKind :: Call {
88
+ func : Operand :: Constant ( ref f) , .. } = terminator. kind {
89
+ if let ty:: TyFnDef ( callee_def_id, substs, _) = f. ty . sty {
90
+ callsites. push_back ( CallSite {
91
+ callee : callee_def_id,
92
+ substs : substs,
93
+ bb : bb,
94
+ location : terminator. source_info
95
+ } ) ;
124
96
}
125
-
126
- in_scc. insert ( def_id) ;
127
97
}
128
98
}
129
99
}
130
100
131
- // Move callsites that are in the the SCC to the end so
132
- // they're inlined after calls to outside the SCC
133
- let mut first_call_in_scc = callsites. len ( ) ;
134
-
135
- let mut i = 0 ;
136
- while i < first_call_in_scc {
137
- let f = callsites[ i] . caller ;
138
- if in_scc. contains ( & f) {
139
- first_call_in_scc -= 1 ;
140
- callsites. swap ( i, first_call_in_scc) ;
141
- } else {
142
- i += 1 ;
143
- }
144
- }
145
-
146
101
let mut local_change;
147
102
let mut changed = false ;
148
103
149
104
loop {
150
105
local_change = false ;
151
- let mut csi = 0 ;
152
- while csi < callsites. len ( ) {
153
- let callsite = callsites[ csi] ;
154
- csi += 1 ;
155
-
156
- let _task = tcx. dep_graph . in_task ( DepNode :: Mir ( callsite. caller ) ) ;
157
- tcx. dep_graph . write ( DepNode :: Mir ( callsite. caller ) ) ;
158
-
159
- let callee_mir = {
160
- if let Some ( callee_mir) = cx. ensure_mir_and_read ( callsite. callee ) {
161
- if !self . should_inline ( callsite, & callee_mir) {
162
- continue ;
163
- }
106
+ while let Some ( callsite) = callsites. pop_front ( ) {
107
+ if !self . tcx . is_item_mir_available ( callsite. callee ) {
108
+ continue ;
109
+ }
164
110
165
- callee_mir. subst ( tcx, callsite. substs )
166
- } else {
167
- continue ;
111
+ let callee_mir = match ty:: queries:: optimized_mir:: try_get ( self . tcx ,
112
+ callsite. location . span ,
113
+ callsite. callee ) {
114
+ Ok ( ref callee_mir) if self . should_inline ( callsite, callee_mir) => {
115
+ callee_mir. subst ( self . tcx , callsite. substs )
168
116
}
169
117
118
+ _ => continue ,
170
119
} ;
171
120
172
- let caller_mir = cx. mir_mut ( callsite. caller ) ;
173
-
174
121
let start = caller_mir. basic_blocks ( ) . len ( ) ;
175
122
176
123
if !self . inline_call ( callsite, caller_mir, callee_mir) {
177
124
continue ;
178
125
}
179
126
180
- inlined_into. insert ( callsite. caller ) ;
181
-
182
127
// Add callsites from inlined function
183
128
for ( bb, bb_data) in caller_mir. basic_blocks ( ) . iter_enumerated ( ) . skip ( start) {
184
129
// Only consider direct calls to functions
@@ -188,8 +133,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
188
133
if let ty:: TyFnDef ( callee_def_id, substs, _) = f. ty . sty {
189
134
// Don't inline the same function multiple times.
190
135
if callsite. callee != callee_def_id {
191
- callsites. push ( CallSite {
192
- caller : callsite. caller ,
136
+ callsites. push_back ( CallSite {
193
137
callee : callee_def_id,
194
138
substs : substs,
195
139
bb : bb,
@@ -200,13 +144,6 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
200
144
}
201
145
}
202
146
203
- csi -= 1 ;
204
- if scc. len ( ) == 1 {
205
- callsites. swap_remove ( csi) ;
206
- } else {
207
- callsites. remove ( csi) ;
208
- }
209
-
210
147
local_change = true ;
211
148
changed = true ;
212
149
}
@@ -216,18 +153,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
216
153
}
217
154
}
218
155
219
- // Simplify functions we inlined into.
220
- for def_id in inlined_into {
221
- let _task = tcx. dep_graph . in_task ( DepNode :: Mir ( def_id) ) ;
222
- tcx. dep_graph . write ( DepNode :: Mir ( def_id) ) ;
223
-
224
- let caller_mir = cx. mir_mut ( def_id) ;
225
-
226
- debug ! ( "Running simplify cfg on {:?}" , def_id) ;
156
+ // Simplify if we inlined anything.
157
+ if changed {
158
+ debug ! ( "Running simplify cfg on {:?}" , self . source) ;
227
159
CfgSimplifier :: new ( caller_mir) . simplify ( ) ;
228
160
remove_dead_blocks ( caller_mir) ;
229
161
}
230
- changed
231
162
}
232
163
233
164
fn should_inline ( & self ,
@@ -286,8 +217,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
286
217
287
218
// FIXME: Give a bonus to functions with only a single caller
288
219
289
- let id = tcx. hir . as_local_node_id ( callsite. caller ) . expect ( "Caller not local" ) ;
290
- let param_env = ty:: ParameterEnvironment :: for_item ( tcx, id) ;
220
+ let param_env = ty:: ParameterEnvironment :: for_item ( tcx, self . source . item_id ( ) ) ;
291
221
292
222
let mut first_block = true ;
293
223
let mut cost = 0 ;
@@ -390,18 +320,11 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
390
320
callsite : CallSite < ' tcx > ,
391
321
caller_mir : & mut Mir < ' tcx > ,
392
322
mut callee_mir : Mir < ' tcx > ) -> bool {
393
- // Don't inline a function into itself
394
- if callsite. caller == callsite. callee { return false ; }
395
-
396
- let _task = self . tcx . dep_graph . in_task ( DepNode :: Mir ( callsite. caller ) ) ;
397
-
398
-
399
323
let terminator = caller_mir[ callsite. bb ] . terminator . take ( ) . unwrap ( ) ;
400
324
match terminator. kind {
401
325
// FIXME: Handle inlining of diverging calls
402
326
TerminatorKind :: Call { args, destination : Some ( destination) , cleanup, .. } => {
403
-
404
- debug ! ( "Inlined {:?} into {:?}" , callsite. callee, callsite. caller) ;
327
+ debug ! ( "Inlined {:?} into {:?}" , callsite. callee, self . source) ;
405
328
406
329
let is_box_free = Some ( callsite. callee ) == self . tcx . lang_items . box_free_fn ( ) ;
407
330
0 commit comments