mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
librustc: Implement def-use chains and trivial copy propagation on MIR.
This only supports trivial cases in which there is exactly one def and one use.
This commit is contained in:
parent
2e6a91812c
commit
480287ec3b
@ -188,6 +188,24 @@ impl<'tcx> Mir<'tcx> {
|
|||||||
self.temp_decls.len() + 1
|
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
|
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
|
||||||
/// invalidating statement indices in `Location`s.
|
/// invalidating statement indices in `Location`s.
|
||||||
pub fn make_statement_nop(&mut self, location: Location) {
|
pub fn make_statement_nop(&mut self, location: Location) {
|
||||||
@ -844,6 +862,24 @@ impl<'tcx> Lvalue<'tcx> {
|
|||||||
elem: elem,
|
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> {
|
impl<'tcx> Debug for Lvalue<'tcx> {
|
||||||
@ -1278,3 +1314,13 @@ impl fmt::Debug for Location {
|
|||||||
write!(fmt, "{:?}[{}]", self.block, self.statement_index)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -150,7 +150,7 @@ macro_rules! make_mir_visitor {
|
|||||||
|
|
||||||
fn visit_lvalue(&mut self,
|
fn visit_lvalue(&mut self,
|
||||||
lvalue: & $($mutability)* Lvalue<'tcx>,
|
lvalue: & $($mutability)* Lvalue<'tcx>,
|
||||||
context: LvalueContext,
|
context: LvalueContext<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
self.super_lvalue(lvalue, context, location);
|
self.super_lvalue(lvalue, context, location);
|
||||||
}
|
}
|
||||||
@ -581,7 +581,7 @@ macro_rules! make_mir_visitor {
|
|||||||
|
|
||||||
fn super_lvalue(&mut self,
|
fn super_lvalue(&mut self,
|
||||||
lvalue: & $($mutability)* Lvalue<'tcx>,
|
lvalue: & $($mutability)* Lvalue<'tcx>,
|
||||||
context: LvalueContext,
|
context: LvalueContext<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
match *lvalue {
|
match *lvalue {
|
||||||
Lvalue::Var(_) |
|
Lvalue::Var(_) |
|
||||||
@ -606,7 +606,12 @@ macro_rules! make_mir_visitor {
|
|||||||
ref $($mutability)* base,
|
ref $($mutability)* base,
|
||||||
ref $($mutability)* elem,
|
ref $($mutability)* elem,
|
||||||
} = *proj;
|
} = *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);
|
self.visit_projection_elem(elem, context, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,6 +756,21 @@ macro_rules! make_mir_visitor {
|
|||||||
|
|
||||||
fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -775,8 +795,20 @@ pub enum LvalueContext<'tcx> {
|
|||||||
// Being borrowed
|
// Being borrowed
|
||||||
Borrow { region: &'tcx Region, kind: BorrowKind },
|
Borrow { region: &'tcx Region, kind: BorrowKind },
|
||||||
|
|
||||||
// Used as base for another lvalue, e.g. `x` in `x.y`
|
// Used as base for another lvalue, e.g. `x` in `x.y`.
|
||||||
Projection,
|
//
|
||||||
|
// 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
|
// Consumed as part of an operand
|
||||||
Consume,
|
Consume,
|
||||||
@ -785,3 +817,69 @@ pub enum LvalueContext<'tcx> {
|
|||||||
StorageLive,
|
StorageLive,
|
||||||
StorageDead,
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -438,6 +438,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
|
|||||||
span_bug!(stmt.source_info.span,
|
span_bug!(stmt.source_info.span,
|
||||||
"SetDiscriminant should not exist during borrowck");
|
"SetDiscriminant should not exist during borrowck");
|
||||||
}
|
}
|
||||||
|
StatementKind::Nop => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
// 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::instcombine::InstCombine::new());
|
||||||
passes.push_pass(box mir::transform::deaggregator::Deaggregator);
|
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::add_call_guards::AddCallGuards);
|
||||||
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
|
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
|
||||||
|
197
src/librustc_mir/def_use.rs
Normal file
197
src/librustc_mir/def_use.rs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Def-use analysis.
|
||||||
|
|
||||||
|
use rustc::mir::repr::{Local, Location, Lvalue, Mir};
|
||||||
|
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
|
||||||
|
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
pub struct DefUseAnalysis<'tcx> {
|
||||||
|
info: IndexVec<Local, Info<'tcx>>,
|
||||||
|
mir_summary: MirSummary,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Info<'tcx> {
|
||||||
|
pub defs_and_uses: Vec<Use<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Use<'tcx> {
|
||||||
|
pub context: LvalueContext<'tcx>,
|
||||||
|
pub location: Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> DefUseAnalysis<'tcx> {
|
||||||
|
pub fn new(mir: &Mir<'tcx>) -> DefUseAnalysis<'tcx> {
|
||||||
|
DefUseAnalysis {
|
||||||
|
info: IndexVec::from_elem_n(Info::new(), mir.count_locals()),
|
||||||
|
mir_summary: MirSummary::new(mir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze(&mut self, mir: &Mir<'tcx>) {
|
||||||
|
let mut finder = DefUseFinder {
|
||||||
|
info: mem::replace(&mut self.info, IndexVec::new()),
|
||||||
|
mir_summary: self.mir_summary,
|
||||||
|
};
|
||||||
|
finder.visit_mir(mir);
|
||||||
|
self.info = finder.info
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_info(&self, local: Local) -> &Info<'tcx> {
|
||||||
|
&self.info[local]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_info_mut(&mut self, local: Local) -> &mut Info<'tcx> {
|
||||||
|
&mut self.info[local]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutate_defs_and_uses<F>(&self, local: Local, mir: &mut Mir<'tcx>, mut callback: F)
|
||||||
|
where F: for<'a> FnMut(&'a mut Lvalue<'tcx>,
|
||||||
|
LvalueContext<'tcx>,
|
||||||
|
Location) {
|
||||||
|
for lvalue_use in &self.info[local].defs_and_uses {
|
||||||
|
MutateUseVisitor::new(local,
|
||||||
|
&mut callback,
|
||||||
|
self.mir_summary,
|
||||||
|
mir).visit_location(mir, lvalue_use.location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME(pcwalton): This should update the def-use chains.
|
||||||
|
pub fn replace_all_defs_and_uses_with(&self,
|
||||||
|
local: Local,
|
||||||
|
mir: &mut Mir<'tcx>,
|
||||||
|
new_lvalue: Lvalue<'tcx>) {
|
||||||
|
self.mutate_defs_and_uses(local, mir, |lvalue, _, _| *lvalue = new_lvalue.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DefUseFinder<'tcx> {
|
||||||
|
info: IndexVec<Local, Info<'tcx>>,
|
||||||
|
mir_summary: MirSummary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> DefUseFinder<'tcx> {
|
||||||
|
fn lvalue_mut_info(&mut self, lvalue: &Lvalue<'tcx>) -> Option<&mut Info<'tcx>> {
|
||||||
|
let info = &mut self.info;
|
||||||
|
self.mir_summary.local_index(lvalue).map(move |local| &mut info[local])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for DefUseFinder<'tcx> {
|
||||||
|
fn visit_lvalue(&mut self,
|
||||||
|
lvalue: &Lvalue<'tcx>,
|
||||||
|
context: LvalueContext<'tcx>,
|
||||||
|
location: Location) {
|
||||||
|
if let Some(ref mut info) = self.lvalue_mut_info(lvalue) {
|
||||||
|
info.defs_and_uses.push(Use {
|
||||||
|
context: context,
|
||||||
|
location: location,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
self.super_lvalue(lvalue, context, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Info<'tcx> {
|
||||||
|
fn new() -> Info<'tcx> {
|
||||||
|
Info {
|
||||||
|
defs_and_uses: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn def_count(&self) -> usize {
|
||||||
|
self.defs_and_uses.iter().filter(|lvalue_use| lvalue_use.context.is_mutating_use()).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn def_count_not_including_drop(&self) -> usize {
|
||||||
|
self.defs_and_uses.iter().filter(|lvalue_use| {
|
||||||
|
lvalue_use.context.is_mutating_use() && !lvalue_use.context.is_drop()
|
||||||
|
}).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_count(&self) -> usize {
|
||||||
|
self.defs_and_uses.iter().filter(|lvalue_use| {
|
||||||
|
lvalue_use.context.is_nonmutating_use()
|
||||||
|
}).count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MutateUseVisitor<'tcx, F> {
|
||||||
|
query: Local,
|
||||||
|
callback: F,
|
||||||
|
mir_summary: MirSummary,
|
||||||
|
phantom: PhantomData<&'tcx ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx, F> MutateUseVisitor<'tcx, F> {
|
||||||
|
fn new(query: Local, callback: F, mir_summary: MirSummary, _: &Mir<'tcx>)
|
||||||
|
-> MutateUseVisitor<'tcx, F>
|
||||||
|
where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
|
||||||
|
MutateUseVisitor {
|
||||||
|
query: query,
|
||||||
|
callback: callback,
|
||||||
|
mir_summary: mir_summary,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx, F> MutVisitor<'tcx> for MutateUseVisitor<'tcx, F>
|
||||||
|
where F: for<'a> FnMut(&'a mut Lvalue<'tcx>, LvalueContext<'tcx>, Location) {
|
||||||
|
fn visit_lvalue(&mut self,
|
||||||
|
lvalue: &mut Lvalue<'tcx>,
|
||||||
|
context: LvalueContext<'tcx>,
|
||||||
|
location: Location) {
|
||||||
|
if self.mir_summary.local_index(lvalue) == Some(self.query) {
|
||||||
|
(self.callback)(lvalue, context, location)
|
||||||
|
}
|
||||||
|
self.super_lvalue(lvalue, context, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A small structure that enables various metadata of the MIR to be queried
|
||||||
|
/// without a reference to the MIR itself.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct MirSummary {
|
||||||
|
arg_count: usize,
|
||||||
|
var_count: usize,
|
||||||
|
temp_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MirSummary {
|
||||||
|
fn new(mir: &Mir) -> MirSummary {
|
||||||
|
MirSummary {
|
||||||
|
arg_count: mir.arg_decls.len(),
|
||||||
|
var_count: mir.var_decls.len(),
|
||||||
|
temp_count: mir.temp_decls.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_index<'tcx>(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
|
||||||
|
match *lvalue {
|
||||||
|
Lvalue::Arg(arg) => Some(Local::new(arg.index())),
|
||||||
|
Lvalue::Var(var) => Some(Local::new(var.index() + self.arg_count)),
|
||||||
|
Lvalue::Temp(temp) => {
|
||||||
|
Some(Local::new(temp.index() + self.arg_count + self.var_count))
|
||||||
|
}
|
||||||
|
Lvalue::ReturnPointer => {
|
||||||
|
Some(Local::new(self.arg_count + self.var_count + self.temp_count))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,8 +46,10 @@ extern crate rustc_const_eval;
|
|||||||
pub mod diagnostics;
|
pub mod diagnostics;
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
pub mod def_use;
|
||||||
pub mod graphviz;
|
pub mod graphviz;
|
||||||
mod hair;
|
mod hair;
|
||||||
pub mod mir_map;
|
pub mod mir_map;
|
||||||
pub mod pretty;
|
pub mod pretty;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
|
180
src/librustc_mir/transform/copy_prop.rs
Normal file
180
src/librustc_mir/transform/copy_prop.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Trivial copy propagation pass.
|
||||||
|
//!
|
||||||
|
//! This uses def-use analysis to remove values that have exactly one def and one use, which must
|
||||||
|
//! be an assignment.
|
||||||
|
//!
|
||||||
|
//! To give an example, we look for patterns that look like:
|
||||||
|
//!
|
||||||
|
//! DEST = SRC
|
||||||
|
//! ...
|
||||||
|
//! USE(DEST)
|
||||||
|
//!
|
||||||
|
//! where `DEST` and `SRC` are both locals of some form. We replace that with:
|
||||||
|
//!
|
||||||
|
//! NOP
|
||||||
|
//! ...
|
||||||
|
//! USE(SRC)
|
||||||
|
//!
|
||||||
|
//! The assignment `DEST = SRC` must be (a) the only mutation of `DEST` and (b) the only
|
||||||
|
//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
|
||||||
|
//! future.
|
||||||
|
|
||||||
|
use def_use::DefUseAnalysis;
|
||||||
|
use rustc::mir::repr::{Local, Lvalue, Mir, Operand, Rvalue, StatementKind};
|
||||||
|
use rustc::mir::transform::{MirPass, MirSource, Pass};
|
||||||
|
use rustc::ty::TyCtxt;
|
||||||
|
use rustc_data_structures::indexed_vec::Idx;
|
||||||
|
|
||||||
|
pub struct CopyPropagation;
|
||||||
|
|
||||||
|
impl Pass for CopyPropagation {}
|
||||||
|
|
||||||
|
impl<'tcx> MirPass<'tcx> for CopyPropagation {
|
||||||
|
fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
|
||||||
|
loop {
|
||||||
|
let mut def_use_analysis = DefUseAnalysis::new(mir);
|
||||||
|
def_use_analysis.analyze(mir);
|
||||||
|
|
||||||
|
let mut changed = false;
|
||||||
|
for dest_local_index in 0..mir.count_locals() {
|
||||||
|
let dest_local = Local::new(dest_local_index);
|
||||||
|
debug!("Considering destination local: {}", mir.format_local(dest_local));
|
||||||
|
|
||||||
|
let src_local;
|
||||||
|
let location;
|
||||||
|
{
|
||||||
|
// The destination must have exactly one def.
|
||||||
|
let dest_use_info = def_use_analysis.local_info(dest_local);
|
||||||
|
let dest_def_count = dest_use_info.def_count_not_including_drop();
|
||||||
|
if dest_def_count == 0 {
|
||||||
|
debug!(" Can't copy-propagate local: dest {} undefined",
|
||||||
|
mir.format_local(dest_local));
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dest_def_count > 1 {
|
||||||
|
debug!(" Can't copy-propagate local: dest {} defined {} times",
|
||||||
|
mir.format_local(dest_local),
|
||||||
|
dest_use_info.def_count());
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dest_use_info.use_count() == 0 {
|
||||||
|
debug!(" Can't copy-propagate local: dest {} unused",
|
||||||
|
mir.format_local(dest_local));
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let dest_lvalue_def = dest_use_info.defs_and_uses.iter().filter(|lvalue_def| {
|
||||||
|
lvalue_def.context.is_mutating_use() && !lvalue_def.context.is_drop()
|
||||||
|
}).next().unwrap();
|
||||||
|
location = dest_lvalue_def.location;
|
||||||
|
|
||||||
|
let basic_block = &mir[location.block];
|
||||||
|
let statement_index = location.statement_index;
|
||||||
|
let statement = match basic_block.statements.get(statement_index) {
|
||||||
|
Some(statement) => statement,
|
||||||
|
None => {
|
||||||
|
debug!(" Can't copy-propagate local: used in terminator");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// That use of the source must be an assignment.
|
||||||
|
let src_lvalue = match statement.kind {
|
||||||
|
StatementKind::Assign(
|
||||||
|
ref dest_lvalue,
|
||||||
|
Rvalue::Use(Operand::Consume(ref src_lvalue)))
|
||||||
|
if Some(dest_local) == mir.local_index(dest_lvalue) => {
|
||||||
|
src_lvalue
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
debug!(" Can't copy-propagate local: source use is not an \
|
||||||
|
assignment");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
src_local = match mir.local_index(src_lvalue) {
|
||||||
|
Some(src_local) => src_local,
|
||||||
|
None => {
|
||||||
|
debug!(" Can't copy-propagate local: source is not a local");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// There must be exactly one use of the source used in a statement (not in a
|
||||||
|
// terminator).
|
||||||
|
let src_use_info = def_use_analysis.local_info(src_local);
|
||||||
|
let src_use_count = src_use_info.use_count();
|
||||||
|
if src_use_count == 0 {
|
||||||
|
debug!(" Can't copy-propagate local: no uses");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if src_use_count != 1 {
|
||||||
|
debug!(" Can't copy-propagate local: {} uses", src_use_info.use_count());
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the source doesn't change in between. This is done
|
||||||
|
// conservatively for now, by ensuring that the source has exactly one
|
||||||
|
// mutation. The goal is to prevent things like:
|
||||||
|
//
|
||||||
|
// DEST = SRC;
|
||||||
|
// SRC = X;
|
||||||
|
// USE(DEST);
|
||||||
|
//
|
||||||
|
// From being misoptimized into:
|
||||||
|
//
|
||||||
|
// SRC = X;
|
||||||
|
// USE(SRC);
|
||||||
|
let src_def_count = src_use_info.def_count_not_including_drop();
|
||||||
|
if src_def_count != 1 {
|
||||||
|
debug!(" Can't copy-propagate local: {} defs of src",
|
||||||
|
src_use_info.def_count_not_including_drop());
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all checks passed, then we can eliminate the destination and the assignment.
|
||||||
|
//
|
||||||
|
// First, remove all markers.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): Don't do this. Merge live ranges instead.
|
||||||
|
debug!(" Replacing all uses of {}", mir.format_local(dest_local));
|
||||||
|
for lvalue_use in &def_use_analysis.local_info(dest_local).defs_and_uses {
|
||||||
|
if lvalue_use.context.is_storage_marker() {
|
||||||
|
mir.make_statement_nop(lvalue_use.location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for lvalue_use in &def_use_analysis.local_info(src_local).defs_and_uses {
|
||||||
|
if lvalue_use.context.is_storage_marker() {
|
||||||
|
mir.make_statement_nop(lvalue_use.location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now replace all uses of the destination local with the source local.
|
||||||
|
let src_lvalue = Lvalue::from_local(mir, src_local);
|
||||||
|
def_use_analysis.replace_all_defs_and_uses_with(dest_local, mir, src_lvalue);
|
||||||
|
|
||||||
|
// Finally, zap the now-useless assignment instruction.
|
||||||
|
mir.make_statement_nop(location);
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
// FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
|
||||||
|
// regenerating the chains.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,4 +19,4 @@ pub mod qualify_consts;
|
|||||||
pub mod dump_mir;
|
pub mod dump_mir;
|
||||||
pub mod deaggregator;
|
pub mod deaggregator;
|
||||||
pub mod instcombine;
|
pub mod instcombine;
|
||||||
|
pub mod copy_prop;
|
||||||
|
@ -328,7 +328,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
|||||||
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
|
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
|
||||||
fn visit_lvalue(&mut self,
|
fn visit_lvalue(&mut self,
|
||||||
lvalue: &mut Lvalue<'tcx>,
|
lvalue: &mut Lvalue<'tcx>,
|
||||||
context: LvalueContext,
|
context: LvalueContext<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
if let Lvalue::Temp(ref mut temp) = *lvalue {
|
if let Lvalue::Temp(ref mut temp) = *lvalue {
|
||||||
*temp = self.promote_temp(*temp);
|
*temp = self.promote_temp(*temp);
|
||||||
|
@ -475,7 +475,10 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
|||||||
/// For functions (constant or not), it also records
|
/// For functions (constant or not), it also records
|
||||||
/// candidates for promotion in promotion_candidates.
|
/// candidates for promotion in promotion_candidates.
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext, location: Location) {
|
fn visit_lvalue(&mut self,
|
||||||
|
lvalue: &Lvalue<'tcx>,
|
||||||
|
context: LvalueContext<'tcx>,
|
||||||
|
location: Location) {
|
||||||
match *lvalue {
|
match *lvalue {
|
||||||
Lvalue::Arg(_) => {
|
Lvalue::Arg(_) => {
|
||||||
self.add(Qualif::FN_ARGUMENT);
|
self.add(Qualif::FN_ARGUMENT);
|
||||||
|
@ -523,7 +523,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
|||||||
|
|
||||||
fn visit_lvalue(&mut self,
|
fn visit_lvalue(&mut self,
|
||||||
lvalue: &mir::Lvalue<'tcx>,
|
lvalue: &mir::Lvalue<'tcx>,
|
||||||
context: mir_visit::LvalueContext,
|
context: mir_visit::LvalueContext<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
debug!("visiting lvalue {:?}", *lvalue);
|
debug!("visiting lvalue {:?}", *lvalue);
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
|
|||||||
|
|
||||||
fn visit_lvalue(&mut self,
|
fn visit_lvalue(&mut self,
|
||||||
lvalue: &mir::Lvalue<'tcx>,
|
lvalue: &mir::Lvalue<'tcx>,
|
||||||
context: LvalueContext,
|
context: LvalueContext<'tcx>,
|
||||||
location: Location) {
|
location: Location) {
|
||||||
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
|
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
|
|||||||
LvalueContext::Store |
|
LvalueContext::Store |
|
||||||
LvalueContext::Inspect |
|
LvalueContext::Inspect |
|
||||||
LvalueContext::Borrow { .. } |
|
LvalueContext::Borrow { .. } |
|
||||||
LvalueContext::Projection => {
|
LvalueContext::Projection(..) => {
|
||||||
self.mark_as_lvalue(index);
|
self.mark_as_lvalue(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ fn helper(_: usize) {
|
|||||||
pub fn ref_dst(s: &[u8]) {
|
pub fn ref_dst(s: &[u8]) {
|
||||||
// We used to generate an extra alloca and memcpy to ref the dst, so check that we copy
|
// We used to generate an extra alloca and memcpy to ref the dst, so check that we copy
|
||||||
// directly to the alloca for "x"
|
// directly to the alloca for "x"
|
||||||
// CHECK: [[X0:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 0
|
// CHECK: [[X0:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %s, i32 0, i32 0
|
||||||
// CHECK: store i8* %0, i8** [[X0]]
|
// CHECK: store i8* %0, i8** [[X0]]
|
||||||
// CHECK: [[X1:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %x, i32 0, i32 1
|
// CHECK: [[X1:%[0-9]+]] = getelementptr {{.*}} { i8*, [[USIZE]] }* %s, i32 0, i32 1
|
||||||
// CHECK: store [[USIZE]] %1, [[USIZE]]* [[X1]]
|
// CHECK: store [[USIZE]] %1, [[USIZE]]* [[X1]]
|
||||||
|
|
||||||
let x = &*s;
|
let x = &*s;
|
||||||
|
@ -21,27 +21,27 @@ fn main() {
|
|||||||
// END RUST SOURCE
|
// END RUST SOURCE
|
||||||
// START rustc.node4.PreTrans.after.mir
|
// START rustc.node4.PreTrans.after.mir
|
||||||
// bb0: {
|
// bb0: {
|
||||||
// StorageLive(var0); // scope 0 at storage_ranges.rs:12:9: 12:10
|
// nop; // scope 0 at storage_ranges.rs:14:9: 14:10
|
||||||
// var0 = const 0i32; // scope 0 at storage_ranges.rs:12:13: 12:14
|
// var0 = const 0i32; // scope 0 at storage_ranges.rs:14:13: 14:14
|
||||||
// StorageLive(var1); // scope 1 at storage_ranges.rs:14:13: 14:14
|
// StorageLive(var1); // scope 1 at storage_ranges.rs:16:13: 16:14
|
||||||
// StorageLive(tmp1); // scope 1 at storage_ranges.rs:14:18: 14:25
|
// StorageLive(tmp1); // scope 1 at storage_ranges.rs:16:18: 16:25
|
||||||
// StorageLive(tmp2); // scope 1 at storage_ranges.rs:14:23: 14:24
|
// nop; // scope 1 at storage_ranges.rs:16:23: 16:24
|
||||||
// tmp2 = var0; // scope 1 at storage_ranges.rs:14:23: 14:24
|
// nop; // scope 1 at storage_ranges.rs:16:23: 16:24
|
||||||
// tmp1 = std::option::Option<i32>::Some(tmp2,); // scope 1 at storage_ranges.rs:14:18: 14:25
|
// tmp1 = std::option::Option<i32>::Some(var0,); // scope 1 at storage_ranges.rs:16:18: 16:25
|
||||||
// var1 = &tmp1; // scope 1 at storage_ranges.rs:14:17: 14:25
|
// var1 = &tmp1; // scope 1 at storage_ranges.rs:16:17: 16:25
|
||||||
// StorageDead(tmp2); // scope 1 at storage_ranges.rs:14:23: 14:24
|
// nop; // scope 1 at storage_ranges.rs:16:23: 16:24
|
||||||
// tmp0 = (); // scope 2 at storage_ranges.rs:13:5: 15:6
|
// tmp0 = (); // scope 2 at storage_ranges.rs:15:5: 17:6
|
||||||
// StorageDead(tmp1); // scope 1 at storage_ranges.rs:14:18: 14:25
|
// StorageDead(tmp1); // scope 1 at storage_ranges.rs:16:18: 16:25
|
||||||
// StorageDead(var1); // scope 1 at storage_ranges.rs:14:13: 14:14
|
// StorageDead(var1); // scope 1 at storage_ranges.rs:16:13: 16:14
|
||||||
// StorageLive(var2); // scope 1 at storage_ranges.rs:16:9: 16:10
|
// StorageLive(var2); // scope 1 at storage_ranges.rs:18:9: 18:10
|
||||||
// var2 = const 1i32; // scope 1 at storage_ranges.rs:16:13: 16:14
|
// var2 = const 1i32; // scope 1 at storage_ranges.rs:18:13: 18:14
|
||||||
// return = (); // scope 3 at storage_ranges.rs:11:11: 17:2
|
// return = (); // scope 3 at storage_ranges.rs:13:11: 19:2
|
||||||
// StorageDead(var2); // scope 1 at storage_ranges.rs:16:9: 16:10
|
// StorageDead(var2); // scope 1 at storage_ranges.rs:18:9: 18:10
|
||||||
// StorageDead(var0); // scope 0 at storage_ranges.rs:12:9: 12:10
|
// nop; // scope 0 at storage_ranges.rs:14:9: 14:10
|
||||||
// goto -> bb1; // scope 0 at storage_ranges.rs:11:1: 17:2
|
// goto -> bb1; // scope 0 at storage_ranges.rs:13:1: 19:2
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// bb1: {
|
// bb1: {
|
||||||
// return; // scope 0 at storage_ranges.rs:11:1: 17:2
|
// return; // scope 0 at storage_ranges.rs:13:1: 19:2
|
||||||
// }
|
// }
|
||||||
// END rustc.node4.PreTrans.after.mir
|
// END rustc.node4.PreTrans.after.mir
|
||||||
|
Loading…
Reference in New Issue
Block a user