Skip to content

Commit c772948

Browse files
authored
Auto merge of #36388 - pcwalton:copy-propagation, r=nikomatsakis
librustc_mir: 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. Currently, some random unrelated MIR tests are failing, probably just because they haven't been updated. r? @eddyb
2 parents 2c2552b + 480287e commit c772948

File tree

20 files changed

+593
-37
lines changed

20 files changed

+593
-37
lines changed

src/librustc/mir/repr.rs

+66
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,32 @@ impl<'tcx> Mir<'tcx> {
187187
self.var_decls.len() +
188188
self.temp_decls.len() + 1
189189
}
190+
191+
pub fn format_local(&self, local: Local) -> String {
192+
let mut index = local.index();
193+
index = match index.checked_sub(self.arg_decls.len()) {
194+
None => return format!("{:?}", Arg::new(index)),
195+
Some(index) => index,
196+
};
197+
index = match index.checked_sub(self.var_decls.len()) {
198+
None => return format!("{:?}", Var::new(index)),
199+
Some(index) => index,
200+
};
201+
index = match index.checked_sub(self.temp_decls.len()) {
202+
None => return format!("{:?}", Temp::new(index)),
203+
Some(index) => index,
204+
};
205+
debug_assert!(index == 0);
206+
return "ReturnPointer".to_string()
207+
}
208+
209+
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
210+
/// invalidating statement indices in `Location`s.
211+
pub fn make_statement_nop(&mut self, location: Location) {
212+
let block = &mut self[location.block];
213+
debug_assert!(location.statement_index < block.statements.len());
214+
block.statements[location.statement_index].make_nop()
215+
}
190216
}
191217

192218
impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
@@ -686,6 +712,14 @@ pub struct Statement<'tcx> {
686712
pub kind: StatementKind<'tcx>,
687713
}
688714

715+
impl<'tcx> Statement<'tcx> {
716+
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
717+
/// invalidating statement indices in `Location`s.
718+
pub fn make_nop(&mut self) {
719+
self.kind = StatementKind::Nop
720+
}
721+
}
722+
689723
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
690724
pub enum StatementKind<'tcx> {
691725
/// Write the RHS Rvalue to the LHS Lvalue.
@@ -699,6 +733,9 @@ pub enum StatementKind<'tcx> {
699733

700734
/// End the current live range for the storage of the local.
701735
StorageDead(Lvalue<'tcx>),
736+
737+
/// No-op. Useful for deleting instructions without affecting statement indices.
738+
Nop,
702739
}
703740

704741
impl<'tcx> Debug for Statement<'tcx> {
@@ -711,6 +748,7 @@ impl<'tcx> Debug for Statement<'tcx> {
711748
SetDiscriminant{lvalue: ref lv, variant_index: index} => {
712749
write!(fmt, "discriminant({:?}) = {:?}", lv, index)
713750
}
751+
Nop => write!(fmt, "nop"),
714752
}
715753
}
716754
}
@@ -824,6 +862,24 @@ impl<'tcx> Lvalue<'tcx> {
824862
elem: elem,
825863
}))
826864
}
865+
866+
pub fn from_local(mir: &Mir<'tcx>, local: Local) -> Lvalue<'tcx> {
867+
let mut index = local.index();
868+
index = match index.checked_sub(mir.arg_decls.len()) {
869+
None => return Lvalue::Arg(Arg(index as u32)),
870+
Some(index) => index,
871+
};
872+
index = match index.checked_sub(mir.var_decls.len()) {
873+
None => return Lvalue::Var(Var(index as u32)),
874+
Some(index) => index,
875+
};
876+
index = match index.checked_sub(mir.temp_decls.len()) {
877+
None => return Lvalue::Temp(Temp(index as u32)),
878+
Some(index) => index,
879+
};
880+
debug_assert!(index == 0);
881+
Lvalue::ReturnPointer
882+
}
827883
}
828884

829885
impl<'tcx> Debug for Lvalue<'tcx> {
@@ -1258,3 +1314,13 @@ impl fmt::Debug for Location {
12581314
write!(fmt, "{:?}[{}]", self.block, self.statement_index)
12591315
}
12601316
}
1317+
1318+
impl Location {
1319+
pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
1320+
if self.block == other.block {
1321+
self.statement_index <= other.statement_index
1322+
} else {
1323+
dominators.is_dominated_by(other.block, self.block)
1324+
}
1325+
}
1326+
}

src/librustc/mir/visit.rs

+104-5
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ macro_rules! make_mir_visitor {
150150

151151
fn visit_lvalue(&mut self,
152152
lvalue: & $($mutability)* Lvalue<'tcx>,
153-
context: LvalueContext,
153+
context: LvalueContext<'tcx>,
154154
location: Location) {
155155
self.super_lvalue(lvalue, context, location);
156156
}
@@ -346,6 +346,7 @@ macro_rules! make_mir_visitor {
346346
StatementKind::StorageDead(ref $($mutability)* lvalue) => {
347347
self.visit_lvalue(lvalue, LvalueContext::StorageDead, location);
348348
}
349+
StatementKind::Nop => {}
349350
}
350351
}
351352

@@ -580,7 +581,7 @@ macro_rules! make_mir_visitor {
580581

581582
fn super_lvalue(&mut self,
582583
lvalue: & $($mutability)* Lvalue<'tcx>,
583-
context: LvalueContext,
584+
context: LvalueContext<'tcx>,
584585
location: Location) {
585586
match *lvalue {
586587
Lvalue::Var(_) |
@@ -605,7 +606,12 @@ macro_rules! make_mir_visitor {
605606
ref $($mutability)* base,
606607
ref $($mutability)* elem,
607608
} = *proj;
608-
self.visit_lvalue(base, LvalueContext::Projection, location);
609+
let context = if context.is_mutating_use() {
610+
LvalueContext::Projection(Mutability::Mut)
611+
} else {
612+
LvalueContext::Projection(Mutability::Not)
613+
};
614+
self.visit_lvalue(base, context, location);
609615
self.visit_projection_elem(elem, context, location);
610616
}
611617

@@ -750,6 +756,21 @@ macro_rules! make_mir_visitor {
750756

751757
fn super_const_usize(&mut self, _substs: & $($mutability)* ConstUsize) {
752758
}
759+
760+
// Convenience methods
761+
762+
fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Location) {
763+
let basic_block = & $($mutability)* mir[location.block];
764+
if basic_block.statements.len() == location.statement_index {
765+
if let Some(ref $($mutability)* terminator) = basic_block.terminator {
766+
self.visit_terminator(location.block, terminator, location)
767+
}
768+
} else {
769+
let statement = & $($mutability)*
770+
basic_block.statements[location.statement_index];
771+
self.visit_statement(location.block, statement, location)
772+
}
773+
}
753774
}
754775
}
755776
}
@@ -774,8 +795,20 @@ pub enum LvalueContext<'tcx> {
774795
// Being borrowed
775796
Borrow { region: &'tcx Region, kind: BorrowKind },
776797

777-
// Used as base for another lvalue, e.g. `x` in `x.y`
778-
Projection,
798+
// Used as base for another lvalue, e.g. `x` in `x.y`.
799+
//
800+
// The `Mutability` argument specifies whether the projection is being performed in order to
801+
// (potentially) mutate the lvalue. For example, the projection `x.y` is marked as a mutation
802+
// in these cases:
803+
//
804+
// x.y = ...;
805+
// f(&mut x.y);
806+
//
807+
// But not in these cases:
808+
//
809+
// z = x.y;
810+
// f(&x.y);
811+
Projection(Mutability),
779812

780813
// Consumed as part of an operand
781814
Consume,
@@ -784,3 +817,69 @@ pub enum LvalueContext<'tcx> {
784817
StorageLive,
785818
StorageDead,
786819
}
820+
821+
impl<'tcx> LvalueContext<'tcx> {
822+
/// Returns true if this lvalue context represents a drop.
823+
pub fn is_drop(&self) -> bool {
824+
match *self {
825+
LvalueContext::Drop => true,
826+
_ => false,
827+
}
828+
}
829+
830+
/// Returns true if this lvalue context represents a storage live or storage dead marker.
831+
pub fn is_storage_marker(&self) -> bool {
832+
match *self {
833+
LvalueContext::StorageLive | LvalueContext::StorageDead => true,
834+
_ => false,
835+
}
836+
}
837+
838+
/// Returns true if this lvalue context represents a storage live marker.
839+
pub fn is_storage_live_marker(&self) -> bool {
840+
match *self {
841+
LvalueContext::StorageLive => true,
842+
_ => false,
843+
}
844+
}
845+
846+
/// Returns true if this lvalue context represents a storage dead marker.
847+
pub fn is_storage_dead_marker(&self) -> bool {
848+
match *self {
849+
LvalueContext::StorageDead => true,
850+
_ => false,
851+
}
852+
}
853+
854+
/// Returns true if this lvalue context represents a use that potentially changes the value.
855+
pub fn is_mutating_use(&self) -> bool {
856+
match *self {
857+
LvalueContext::Store | LvalueContext::Call |
858+
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } |
859+
LvalueContext::Projection(Mutability::Mut) |
860+
LvalueContext::Drop => true,
861+
LvalueContext::Inspect |
862+
LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
863+
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
864+
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume |
865+
LvalueContext::StorageLive | LvalueContext::StorageDead => false,
866+
}
867+
}
868+
869+
/// Returns true if this lvalue context represents a use that does not change the value.
870+
pub fn is_nonmutating_use(&self) -> bool {
871+
match *self {
872+
LvalueContext::Inspect | LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
873+
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
874+
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true,
875+
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store |
876+
LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) |
877+
LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead => false,
878+
}
879+
}
880+
881+
pub fn is_use(&self) -> bool {
882+
self.is_mutating_use() || self.is_nonmutating_use()
883+
}
884+
}
885+

src/librustc_borrowck/borrowck/mir/dataflow/impls.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
455455
});
456456
}
457457
repr::StatementKind::StorageLive(_) |
458-
repr::StatementKind::StorageDead(_) => {}
458+
repr::StatementKind::StorageDead(_) |
459+
repr::StatementKind::Nop => {}
459460
}
460461
}
461462

src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
105105
(lvalue, rvalue)
106106
}
107107
repr::StatementKind::StorageLive(_) |
108-
repr::StatementKind::StorageDead(_) => continue,
108+
repr::StatementKind::StorageDead(_) |
109+
repr::StatementKind::Nop => continue,
109110
repr::StatementKind::SetDiscriminant{ .. } =>
110111
span_bug!(stmt.source_info.span,
111112
"sanity_check should run before Deaggregator inserts SetDiscriminant"),

src/librustc_borrowck/borrowck/mir/gather_moves.rs

+1
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
438438
span_bug!(stmt.source_info.span,
439439
"SetDiscriminant should not exist during borrowck");
440440
}
441+
StatementKind::Nop => {}
441442
}
442443
}
443444

src/librustc_borrowck/borrowck/mir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,8 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
389389
|moi| callback(moi, DropFlagState::Present))
390390
}
391391
repr::StatementKind::StorageLive(_) |
392-
repr::StatementKind::StorageDead(_) => {}
392+
repr::StatementKind::StorageDead(_) |
393+
repr::StatementKind::Nop => {}
393394
},
394395
None => {
395396
debug!("drop_flag_effects: replace {:?}", block.terminator());

src/librustc_driver/driver.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10281028
// No lifetime analysis based on borrowing can be done from here on out.
10291029
passes.push_pass(box mir::transform::instcombine::InstCombine::new());
10301030
passes.push_pass(box mir::transform::deaggregator::Deaggregator);
1031+
passes.push_pass(box mir::transform::copy_prop::CopyPropagation);
10311032

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

0 commit comments

Comments
 (0)