Skip to content

Commit 3cce5c7

Browse files
committed
Don't inline virtual calls (take 2)
When I fixed the previous mis-optimizations, I didn't realize there were actually two different places where we mutate `callsites` and both of them should have the same behavior. As a result, if a function was inlined and that function contained virtual function calls, they were incorrectly being inlined. I also added a test case which covers this.
1 parent 36a50c2 commit 3cce5c7

File tree

2 files changed

+85
-45
lines changed

2 files changed

+85
-45
lines changed

src/librustc_mir/transform/inline.rs

+49-45
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec};
1919

2020
use rustc::mir::*;
2121
use rustc::mir::visit::*;
22-
use rustc::ty::{self, Instance, InstanceDef, Ty, TyCtxt};
22+
use rustc::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
2323
use rustc::ty::subst::{Subst,Substs};
2424

2525
use std::collections::VecDeque;
@@ -85,39 +85,16 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
8585
// Only do inlining into fn bodies.
8686
let id = self.tcx.hir.as_local_node_id(self.source.def_id).unwrap();
8787
let body_owner_kind = self.tcx.hir.body_owner_kind(id);
88+
8889
if let (hir::BodyOwnerKind::Fn, None) = (body_owner_kind, self.source.promoted) {
8990

9091
for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated() {
91-
// Don't inline calls that are in cleanup blocks.
92-
if bb_data.is_cleanup { continue; }
93-
94-
// Only consider direct calls to functions
95-
let terminator = bb_data.terminator();
96-
if let TerminatorKind::Call {
97-
func: ref op, .. } = terminator.kind {
98-
if let ty::FnDef(callee_def_id, substs) = op.ty(caller_mir, self.tcx).sty {
99-
if let Some(instance) = Instance::resolve(self.tcx,
100-
param_env,
101-
callee_def_id,
102-
substs) {
103-
let is_virtual =
104-
if let InstanceDef::Virtual(..) = instance.def {
105-
true
106-
} else {
107-
false
108-
};
109-
110-
if !is_virtual {
111-
callsites.push_back(CallSite {
112-
callee: instance.def_id(),
113-
substs: instance.substs,
114-
bb,
115-
location: terminator.source_info
116-
});
117-
}
118-
}
119-
}
120-
}
92+
if let Some(callsite) = self.get_valid_function_call(bb,
93+
bb_data,
94+
caller_mir,
95+
param_env) {
96+
callsites.push_back(callsite);
97+
}
12198
}
12299
} else {
123100
return;
@@ -163,20 +140,13 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
163140

164141
// Add callsites from inlined function
165142
for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated().skip(start) {
166-
// Only consider direct calls to functions
167-
let terminator = bb_data.terminator();
168-
if let TerminatorKind::Call {
169-
func: Operand::Constant(ref f), .. } = terminator.kind {
170-
if let ty::FnDef(callee_def_id, substs) = f.ty.sty {
171-
// Don't inline the same function multiple times.
172-
if callsite.callee != callee_def_id {
173-
callsites.push_back(CallSite {
174-
callee: callee_def_id,
175-
substs,
176-
bb,
177-
location: terminator.source_info
178-
});
179-
}
143+
if let Some(new_callsite) = self.get_valid_function_call(bb,
144+
bb_data,
145+
caller_mir,
146+
param_env) {
147+
// Don't inline the same function multiple times.
148+
if callsite.callee != new_callsite.callee {
149+
callsites.push_back(new_callsite);
180150
}
181151
}
182152
}
@@ -198,6 +168,40 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
198168
}
199169
}
200170

171+
fn get_valid_function_call(&self,
172+
bb: BasicBlock,
173+
bb_data: &BasicBlockData<'tcx>,
174+
caller_mir: &Mir<'tcx>,
175+
param_env: ParamEnv<'tcx>,
176+
) -> Option<CallSite<'tcx>> {
177+
// Don't inline calls that are in cleanup blocks.
178+
if bb_data.is_cleanup { return None; }
179+
180+
// Only consider direct calls to functions
181+
let terminator = bb_data.terminator();
182+
if let TerminatorKind::Call { func: ref op, .. } = terminator.kind {
183+
if let ty::FnDef(callee_def_id, substs) = op.ty(caller_mir, self.tcx).sty {
184+
let instance = Instance::resolve(self.tcx,
185+
param_env,
186+
callee_def_id,
187+
substs)?;
188+
189+
if let InstanceDef::Virtual(..) = instance.def {
190+
return None;
191+
}
192+
193+
return Some(CallSite {
194+
callee: instance.def_id(),
195+
substs: instance.substs,
196+
bb,
197+
location: terminator.source_info
198+
});
199+
}
200+
}
201+
202+
None
203+
}
204+
201205
fn consider_optimizing(&self,
202206
callsite: CallSite<'tcx>,
203207
callee_mir: &Mir<'tcx>)
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// compile-flags: -Z span_free_formats -Z mir-opt-level=3
2+
3+
#[inline]
4+
fn test(x: &dyn X) -> bool {
5+
x.y()
6+
}
7+
8+
fn test2(x: &dyn X) -> bool {
9+
test(x)
10+
}
11+
12+
trait X {
13+
fn y(&self) -> bool {
14+
false
15+
}
16+
}
17+
18+
impl X for () {
19+
fn y(&self) -> bool {
20+
true
21+
}
22+
}
23+
24+
fn main() {
25+
println!("Should be true: {}", test2(&()));
26+
}
27+
28+
// END RUST SOURCE
29+
// START rustc.test2.Inline.after.mir
30+
// ...
31+
// bb0: {
32+
// ...
33+
// _0 = const X::y(move _2) -> bb1;
34+
// }
35+
// ...
36+
// END rustc.test2.Inline.after.mir

0 commit comments

Comments
 (0)