Skip to content

Commit d203b9c

Browse files
committed
Comments, todos and small improvements
1 parent 2cc114c commit d203b9c

File tree

5 files changed

+202
-129
lines changed

5 files changed

+202
-129
lines changed

src/librustc_mir/transform/dataflow/combinators.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
110
use rustc::mir::repr as mir;
211

312
use std::marker::PhantomData;

src/librustc_mir/transform/dataflow/lattice.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
111
use std::fmt::{Debug, Formatter};
212
use std::collections::hash_map::Entry;
313
use std::collections::HashMap;
@@ -140,7 +150,6 @@ impl<T: Debug> Debug for WBottom<T> {
140150
type WTopBottom<T> = WTop<WBottom<T>>;
141151

142152

143-
// TODO: should have wrapper, really, letting to pick between union or intersection..
144153
/// A hashmap lattice with union join operation.
145154
impl<K, T, H> Lattice for HashMap<K, T, H>
146155
where K: Clone + Eq + ::std::hash::Hash,

src/librustc_mir/transform/dataflow/mod.rs

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//! The interleaved, speculative MIR dataflow framework. This framework is heavily inspired by
11+
//! Hoopl[1][2], and while it has diverged from its inspiration quite a bit in implementation
12+
//! aproach, the general idea stays the same.
13+
//!
14+
//! [1]: https://github.com/haskell/hoopl
15+
//! [2]: http://research.microsoft.com/en-us/um/people/simonpj/papers/c--/hoopl-haskell10.pdf
16+
117
use rustc::mir::repr as mir;
218
use rustc::mir::repr::{BasicBlock, BasicBlockData, Mir, START_BLOCK};
319
use rustc_data_structures::bitvec::BitVector;
@@ -9,19 +25,31 @@ mod lattice;
925
mod combinators;
1026

1127
pub enum StatementChange<'tcx> {
28+
/// Remove the statement being inspected.
1229
Remove,
30+
/// Replace the statement with another (or the same) one.
1331
Statement(mir::Statement<'tcx>)
32+
// FIXME: Should grow a way to do replacements with arbitrary graphs.
33+
// Fixing this needs figuring out how to represent such an arbitrary graph in a way that could
34+
// be both analysed (by and_then combinator, for example) and applied to the MIR/constructed
35+
// out from MIR. Such representation needs to allow at least:
36+
// * Adding new blocks, edges between blocks in the replacement as well as edges to the other
37+
// blocks in the MIR;
38+
// * Adding new locals and perhaps changing existing ones (e.g. the type)
1439
}
1540
pub enum TerminatorChange<'tcx> {
41+
/// Replace the terminator with another (or the same) one.
1642
Terminator(mir::Terminator<'tcx>)
43+
// FIXME: Should grow a way to do replacements with arbitrary graphs.
1744
}
1845

1946
pub trait Transfer<'tcx> {
47+
/// The lattice used with this transfer function.
2048
type Lattice: Lattice;
2149

2250
/// Return type of terminator transfer function.
2351
///
24-
/// `Vec<Lattice>` for forward analysis, `Lattice` for backward analysis.
52+
/// `Vec<Self::Lattice>` for forward analysis, `Self::Lattice` for backward analysis.
2553
type TerminatorReturn;
2654

2755
/// The transfer function which given a statement and a fact produces another fact which is
@@ -43,12 +71,19 @@ pub trait Rewrite<'tcx, T: Transfer<'tcx>> {
4371
-> TerminatorChange<'tcx>;
4472

4573
/// Combine two rewrites using RewriteAndThen combinator.
74+
///
75+
/// See documentation for the combinator for explanation of its behaviour.
4676
fn and_then<R2>(self, other: R2)
4777
-> RewriteAndThen<'tcx, T, Self, R2>
4878
where Self: Sized, R2: Rewrite<'tcx, T>
4979
{
5080
RewriteAndThen::new(self, other)
5181
}
82+
// FIXME: should gain at least these combinators:
83+
// * Fueled – only runs rewriter a set amount of times (needs saving the state of rewriter at
84+
// certain points);
85+
// * Deep – rewrite graph produced by rewriter with the same rewriter again;
86+
// * maybe a wrapper to hold a tcx?
5287
}
5388

5489
#[derive(Clone)]
@@ -61,7 +96,9 @@ pub struct Dataflow<'a, 'tcx: 'a, T, R>
6196
where T: Transfer<'tcx>, R: Rewrite<'tcx, T>
6297
{
6398
mir: &'a Mir<'tcx>,
64-
// FIXME: bitvector may not be the best choice
99+
// FIXME: bitvector may not be the best choice, I feel like using a FIFO queue should yield
100+
// better results at some cost of space use. This queue needs to be a set (no duplicate
101+
// entries), though, so plain linked-list based queue is not suitable.
65102
queue: BitVector,
66103
knowledge: IndexVec<BasicBlock, Option<Knowledge<'tcx, T::Lattice>>>,
67104
rewrite: R,
@@ -73,6 +110,7 @@ where L: Lattice,
73110
T: Transfer<'tcx, Lattice=L, TerminatorReturn=Vec<L>>,
74111
R: Rewrite<'tcx, T>
75112
{
113+
/// Execute dataflow in forward direction
76114
pub fn forward(mir: &'a Mir<'tcx>, transfer: T, rewrite: R) -> Mir<'tcx> {
77115
let block_count = mir.basic_blocks().len();
78116
let mut queue = BitVector::new(block_count);
@@ -90,7 +128,6 @@ where L: Lattice,
90128
}
91129

92130
fn forward_block(&mut self, bb: BasicBlock, mut fact: T::Lattice) {
93-
// debug!("forward dataflow analysing {:?}", block);
94131
let bb_data = &self.mir[bb];
95132
let mut new_stmts = Vec::with_capacity(bb_data.statements.len());
96133
for stmt in &bb_data.statements {
@@ -108,6 +145,11 @@ where L: Lattice,
108145
};
109146
let successors = new_term.successors().into_owned();
110147
assert!(successors.len() == new_facts.len(), "new_facts.len() must match successor count");
148+
// Replace block first and update facts after. This order is important, because updating
149+
// facts for a block invalidates the block replacement. If you consider a case like a block
150+
// having a backedge into itself, then this particular ordering will correctly invalidate
151+
// the replacement block and put this block back into the queue for repeated analysis,
152+
// whereas the swapped ordering would not invalidate the replacement at all.
111153
self.replace_block(bb, BasicBlockData {
112154
statements: new_stmts,
113155
terminator: Some(new_term),
@@ -126,6 +168,7 @@ where L: Lattice,
126168
T: Transfer<'tcx, Lattice=L, TerminatorReturn=L>,
127169
R: Rewrite<'tcx, T>
128170
{
171+
/// Execute dataflow in backward direction.
129172
pub fn backward(mir: &'a Mir<'tcx>, transfer: T, rewrite: R) -> Mir<'tcx> {
130173
let block_count = mir.basic_blocks().len();
131174
let mut queue = BitVector::new(block_count);
@@ -184,9 +227,9 @@ where T: Transfer<'tcx>,
184227
// problematic, consider a constant propagation pass for a loop.
185228
//
186229
// → --- { idx = 1 } ---
187-
// | idx = idx + 1 # replaced with `idx = 2`
188-
// | if(...) break; # consider else branch taken
189-
// ↑ --- { idx = 2 } ---
230+
// | idx = idx + 1 # replaced with `idx = 2`
231+
// | if(...) break; # consider else branch taken
232+
// ↑ --- { idx = 2 } --- # backedge to the top
190233
//
191234
// Here once we analyse the backedge the fact {idx = 1} is joined with fact {idx = 2}
192235
// producing a Top ({idx = ⊤}) and rendering our replacement of `idx = idx + 1` with `idx =
@@ -208,15 +251,15 @@ where T: Transfer<'tcx>,
208251
fn update_fact(&mut self, bb: BasicBlock, new_fact: T::Lattice) -> bool {
209252
match self.knowledge[bb] {
210253
ref mut val@None => {
211-
// In case of no prior knowledge about this block, we essentially introduce new
212-
// data and thus return true.
254+
// In case of no prior knowledge about this block, it means we are introducing new
255+
// knowledge, and therefore, must return true.
213256
*val = Some(Knowledge { fact: new_fact, new_block: None });
214257
true
215258
},
216259
Some(Knowledge { ref mut fact, ref mut new_block }) => {
217260
let join = T::Lattice::join(fact, new_fact);
218-
// In case of some prior knowledge and provided our knowledge changed, we must
219-
// invalidate any new_block that could exist.
261+
// In case of some prior knowledge and provided our knowledge changed, we should
262+
// invalidate any replacement block that could already exist.
220263
if join {
221264
*new_block = None;
222265
}
@@ -235,6 +278,7 @@ where T: Transfer<'tcx>,
235278
}
236279
}
237280

281+
/// Build the new MIR by combining the replacement blocks and original MIR into a clone.
238282
fn construct_mir(mut self) -> Mir<'tcx> {
239283
let mut new_blocks = IndexVec::with_capacity(self.mir.basic_blocks().len());
240284
for (block, old_data) in self.mir.basic_blocks().iter_enumerated() {
@@ -261,8 +305,8 @@ where T: Transfer<'tcx>,
261305
}
262306

263307
fn mir_exits<'tcx>(mir: &Mir<'tcx>, exits: &mut BitVector) {
264-
// Do this smartly. First of all, find all the nodes without successors (these are guaranteed
265-
// exit nodes).
308+
// Do this smartly (cough… using bruteforce… cough). First of all, find all the nodes without
309+
// successors. These are guaranteed exit nodes.
266310
let mir_len = mir.basic_blocks().len();
267311
let mut lead_to_exit = BitVector::new(mir_len);
268312
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
@@ -284,8 +328,8 @@ fn mir_exits<'tcx>(mir: &Mir<'tcx>, exits: &mut BitVector) {
284328
// ```
285329
//
286330
// make it considerably more complex. In the end, it doesn’t matter very much which node we
287-
// pick here, as picking any node does not make analysis incorrect, and a bad choice will only
288-
// result in dataflow doing an extra pass over some of the blocks.
331+
// pick here. Picking any node inside the loop will make dataflow visit all the nodes, only
332+
// potentially doing an extra pass or two on a few blocks.
289333
lead_to_exit.invert();
290334
while let Some(exit) = lead_to_exit.pop() {
291335
if exit >= mir_len { continue }

src/librustc_mir/transform/liveness.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
110
use rustc_data_structures::bitvec::BitVector;
211
use rustc_data_structures::indexed_vec::Idx;
312
use rustc::mir::repr::*;

0 commit comments

Comments
 (0)