Skip to content

Commit ccdc81a

Browse files
committed
Add a MIR dataflow framework (again)
This time I opted for scaling back the list of features I intend to implement in order to get this stuff land at all (or at least before 2020). Namely the list of features which got cut: * Rewrites with arbitrary subgraphs. Instead this only implements a way to either remove a statement/terminator or replace it with another (or same) statement/terminator. * Fueled rewriters, deep rewriters, a bunch of combinators; * … I feel like there were many more, but 2AM does not blend well with trying to remember stuff. Also includes a nicely working liveness pass. This pass removes all moves into unused variables and thus allows to not translate a lot of unnecessary variables. This, even without move propagation of some sort, should help a bit with translation times and maybe stack usage (esp. in debug builds). This liveness pass is also a viable option for cleanup after other passes which would like to leave stuff behind them (such as the move propagation pass). Finally, the pass demonstrates the *backward* analysis, and also the bruteforce-ish algorithm to discover all graph exits from MIR :)
1 parent 34d0545 commit ccdc81a

File tree

18 files changed

+844
-108
lines changed

18 files changed

+844
-108
lines changed

src/librustc/mir/repr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use super::cache::Cache;
3636
macro_rules! newtype_index {
3737
($name:ident, $debug_name:expr) => (
3838
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
39-
RustcEncodable, RustcDecodable)]
39+
RustcEncodable, RustcDecodable)]
4040
pub struct $name(u32);
4141

4242
impl Idx for $name {
@@ -725,7 +725,7 @@ newtype_index!(Local, "local");
725725

726726
/// A path to a value; something that can be evaluated without
727727
/// changing or disturbing program state.
728-
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
728+
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
729729
pub enum Lvalue<'tcx> {
730730
/// local variable declared by the user
731731
Var(Var),
@@ -893,7 +893,7 @@ pub struct VisibilityScopeData {
893893
/// These are values that can appear inside an rvalue (or an index
894894
/// lvalue). They are intentionally limited to prevent rvalues from
895895
/// being nested in one another.
896-
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
896+
#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
897897
pub enum Operand<'tcx> {
898898
Consume(Lvalue<'tcx>),
899899
Constant(Constant<'tcx>),

src/librustc/mir/tcx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<'a, 'gcx, 'tcx> LvalueTy<'tcx> {
8383
variant_index: index }
8484
}
8585
_ => {
86-
bug!("cannot downcast non-enum type: `{:?}`", self)
86+
bug!("cannot downcast non-enum type: `{:?}` as `{:?}`", self, elem)
8787
}
8888
},
8989
ProjectionElem::Field(_, fty) => LvalueTy::Ty { ty: fty }

src/librustc/mir/visit.rs

Lines changed: 81 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,69 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
//! # The MIR Visitor
11+
//!
12+
//! ## Overview
13+
//!
14+
//! There are two visitors, one for immutable and one for mutable references,
15+
//! but both are generated by the following macro. The code is written according
16+
//! to the following conventions:
17+
//!
18+
//! - introduce a `visit_foo` and a `super_foo` method for every MIR type
19+
//! - `visit_foo`, by default, calls `super_foo`
20+
//! - `super_foo`, by default, destructures the `foo` and calls `visit_foo`
21+
//!
22+
//! This allows you as a user to override `visit_foo` for types are
23+
//! interested in, and invoke (within that method) call
24+
//! `self.super_foo` to get the default behavior. Just as in an OO
25+
//! language, you should never call `super` methods ordinarily except
26+
//! in that circumstance.
27+
//!
28+
//! For the most part, we do not destructure things external to the
29+
//! MIR, e.g. types, spans, etc, but simply visit them and stop. This
30+
//! avoids duplication with other visitors like `TypeFoldable`. But
31+
//! there is one exception: we do destructure the `FnOutput` to reach
32+
//! the type within. Just because.
33+
//!
34+
//! ## Updating
35+
//!
36+
//! The code is written in a very deliberate style intended to minimize
37+
//! the chance of things being overlooked. You'll notice that we always
38+
//! use pattern matching to reference fields and we ensure that all
39+
//! matches are exhaustive.
40+
//!
41+
//! For example, the `super_basic_block_data` method begins like this:
42+
//!
43+
//! ```rust
44+
//! fn super_basic_block_data(&mut self,
45+
//! block: BasicBlock,
46+
//! data: & $($mutability)* BasicBlockData<'tcx>) {
47+
//! let BasicBlockData {
48+
//! ref $($mutability)* statements,
49+
//! ref $($mutability)* terminator,
50+
//! is_cleanup: _
51+
//! } = *data;
52+
//!
53+
//! for statement in statements {
54+
//! self.visit_statement(block, statement);
55+
//! }
56+
//!
57+
//! ...
58+
//! }
59+
//! ```
60+
//!
61+
//! Here we used `let BasicBlockData { <fields> } = *data` deliberately,
62+
//! rather than writing `data.statements` in the body. This is because if one
63+
//! adds a new field to `BasicBlockData`, one will be forced to revise this code,
64+
//! and hence one will (hopefully) invoke the correct visit methods (if any).
65+
//!
66+
//! For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
67+
//! That means you never write `..` to skip over fields, nor do you write `_`
68+
//! to skip over variants in a `match`.
69+
//!
70+
//! The only place that `_` is acceptable is to match a field (or
71+
//! variant argument) that does not require visiting, as in
72+
//! `is_cleanup` above.
1073
1174
use middle::const_val::ConstVal;
1275
use hir::def_id::DefId;
@@ -18,68 +81,6 @@ use rustc_data_structures::tuple_slice::TupleSlice;
1881
use rustc_data_structures::indexed_vec::Idx;
1982
use syntax_pos::Span;
2083

21-
// # The MIR Visitor
22-
//
23-
// ## Overview
24-
//
25-
// There are two visitors, one for immutable and one for mutable references,
26-
// but both are generated by the following macro. The code is written according
27-
// to the following conventions:
28-
//
29-
// - introduce a `visit_foo` and a `super_foo` method for every MIR type
30-
// - `visit_foo`, by default, calls `super_foo`
31-
// - `super_foo`, by default, destructures the `foo` and calls `visit_foo`
32-
//
33-
// This allows you as a user to override `visit_foo` for types are
34-
// interested in, and invoke (within that method) call
35-
// `self.super_foo` to get the default behavior. Just as in an OO
36-
// language, you should never call `super` methods ordinarily except
37-
// in that circumstance.
38-
//
39-
// For the most part, we do not destructure things external to the
40-
// MIR, e.g. types, spans, etc, but simply visit them and stop. This
41-
// avoids duplication with other visitors like `TypeFoldable`.
42-
//
43-
// ## Updating
44-
//
45-
// The code is written in a very deliberate style intended to minimize
46-
// the chance of things being overlooked. You'll notice that we always
47-
// use pattern matching to reference fields and we ensure that all
48-
// matches are exhaustive.
49-
//
50-
// For example, the `super_basic_block_data` method begins like this:
51-
//
52-
// ```rust
53-
// fn super_basic_block_data(&mut self,
54-
// block: BasicBlock,
55-
// data: & $($mutability)* BasicBlockData<'tcx>) {
56-
// let BasicBlockData {
57-
// ref $($mutability)* statements,
58-
// ref $($mutability)* terminator,
59-
// is_cleanup: _
60-
// } = *data;
61-
//
62-
// for statement in statements {
63-
// self.visit_statement(block, statement);
64-
// }
65-
//
66-
// ...
67-
// }
68-
// ```
69-
//
70-
// Here we used `let BasicBlockData { <fields> } = *data` deliberately,
71-
// rather than writing `data.statements` in the body. This is because if one
72-
// adds a new field to `BasicBlockData`, one will be forced to revise this code,
73-
// and hence one will (hopefully) invoke the correct visit methods (if any).
74-
//
75-
// For this to work, ALL MATCHES MUST BE EXHAUSTIVE IN FIELDS AND VARIANTS.
76-
// That means you never write `..` to skip over fields, nor do you write `_`
77-
// to skip over variants in a `match`.
78-
//
79-
// The only place that `_` is acceptable is to match a field (or
80-
// variant argument) that does not require visiting, as in
81-
// `is_cleanup` above.
82-
8384
macro_rules! make_mir_visitor {
8485
($visitor_trait_name:ident, $($mutability:ident)*) => {
8586
pub trait $visitor_trait_name<'tcx> {
@@ -419,7 +420,7 @@ macro_rules! make_mir_visitor {
419420
self.visit_operand(arg);
420421
}
421422
if let Some((ref $($mutability)* destination, target)) = *destination {
422-
self.visit_lvalue(destination, LvalueContext::Call);
423+
self.visit_lvalue(destination, LvalueContext::CallStore);
423424
self.visit_branch(block, target);
424425
}
425426
cleanup.map(|t| self.visit_branch(block, t));
@@ -529,7 +530,7 @@ macro_rules! make_mir_visitor {
529530
ref $($mutability)* inputs,
530531
asm: _ } => {
531532
for output in & $($mutability)* outputs[..] {
532-
self.visit_lvalue(output, LvalueContext::Store);
533+
self.visit_lvalue(output, LvalueContext::AsmOutput);
533534
}
534535
for input in & $($mutability)* inputs[..] {
535536
self.visit_operand(input);
@@ -723,33 +724,38 @@ macro_rules! make_mir_visitor {
723724
make_mir_visitor!(Visitor,);
724725
make_mir_visitor!(MutVisitor,mut);
725726

726-
#[derive(Copy, Clone, Debug)]
727+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
727728
pub enum LvalueContext {
728-
// Appears as LHS of an assignment
729+
/// Appears as LHS of an assignment
729730
Store,
730731

731-
// Dest of a call
732-
Call,
732+
/// Dest of a call
733+
CallStore,
733734

734-
// Being dropped
735+
/// Being dropped
735736
Drop,
736737

737-
// Being inspected in some way, like loading a len
738+
/// Being inspected in some way, like loading a len
738739
Inspect,
739740

740-
// Being borrowed
741+
/// Being borrowed
741742
Borrow { region: Region, kind: BorrowKind },
742743

743-
// Being sliced -- this should be same as being borrowed, probably
744+
/// Being sliced -- this should be same as being borrowed, probably
744745
Slice { from_start: usize, from_end: usize },
745746

746-
// Used as base for another lvalue, e.g. `x` in `x.y`
747+
/// Used as base for another lvalue, e.g. `x` in `x.y`
747748
Projection,
748749

749-
// Consumed as part of an operand
750+
/// Consumed as part of an operand
750751
Consume,
751752

752-
// Starting and ending a storage live range
753+
/// Starting and ending a storage live range
753754
StorageLive,
754755
StorageDead,
756+
757+
/// “output” from inline asm.
758+
///
759+
/// Cannot be interpreted as a store, because assembly may choose to ignore it.
760+
AsmOutput
755761
}

src/librustc_data_structures/bitvec.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ impl BitVector {
6262
}
6363
}
6464

65-
/// Return and unset first bit set.
65+
/// Return and unset last bit set.
6666
pub fn pop(&mut self) -> Option<usize> {
67-
for (idx, el) in self.data.iter_mut().enumerate() {
67+
for (idx, el) in self.data.iter_mut().enumerate().rev() {
6868
if *el != 0 {
69-
let bit = el.trailing_zeros() as usize;
70-
*el &= !word_mask(bit).1;
69+
let bit = 63 - el.leading_zeros() as usize;
70+
*el &= !(1 << bit);
7171
return Some(idx * 64 + bit);
7272
}
7373
}
@@ -77,11 +77,12 @@ impl BitVector {
7777
/// Returns true if the bit has changed.
7878
pub fn remove(&mut self, bit: usize) -> bool {
7979
let (word, mask) = word_mask(bit);
80-
let data = &mut self.data[word];
81-
let value = *data;
82-
let new_value = value & !mask;
83-
*data = new_value;
84-
new_value != value
80+
self.data.get_mut(word).map(|data| {
81+
let value = *data;
82+
let new_value = value & !mask;
83+
*data = new_value;
84+
new_value != value
85+
}).unwrap_or(false)
8586
}
8687

8788
/// Clear the bitvector.
@@ -417,3 +418,19 @@ fn matrix_iter() {
417418
}
418419
assert!(iter.next().is_none());
419420
}
421+
422+
fn bitvec_pop() {
423+
let mut bitvec = BitVector::new(100);
424+
bitvec.insert(1);
425+
bitvec.insert(10);
426+
bitvec.insert(19);
427+
bitvec.insert(62);
428+
bitvec.insert(63);
429+
bitvec.insert(64);
430+
bitvec.insert(65);
431+
bitvec.insert(66);
432+
bitvec.insert(99);
433+
let idxs = vec![];
434+
while let Some(idx) = bitvec.pop() { idxs.push(idx); }
435+
assert_eq!(idxs, [99, 66, 65, 64, 63, 62, 19, 10, 1]);
436+
}

src/librustc_data_structures/indexed_vec.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ impl<I: Idx, T> IndexVec<I, T> {
102102
self.raw.len()
103103
}
104104

105+
#[inline]
106+
pub unsafe fn set_len(&mut self, len: usize) {
107+
self.raw.set_len(len)
108+
}
109+
105110
#[inline]
106111
pub fn is_empty(&self) -> bool {
107112
self.raw.is_empty()
@@ -149,6 +154,16 @@ impl<I: Idx, T> IndexVec<I, T> {
149154
pub fn last(&self) -> Option<I> {
150155
self.len().checked_sub(1).map(I::new)
151156
}
157+
158+
#[inline]
159+
pub unsafe fn as_ptr(&mut self) -> *const T {
160+
self.raw.as_ptr()
161+
}
162+
163+
#[inline]
164+
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
165+
self.raw.as_mut_ptr()
166+
}
152167
}
153168

154169
impl<I: Idx, T> Index<I> for IndexVec<I, T> {

src/librustc_driver/driver.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
990990
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads"));
991991

992992
passes.push_pass(box mir::transform::erase_regions::EraseRegions);
993+
passes.push_pass(box mir::transform::liveness::LocalLivenessAnalysis);
993994

994995
passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
995996
passes.push_pass(box borrowck::ElaborateDrops);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use rustc::mir::repr as mir;
2+
3+
use std::marker::PhantomData;
4+
use super::*;
5+
6+
/// This combinator has the following behaviour:
7+
///
8+
/// * Rewrite the node with the first rewriter.
9+
/// * if the first rewriter replaced the node, 2nd rewriter is used to rewrite the replacement.
10+
/// * otherwise 2nd rewriter is used to rewrite the original node.
11+
pub struct RewriteAndThen<'tcx, T, R1, R2>(R1, R2, PhantomData<(&'tcx (), T)>)
12+
where T: Transfer<'tcx>, R1: Rewrite<'tcx, T>, R2: Rewrite<'tcx, T>;
13+
14+
impl<'tcx, T, R1, R2> RewriteAndThen<'tcx, T, R1, R2>
15+
where T: Transfer<'tcx>, R1: Rewrite<'tcx, T>, R2: Rewrite<'tcx, T>
16+
{
17+
pub fn new(r1: R1, r2: R2) -> RewriteAndThen<'tcx, T, R1, R2> {
18+
RewriteAndThen(r1, r2, PhantomData)
19+
}
20+
}
21+
22+
impl<'tcx, T, R1, R2> Rewrite<'tcx, T> for RewriteAndThen<'tcx, T, R1, R2>
23+
where T: Transfer<'tcx>, R1: Rewrite<'tcx, T>, R2: Rewrite<'tcx, T> {
24+
fn stmt(&self, stmt: &mir::Statement<'tcx>, fact: &T::Lattice) -> StatementChange<'tcx> {
25+
let rs = self.0.stmt(stmt, fact);
26+
match rs {
27+
StatementChange::Remove => StatementChange::Remove,
28+
StatementChange::Statement(ns) => self.1.stmt(&ns, fact),
29+
}
30+
}
31+
32+
fn term(&self, term: &mir::Terminator<'tcx>, fact: &T::Lattice) -> TerminatorChange<'tcx> {
33+
let rt = self.0.term(term, fact);
34+
match rt {
35+
TerminatorChange::Terminator(nt) => self.1.term(&nt, fact)
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)