Skip to content

[MIR] Initial implementation of inlining #36593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
12 changes: 8 additions & 4 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,8 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
Ref(region, bk, ref lval) => Ref(region.fold_with(folder), bk, lval.fold_with(folder)),
Len(ref lval) => Len(lval.fold_with(folder)),
Cast(kind, ref op, ty) => Cast(kind, op.fold_with(folder), ty.fold_with(folder)),
BinaryOp(op, ref rhs, ref lhs) => BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)),
BinaryOp(op, ref rhs, ref lhs) =>
BinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)),
CheckedBinaryOp(op, ref rhs, ref lhs) =>
CheckedBinaryOp(op, rhs.fold_with(folder), lhs.fold_with(folder)),
UnaryOp(op, ref val) => UnaryOp(op, val.fold_with(folder)),
Expand All @@ -1587,7 +1588,8 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
AggregateKind::Tuple => AggregateKind::Tuple,
AggregateKind::Adt(def, v, substs, n) =>
AggregateKind::Adt(def, v, substs.fold_with(folder), n),
AggregateKind::Closure(id, substs) => AggregateKind::Closure(id, substs.fold_with(folder))
AggregateKind::Closure(id, substs) =>
AggregateKind::Closure(id, substs.fold_with(folder))
};
Aggregate(kind, fields.fold_with(folder))
}
Expand All @@ -1608,7 +1610,8 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
Len(ref lval) => lval.visit_with(visitor),
Cast(_, ref op, ty) => op.visit_with(visitor) || ty.visit_with(visitor),
BinaryOp(_, ref rhs, ref lhs) |
CheckedBinaryOp(_, ref rhs, ref lhs) => rhs.visit_with(visitor) || lhs.visit_with(visitor),
CheckedBinaryOp(_, ref rhs, ref lhs) =>
rhs.visit_with(visitor) || lhs.visit_with(visitor),
UnaryOp(_, ref val) => val.visit_with(visitor),
Box(ty) => ty.visit_with(visitor),
Aggregate(ref kind, ref fields) => {
Expand All @@ -1619,7 +1622,8 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
AggregateKind::Closure(_, substs) => substs.visit_with(visitor)
}) || fields.visit_with(visitor)
}
InlineAsm { ref outputs, ref inputs, .. } => outputs.visit_with(visitor) || inputs.visit_with(visitor)
InlineAsm { ref outputs, ref inputs, .. } =>
outputs.visit_with(visitor) || inputs.visit_with(visitor)
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_mir/callgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub struct CallGraph {
}

impl CallGraph {
// FIXME: allow for construction of a callgraph that inspects
// cross-crate MIRs if available.
pub fn build<'tcx>(map: &MirMap<'tcx>) -> CallGraph {
let def_ids = map.map.keys();

Expand All @@ -52,10 +54,12 @@ impl CallGraph {
callgraph
}

// Iterate over the strongly-connected components of the graph
pub fn scc_iter<'g>(&'g self) -> SCCIterator<'g> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think you can lifetime elide 'g here

SCCIterator::new(&self.graph)
}

// Get the def_id for the given graph node
pub fn def_id(&self, node: graph::NodeIndex) -> DefId {
*self.graph.node_data(node)
}
Expand Down Expand Up @@ -93,6 +97,11 @@ struct StackElement<'g> {
children: graph::AdjacentTargets<'g, DefId, ()>
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in rustc_data_structures, with tests.

* Iterator over strongly-connected-components using Tarjan's algorithm[1]
*
* [1]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ///, not that I care

pub struct SCCIterator<'g> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be factored into librustc_data_structures, and ideally the graph algorithms portion of that code. But for now just open a FIXME about it.

graph: &'g graph::Graph<DefId, ()>,
index: usize,
Expand Down
19 changes: 16 additions & 3 deletions src/librustc_mir/transform/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {

let tcx = self.tcx;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like an info! at the start of this function and on every exit point. This should make optimization-debugging so much easier.


// Don't inline closures
// Don't inline closures that have captures
// FIXME: Handle closures better
if callee_mir.upvar_decls.len() > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the problem here? I was sure closures with upvars are a prime inlining target.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to handle the debuginfo related stuff, so I'm punting on it for now. You're right, but this isn't intended to be a complete implementation right away.

return false;
}
Expand Down Expand Up @@ -441,12 +442,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {

let terminator = caller_mir[callsite.bb].terminator.take().unwrap();
match terminator.kind {
// FIXME: Handle inlining of diverging calls
TerminatorKind::Call { args, destination: Some(destination), cleanup, .. } => {

debug!("Inlined {:?} into {:?}", callsite.callee, callsite.caller);

let is_box_free = Some(callsite.callee) == self.tcx.lang_items.box_free_fn();
let bb_len = caller_mir.basic_blocks().len();

let mut var_map = IndexVec::with_capacity(callee_mir.var_decls.len());
let mut temp_map = IndexVec::with_capacity(callee_mir.temp_decls.len());
Expand Down Expand Up @@ -527,7 +528,6 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {

let return_block = destination.1;

// Copy the arguments if needed.
let args : Vec<_> = if is_box_free {
assert!(args.len() == 1);
// box_free takes a Box, but is defined with a *mut T, inlining
Expand Down Expand Up @@ -580,7 +580,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {

vec![Operand::Consume(tmp)]
} else {
// Copy the arguments if needed.
let tcx = self.tcx;
// FIXME: Analysis of the usage of the arguments to avoid
// unnecessary temporaries.
args.iter().map(|a| {
if let Operand::Consume(Lvalue::Temp(_)) = *a {
// Reuse the operand if it's a temporary already
Expand All @@ -606,6 +609,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
}).collect()
};

let bb_len = caller_mir.basic_blocks.len();
let mut integrator = Integrator {
block_idx: bb_len,
args: &args,
Expand Down Expand Up @@ -663,6 +667,13 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParameterE
})
}

/**
* Integrator.
*
* Integrates blocks from the callee function into the calling function.
* Updates block indices, references to locals and other control flow
* stuff.
*/
struct Integrator<'a, 'tcx: 'a> {
block_idx: usize,
args: &'a [Operand<'tcx>],
Expand Down Expand Up @@ -707,6 +718,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
let idx = arg.index();
if let Operand::Consume(ref lval) = self.args[idx] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't args just be a IdxVec<-, Lvalue<'tcx>>?

*lvalue = lval.clone();
} else {
bug!("Arg operand `{:?}` is not an Lvalue use.", arg)
}
}
_ => self.super_lvalue(lvalue, _ctxt, _location)
Expand Down