Skip to content

Commit 1cedff9

Browse files
committed
Initial implementation of inlining
Implements basic inlining for MIR functions. The cases it will inline for are conservative, any function that can unwind will not be inlined. Inlining is currently only done if `mir-opt-level` is set to 2. Does not handle debuginfo correctly at the moment.
1 parent 47d5bb7 commit 1cedff9

File tree

6 files changed

+976
-4
lines changed

6 files changed

+976
-4
lines changed

src/librustc/dep_graph/dep_node.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub enum DepNode<D: Clone + Debug> {
3434
// `Krate` value gives you access to all other items. To avoid
3535
// this fate, do not call `tcx.map.krate()`; instead, prefer
3636
// wrappers like `tcx.visit_all_items_in_krate()`. If there is no
37-
// suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain
37+
// suitable wrapper, you can use `tcx.dep_graph.in_ignore()` to gain
3838
// access to the krate, but you must remember to add suitable
3939
// edges yourself for the individual items that you read.
4040
Krate,
@@ -252,4 +252,3 @@ impl<D: Clone + Debug> DepNode<D> {
252252
/// them even in the absence of a tcx.)
253253
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
254254
pub struct WorkProductId(pub String);
255-

src/librustc/mir/repr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ macro_rules! newtype_index {
6363
pub struct Mir<'tcx> {
6464
/// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
6565
/// that indexes into this vector.
66-
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
66+
pub basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
6767

6868
/// List of visibility (lexical) scopes; these are referenced by statements
6969
/// and used (eventually) for debuginfo. Indexed by a `VisibilityScope`.

src/librustc_mir/callgraph.rs

+242
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! MIR-based callgraph.
12+
//!
13+
//! This only considers direct calls
14+
15+
use rustc::hir::def_id::DefId;
16+
use rustc_data_structures::graph;
17+
18+
use rustc::mir::repr::*;
19+
use rustc::mir::visit::*;
20+
use rustc::mir::mir_map::MirMap;
21+
22+
use rustc::ty;
23+
24+
use rustc::util::nodemap::DefIdMap;
25+
26+
pub struct CallGraph {
27+
node_map: DefIdMap<graph::NodeIndex>,
28+
graph: graph::Graph<DefId, ()>
29+
}
30+
31+
impl CallGraph {
32+
pub fn build<'tcx>(map: &MirMap<'tcx>) -> CallGraph {
33+
let def_ids = map.map.keys();
34+
35+
let mut callgraph = CallGraph {
36+
node_map: DefIdMap(),
37+
graph: graph::Graph::new()
38+
};
39+
40+
for def_id in def_ids {
41+
let idx = callgraph.add_node(def_id);
42+
43+
let mut call_visitor = CallVisitor {
44+
caller: idx,
45+
graph: &mut callgraph
46+
};
47+
48+
let mir = map.map.get(&def_id).unwrap();
49+
call_visitor.visit_mir(&mir);
50+
}
51+
52+
callgraph
53+
}
54+
55+
pub fn scc_iter<'g>(&'g self) -> SCCIterator<'g> {
56+
SCCIterator::new(&self.graph)
57+
}
58+
59+
pub fn def_id(&self, node: graph::NodeIndex) -> DefId {
60+
*self.graph.node_data(node)
61+
}
62+
63+
fn add_node(&mut self, id: DefId) -> graph::NodeIndex {
64+
let graph = &mut self.graph;
65+
*self.node_map.entry(id).or_insert_with(|| {
66+
graph.add_node(id)
67+
})
68+
}
69+
}
70+
71+
struct CallVisitor<'a> {
72+
caller: graph::NodeIndex,
73+
graph: &'a mut CallGraph
74+
}
75+
76+
impl<'a, 'tcx> Visitor<'tcx> for CallVisitor<'a> {
77+
fn visit_terminator_kind(&mut self, _block: BasicBlock,
78+
kind: &TerminatorKind<'tcx>, _loc: Location) {
79+
if let TerminatorKind::Call {
80+
func: Operand::Constant(ref f)
81+
, .. } = *kind {
82+
if let ty::TyFnDef(def_id, _, _) = f.ty.sty {
83+
let callee = self.graph.add_node(def_id);
84+
self.graph.graph.add_edge(self.caller, callee, ());
85+
}
86+
}
87+
}
88+
}
89+
90+
struct StackElement<'g> {
91+
node: graph::NodeIndex,
92+
lowlink: usize,
93+
children: graph::AdjacentTargets<'g, DefId, ()>
94+
}
95+
96+
pub struct SCCIterator<'g> {
97+
graph: &'g graph::Graph<DefId, ()>,
98+
index: usize,
99+
node_indices: Vec<Option<usize>>,
100+
scc_stack: Vec<graph::NodeIndex>,
101+
current_scc: Vec<graph::NodeIndex>,
102+
visit_stack: Vec<StackElement<'g>>,
103+
}
104+
105+
impl<'g> SCCIterator<'g> {
106+
pub fn new(graph: &'g graph::Graph<DefId, ()>) -> SCCIterator<'g> {
107+
if graph.len_nodes() == 0 {
108+
return SCCIterator {
109+
graph: graph,
110+
index: 0,
111+
node_indices: Vec::new(),
112+
scc_stack: Vec::new(),
113+
current_scc: Vec::new(),
114+
visit_stack: Vec::new()
115+
};
116+
}
117+
118+
let first = graph::NodeIndex(0);
119+
120+
SCCIterator::with_entry(graph, first)
121+
}
122+
123+
pub fn with_entry(graph: &'g graph::Graph<DefId, ()>,
124+
entry: graph::NodeIndex) -> SCCIterator<'g> {
125+
let mut iter = SCCIterator {
126+
graph: graph,
127+
index: 0,
128+
node_indices: Vec::with_capacity(graph.len_nodes()),
129+
scc_stack: Vec::new(),
130+
current_scc: Vec::new(),
131+
visit_stack: Vec::new()
132+
};
133+
134+
iter.visit_one(entry);
135+
136+
iter
137+
}
138+
139+
fn get_next(&mut self) {
140+
self.current_scc.clear();
141+
142+
while !self.visit_stack.is_empty() {
143+
self.visit_children();
144+
145+
let node = self.visit_stack.pop().unwrap();
146+
147+
if let Some(last) = self.visit_stack.last_mut() {
148+
if last.lowlink > node.lowlink {
149+
last.lowlink = node.lowlink;
150+
}
151+
}
152+
153+
debug!("TarjanSCC: Popped node {:?} : lowlink = {:?}; index = {:?}",
154+
node.node, node.lowlink, self.node_index(node.node).unwrap());
155+
156+
if node.lowlink != self.node_index(node.node).unwrap() {
157+
continue;
158+
}
159+
160+
loop {
161+
let n = self.scc_stack.pop().unwrap();
162+
self.current_scc.push(n);
163+
self.set_node_index(n, !0);
164+
if n == node.node { return; }
165+
}
166+
}
167+
}
168+
169+
fn visit_one(&mut self, node: graph::NodeIndex) {
170+
self.index += 1;
171+
let idx = self.index;
172+
self.set_node_index(node, idx);
173+
self.scc_stack.push(node);
174+
self.visit_stack.push(StackElement {
175+
node: node,
176+
lowlink: self.index,
177+
children: self.graph.successor_nodes(node)
178+
});
179+
debug!("TarjanSCC: Node {:?} : index = {:?}", node, idx);
180+
}
181+
182+
fn visit_children(&mut self) {
183+
while let Some(child) = self.visit_stack.last_mut().unwrap().children.next() {
184+
if let Some(child_num) = self.node_index(child) {
185+
let cur = self.visit_stack.last_mut().unwrap();
186+
if cur.lowlink > child_num {
187+
cur.lowlink = child_num;
188+
}
189+
} else {
190+
self.visit_one(child);
191+
}
192+
}
193+
}
194+
195+
fn node_index(&self, node: graph::NodeIndex) -> Option<usize> {
196+
self.node_indices.get(node.node_id()).and_then(|&idx| idx)
197+
}
198+
199+
fn set_node_index(&mut self, node: graph::NodeIndex, idx: usize) {
200+
let i = node.node_id();
201+
if i >= self.node_indices.len() {
202+
self.node_indices.resize(i + 1, None);
203+
}
204+
self.node_indices[i] = Some(idx);
205+
}
206+
}
207+
208+
impl<'g> Iterator for SCCIterator<'g> {
209+
type Item = Vec<graph::NodeIndex>;
210+
211+
fn next(&mut self) -> Option<Vec<graph::NodeIndex>> {
212+
self.get_next();
213+
214+
if self.current_scc.is_empty() {
215+
// Try a new root for the next SCC, if the node_indices
216+
// map is doesn't contain all nodes, use the smallest one
217+
// with no entry, otherwise find the first empty node.
218+
//
219+
// FIXME: This should probably use a set of precomputed
220+
// roots instead
221+
if self.node_indices.len() < self.graph.len_nodes() {
222+
let idx = graph::NodeIndex(self.node_indices.len());
223+
self.visit_one(idx);
224+
} else {
225+
for idx in 0..self.node_indices.len() {
226+
if self.node_indices[idx].is_none() {
227+
let idx = graph::NodeIndex(idx);
228+
self.visit_one(idx);
229+
break;
230+
}
231+
}
232+
}
233+
self.get_next();
234+
}
235+
236+
if self.current_scc.is_empty() {
237+
None
238+
} else {
239+
Some(self.current_scc.clone())
240+
}
241+
}
242+
}

src/librustc_mir/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ pub mod diagnostics;
4747

4848
pub mod build;
4949
pub mod def_use;
50+
pub mod callgraph;
5051
pub mod graphviz;
5152
mod hair;
5253
pub mod mir_map;
5354
pub mod pretty;
5455
pub mod transform;
55-

0 commit comments

Comments
 (0)