Skip to content

Commit 1dd9c3e

Browse files
committed
support inlining by asking for optimizer mir for callees
I tested this with it enabled 100% of the time, and we were able to run mir-opt tests successfully.
1 parent 669d316 commit 1dd9c3e

File tree

3 files changed

+68
-140
lines changed

3 files changed

+68
-140
lines changed

src/librustc_driver/driver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
928928
passes.push_pass(MIR_OPTIMIZED, mir::transform::simplify::SimplifyCfg::new("elaborate-drops"));
929929

930930
// No lifetime analysis based on borrowing can be done from here on out.
931-
// TODO passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline);
931+
passes.push_pass(MIR_OPTIMIZED, mir::transform::inline::Inline);
932932
passes.push_pass(MIR_OPTIMIZED, mir::transform::instcombine::InstCombine);
933933
passes.push_pass(MIR_OPTIMIZED, mir::transform::deaggregator::Deaggregator);
934934
passes.push_pass(MIR_OPTIMIZED, mir::transform::copy_prop::CopyPropagation);

src/librustc_mir/queries.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,15 @@ pub fn provide(providers: &mut Providers) {
3838
mir_const,
3939
mir_validated,
4040
optimized_mir,
41+
is_item_mir_available,
4142
..*providers
4243
};
4344
}
4445

46+
fn is_item_mir_available<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
47+
tcx.mir_keys(def_id.krate).contains(&def_id)
48+
}
49+
4550
/// Finds the full set of def-ids within the current crate that have
4651
/// MIR associated with them.
4752
fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum)

src/librustc_mir/transform/inline.rs

Lines changed: 62 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,20 @@ use rustc::hir::def_id::DefId;
1414

1515
use rustc_data_structures::bitvec::BitVector;
1616
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
17-
use rustc_data_structures::graph;
1817

19-
use rustc::dep_graph::DepNode;
2018
use rustc::mir::*;
21-
use rustc::mir::transform::{MirSource, PassId};
19+
use rustc::mir::transform::{MirPass, MirSource};
2220
use rustc::mir::visit::*;
2321
use rustc::traits;
2422
use rustc::ty::{self, Ty, TyCtxt};
25-
use rustc::ty::maps::Multi;
26-
use rustc::ty::steal::Steal;
2723
use rustc::ty::subst::{Subst,Substs};
28-
use rustc::util::nodemap::{DefIdSet};
2924

25+
use std::collections::VecDeque;
3026
use super::simplify::{remove_dead_blocks, CfgSimplifier};
3127

3228
use syntax::{attr};
3329
use syntax::abi::Abi;
3430

35-
use callgraph;
36-
use transform::interprocedural::InterproceduralCx;
37-
3831
const DEFAULT_THRESHOLD: usize = 50;
3932
const HINT_THRESHOLD: usize = 100;
4033

@@ -45,140 +38,92 @@ const UNKNOWN_SIZE_COST: usize = 10;
4538

4639
pub struct Inline;
4740

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,
5147
}
5248

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);
6456
}
65-
66-
Multi::from(cx.into_local_mirs())
6757
}
6858
}
6959

7060
struct Inliner<'a, 'tcx: 'a> {
7161
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,
8163
}
8264

8365
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+
});
12496
}
125-
126-
in_scc.insert(def_id);
12797
}
12898
}
12999
}
130100

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-
146101
let mut local_change;
147102
let mut changed = false;
148103

149104
loop {
150105
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+
}
164110

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)
168116
}
169117

118+
_ => continue,
170119
};
171120

172-
let caller_mir = cx.mir_mut(callsite.caller);
173-
174121
let start = caller_mir.basic_blocks().len();
175122

176123
if !self.inline_call(callsite, caller_mir, callee_mir) {
177124
continue;
178125
}
179126

180-
inlined_into.insert(callsite.caller);
181-
182127
// Add callsites from inlined function
183128
for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated().skip(start) {
184129
// Only consider direct calls to functions
@@ -188,8 +133,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
188133
if let ty::TyFnDef(callee_def_id, substs, _) = f.ty.sty {
189134
// Don't inline the same function multiple times.
190135
if callsite.callee != callee_def_id {
191-
callsites.push(CallSite {
192-
caller: callsite.caller,
136+
callsites.push_back(CallSite {
193137
callee: callee_def_id,
194138
substs: substs,
195139
bb: bb,
@@ -200,13 +144,6 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
200144
}
201145
}
202146

203-
csi -= 1;
204-
if scc.len() == 1 {
205-
callsites.swap_remove(csi);
206-
} else {
207-
callsites.remove(csi);
208-
}
209-
210147
local_change = true;
211148
changed = true;
212149
}
@@ -216,18 +153,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
216153
}
217154
}
218155

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);
227159
CfgSimplifier::new(caller_mir).simplify();
228160
remove_dead_blocks(caller_mir);
229161
}
230-
changed
231162
}
232163

233164
fn should_inline(&self,
@@ -286,8 +217,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
286217

287218
// FIXME: Give a bonus to functions with only a single caller
288219

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());
291221

292222
let mut first_block = true;
293223
let mut cost = 0;
@@ -390,18 +320,11 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
390320
callsite: CallSite<'tcx>,
391321
caller_mir: &mut Mir<'tcx>,
392322
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-
399323
let terminator = caller_mir[callsite.bb].terminator.take().unwrap();
400324
match terminator.kind {
401325
// FIXME: Handle inlining of diverging calls
402326
TerminatorKind::Call { args, destination: Some(destination), cleanup, .. } => {
403-
404-
debug!("Inlined {:?} into {:?}", callsite.callee, callsite.caller);
327+
debug!("Inlined {:?} into {:?}", callsite.callee, self.source);
405328

406329
let is_box_free = Some(callsite.callee) == self.tcx.lang_items.box_free_fn();
407330

0 commit comments

Comments
 (0)