Skip to content

Commit

Permalink
librustc: Implement def-use chains and trivial copy propagation on MIR.
Browse files Browse the repository at this point in the history
This only supports trivial cases in which there is exactly one def and
one use.
  • Loading branch information
pcwalton committed Sep 20, 2016
1 parent 2e6a918 commit 480287e
Show file tree
Hide file tree
Showing 14 changed files with 560 additions and 32 deletions.
46 changes: 46 additions & 0 deletions src/librustc/mir/repr.rs
Expand Up @@ -188,6 +188,24 @@ impl<'tcx> Mir<'tcx> {
self.temp_decls.len() + 1
}

pub fn format_local(&self, local: Local) -> String {
let mut index = local.index();
index = match index.checked_sub(self.arg_decls.len()) {
None => return format!("{:?}", Arg::new(index)),
Some(index) => index,
};
index = match index.checked_sub(self.var_decls.len()) {
None => return format!("{:?}", Var::new(index)),
Some(index) => index,
};
index = match index.checked_sub(self.temp_decls.len()) {
None => return format!("{:?}", Temp::new(index)),
Some(index) => index,
};
debug_assert!(index == 0);
return "ReturnPointer".to_string()
}

/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
/// invalidating statement indices in `Location`s.
pub fn make_statement_nop(&mut self, location: Location) {
Expand Down Expand Up @@ -844,6 +862,24 @@ impl<'tcx> Lvalue<'tcx> {
elem: elem,
}))
}

pub fn from_local(mir: &Mir<'tcx>, local: Local) -> Lvalue<'tcx> {
let mut index = local.index();
index = match index.checked_sub(mir.arg_decls.len()) {
None => return Lvalue::Arg(Arg(index as u32)),
Some(index) => index,
};
index = match index.checked_sub(mir.var_decls.len()) {
None => return Lvalue::Var(Var(index as u32)),
Some(index) => index,
};
index = match index.checked_sub(mir.temp_decls.len()) {
None => return Lvalue::Temp(Temp(index as u32)),
Some(index) => index,
};
debug_assert!(index == 0);
Lvalue::ReturnPointer
}
}

impl<'tcx> Debug for Lvalue<'tcx> {
Expand Down Expand Up @@ -1278,3 +1314,13 @@ impl fmt::Debug for Location {
write!(fmt, "{:?}[{}]", self.block, self.statement_index)
}
}

impl Location {
pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
if self.block == other.block {
self.statement_index <= other.statement_index
} else {
dominators.is_dominated_by(other.block, self.block)
}
}
}
108 changes: 103 additions & 5 deletions src/librustc/mir/visit.rs
Expand Up @@ -150,7 +150,7 @@ macro_rules! make_mir_visitor {

fn visit_lvalue(&mut self,
lvalue: & $($mutability)* Lvalue<'tcx>,
context: LvalueContext,
context: LvalueContext<'tcx>,
location: Location) {
self.super_lvalue(lvalue, context, location);
}
Expand Down Expand Up @@ -581,7 +581,7 @@ macro_rules! make_mir_visitor {

fn super_lvalue(&mut self,
lvalue: & $($mutability)* Lvalue<'tcx>,
context: LvalueContext,
context: LvalueContext<'tcx>,
location: Location) {
match *lvalue {
Lvalue::Var(_) |
Expand All @@ -606,7 +606,12 @@ macro_rules! make_mir_visitor {
ref $($mutability)* base,
ref $($mutability)* elem,
} = *proj;
self.visit_lvalue(base, LvalueContext::Projection, location);
let context = if context.is_mutating_use() {
LvalueContext::Projection(Mutability::Mut)
} else {
LvalueContext::Projection(Mutability::Not)
};
self.visit_lvalue(base, context, location);
self.visit_projection_elem(elem, context, location);
}

Expand Down Expand Up @@ -751,6 +756,21 @@ macro_rules! make_mir_visitor {

fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
}

// Convenience methods

fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Location) {
let basic_block = & $($mutability)* mir[location.block];
if basic_block.statements.len() == location.statement_index {
if let Some(ref $($mutability)* terminator) = basic_block.terminator {
self.visit_terminator(location.block, terminator, location)
}
} else {
let statement = & $($mutability)*
basic_block.statements[location.statement_index];
self.visit_statement(location.block, statement, location)
}
}
}
}
}
Expand All @@ -775,8 +795,20 @@ pub enum LvalueContext<'tcx> {
// Being borrowed
Borrow { region: &'tcx Region, kind: BorrowKind },

// Used as base for another lvalue, e.g. `x` in `x.y`
Projection,
// Used as base for another lvalue, e.g. `x` in `x.y`.
//
// The `Mutability` argument specifies whether the projection is being performed in order to
// (potentially) mutate the lvalue. For example, the projection `x.y` is marked as a mutation
// in these cases:
//
// x.y = ...;
// f(&mut x.y);
//
// But not in these cases:
//
// z = x.y;
// f(&x.y);
Projection(Mutability),

// Consumed as part of an operand
Consume,
Expand All @@ -785,3 +817,69 @@ pub enum LvalueContext<'tcx> {
StorageLive,
StorageDead,
}

impl<'tcx> LvalueContext<'tcx> {
/// Returns true if this lvalue context represents a drop.
pub fn is_drop(&self) -> bool {
match *self {
LvalueContext::Drop => true,
_ => false,
}
}

/// Returns true if this lvalue context represents a storage live or storage dead marker.
pub fn is_storage_marker(&self) -> bool {
match *self {
LvalueContext::StorageLive | LvalueContext::StorageDead => true,
_ => false,
}
}

/// Returns true if this lvalue context represents a storage live marker.
pub fn is_storage_live_marker(&self) -> bool {
match *self {
LvalueContext::StorageLive => true,
_ => false,
}
}

/// Returns true if this lvalue context represents a storage dead marker.
pub fn is_storage_dead_marker(&self) -> bool {
match *self {
LvalueContext::StorageDead => true,
_ => false,
}
}

/// Returns true if this lvalue context represents a use that potentially changes the value.
pub fn is_mutating_use(&self) -> bool {
match *self {
LvalueContext::Store | LvalueContext::Call |
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } |
LvalueContext::Projection(Mutability::Mut) |
LvalueContext::Drop => true,
LvalueContext::Inspect |
LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume |
LvalueContext::StorageLive | LvalueContext::StorageDead => false,
}
}

/// Returns true if this lvalue context represents a use that does not change the value.
pub fn is_nonmutating_use(&self) -> bool {
match *self {
LvalueContext::Inspect | LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true,
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store |
LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) |
LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead => false,
}
}

pub fn is_use(&self) -> bool {
self.is_mutating_use() || self.is_nonmutating_use()
}
}

1 change: 1 addition & 0 deletions src/librustc_borrowck/borrowck/mir/gather_moves.rs
Expand Up @@ -438,6 +438,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
span_bug!(stmt.source_info.span,
"SetDiscriminant should not exist during borrowck");
}
StatementKind::Nop => {}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_driver/driver.rs
Expand Up @@ -1028,6 +1028,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// No lifetime analysis based on borrowing can be done from here on out.
passes.push_pass(box mir::transform::instcombine::InstCombine::new());
passes.push_pass(box mir::transform::deaggregator::Deaggregator);
passes.push_pass(box mir::transform::copy_prop::CopyPropagation);

passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
Expand Down

0 comments on commit 480287e

Please sign in to comment.