Skip to content

Commit b1ee6e8

Browse files
committedFeb 1, 2018
rustc_mir: add an "event saturating dataflow" framework.
1 parent c70032f commit b1ee6e8

File tree

2 files changed

+390
-0
lines changed

2 files changed

+390
-0
lines changed
 
+389
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
// Copyright 2018 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+
11+
pub use self::SeekLocation::*;
12+
13+
use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
14+
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
15+
use rustc_data_structures::bitvec::BitMatrix;
16+
use rustc::mir::*;
17+
use std::collections::{BTreeMap, VecDeque};
18+
use std::iter;
19+
use std::marker::PhantomData;
20+
use analysis::locations::{FlatLocation, FlatLocations};
21+
22+
// FIXME(eddyb) move to rustc_data_structures.
23+
#[derive(Clone)]
24+
pub struct SparseBitSet<I: Idx> {
25+
map: BTreeMap<u32, u128>,
26+
_marker: PhantomData<I>,
27+
}
28+
29+
fn key_and_mask<I: Idx>(index: I) -> (u32, u128) {
30+
let index = index.index();
31+
let key = index / 128;
32+
let key_u32 = key as u32;
33+
assert_eq!(key_u32 as usize, key);
34+
(key_u32, 1 << (index % 128))
35+
}
36+
37+
impl<I: Idx> SparseBitSet<I> {
38+
pub fn new() -> Self {
39+
SparseBitSet {
40+
map: BTreeMap::new(),
41+
_marker: PhantomData
42+
}
43+
}
44+
45+
pub fn contains(&self, index: I) -> bool {
46+
let (key, mask) = key_and_mask(index);
47+
self.map.get(&key).map_or(false, |bits| (bits & mask) != 0)
48+
}
49+
50+
pub fn insert(&mut self, index: I) -> bool {
51+
let (key, mask) = key_and_mask(index);
52+
let bits = self.map.entry(key).or_insert(0);
53+
let old_bits = *bits;
54+
let new_bits = old_bits | mask;
55+
*bits = new_bits;
56+
new_bits != old_bits
57+
}
58+
59+
pub fn remove(&mut self, index: I) -> bool {
60+
let (key, mask) = key_and_mask(index);
61+
if let Some(bits) = self.map.get_mut(&key) {
62+
let old_bits = *bits;
63+
let new_bits = old_bits & !mask;
64+
*bits = new_bits;
65+
// FIXME(eddyb) maybe remove entry if now `0`.
66+
new_bits != old_bits
67+
} else {
68+
false
69+
}
70+
}
71+
72+
pub fn iter<'a>(&'a self) -> impl Iterator<Item = I> + 'a {
73+
self.map.iter().flat_map(|(&key, &bits)| {
74+
let base = key as usize * 128;
75+
(0..128).filter_map(move |i| {
76+
if (bits & (1 << i)) != 0 {
77+
Some(I::new(base + i))
78+
} else {
79+
None
80+
}
81+
})
82+
})
83+
}
84+
}
85+
86+
/// A pair with named fields (`past` and `future`).
87+
/// Used solely to avoid mixing the two up.
88+
#[derive(Copy, Clone, Default)]
89+
pub struct PastAndFuture<P, F> {
90+
pub past: P,
91+
pub future: F
92+
}
93+
94+
/// Event (dataflow) propagation direction.
95+
/// Past events are propagated forward, while
96+
/// future events are propagated backward.
97+
pub trait Direction: 'static {
98+
const FORWARD: bool;
99+
fn each_propagation_edge<F>(mir: &Mir, from: BasicBlock, f: F)
100+
where F: FnMut(BasicBlock);
101+
fn each_block_location<F>(flat_locations: &FlatLocations, block: BasicBlock, f: F)
102+
where F: FnMut(FlatLocation);
103+
}
104+
105+
pub enum Forward {}
106+
impl Direction for Forward {
107+
const FORWARD: bool = true;
108+
fn each_propagation_edge<F>(mir: &Mir, from: BasicBlock, mut f: F)
109+
where F: FnMut(BasicBlock)
110+
{
111+
for &to in mir.basic_blocks()[from].terminator().successors().iter() {
112+
f(to);
113+
}
114+
}
115+
fn each_block_location<F>(flat_locations: &FlatLocations, block: BasicBlock, mut f: F)
116+
where F: FnMut(FlatLocation)
117+
{
118+
let range = flat_locations.block_range(block);
119+
// FIXME(eddyb) implement `Step` on `FlatLocation`.
120+
for i in range.start.index()..range.end.index() {
121+
f(FlatLocation::new(i))
122+
}
123+
}
124+
}
125+
126+
pub enum Backward {}
127+
impl Direction for Backward {
128+
const FORWARD: bool = false;
129+
fn each_propagation_edge<F>(mir: &Mir, from: BasicBlock, mut f: F)
130+
where F: FnMut(BasicBlock)
131+
{
132+
for &to in mir.predecessors_for(from).iter() {
133+
f(to);
134+
}
135+
}
136+
fn each_block_location<F>(flat_locations: &FlatLocations, block: BasicBlock, mut f: F)
137+
where F: FnMut(FlatLocation)
138+
{
139+
let range = flat_locations.block_range(block);
140+
// FIXME(eddyb) implement `Step` on `FlatLocation`.
141+
for i in (range.start.index()..range.end.index()).rev() {
142+
f(FlatLocation::new(i))
143+
}
144+
}
145+
}
146+
147+
pub struct Events<'a, 'b, 'tcx: 'a, I: Idx> {
148+
mir: &'a Mir<'tcx>,
149+
flat_locations: &'b FlatLocations,
150+
count: usize,
151+
at_location: IndexVec<FlatLocation, SparseBitSet<I>>,
152+
in_block: BitMatrix
153+
}
154+
155+
impl<'a, 'b, 'tcx, I: Idx> Events<'a, 'b, 'tcx, I> {
156+
pub fn new(mir: &'a Mir<'tcx>,
157+
flat_locations: &'b FlatLocations,
158+
count: usize)
159+
-> Self {
160+
Events {
161+
mir,
162+
flat_locations,
163+
count,
164+
at_location: IndexVec::from_elem_n(SparseBitSet::new(),
165+
flat_locations.total_count),
166+
in_block: BitMatrix::new(mir.basic_blocks().len(), count)
167+
}
168+
}
169+
170+
pub fn insert_at(&mut self, index: I, location: Location) {
171+
let flat_location = self.flat_locations.get(location);
172+
self.at_location[flat_location].insert(index);
173+
self.in_block.add(location.block.index(), index.index());
174+
}
175+
176+
pub fn flow<P>(self, entry_past: P)
177+
-> PastAndFuture<EventFlowResults<'b, Forward, I>,
178+
EventFlowResults<'b, Backward, I>>
179+
where P: Iterator<Item = I>
180+
{
181+
182+
PastAndFuture {
183+
past: self.flow_in_direction(entry_past.map(|i| (START_BLOCK, i))),
184+
future: self.flow_in_direction(iter::empty())
185+
}
186+
}
187+
188+
fn flow_in_direction<D: Direction, E>(&self, external: E)
189+
-> EventFlowResults<'b, D, I>
190+
where E: Iterator<Item = (BasicBlock, I)>
191+
{
192+
let mut queue = VecDeque::with_capacity(self.mir.basic_blocks().len());
193+
let mut enqueued = IdxSetBuf::new_filled(self.mir.basic_blocks().len());
194+
195+
// 0. Add some external events in the past/future of certain blocks.
196+
let mut into_block = BitMatrix::new(self.mir.basic_blocks().len(), self.count);
197+
for (block, i) in external {
198+
into_block.add(block.index(), i.index());
199+
}
200+
201+
// 1. Propagate `in_block` events to immediate successors/predecessors.
202+
for from in self.mir.basic_blocks().indices() {
203+
D::each_propagation_edge(&self.mir, from, |to| {
204+
// FIXME(eddyb) This could use a version of `BitMatrix::merge`
205+
// between two rows that are in diferent `BitMatrix`es.
206+
for i in self.in_block.iter(from.index()) {
207+
into_block.add(to.index(), i);
208+
}
209+
});
210+
queue.push_back(from);
211+
}
212+
213+
// 2. Propagate `into_block` events until saturation is achieved.
214+
while let Some(from) = queue.pop_front() {
215+
D::each_propagation_edge(&self.mir, from, |to| {
216+
if into_block.merge(from.index(), to.index()) {
217+
if enqueued.add(&to) {
218+
queue.push_back(to);
219+
}
220+
}
221+
});
222+
enqueued.remove(&from);
223+
}
224+
225+
// 3. Cache the difference between consecutive locations within each block.
226+
let mut out_of_block = into_block.clone();
227+
let mut diff_at_location = IndexVec::from_elem_n(SparseBitSet::new(),
228+
self.flat_locations.total_count);
229+
for block in self.mir.basic_blocks().indices() {
230+
D::each_block_location(&self.flat_locations, block, |flat_location| {
231+
let at_location = &self.at_location[flat_location];
232+
let diff = &mut diff_at_location[flat_location];
233+
// FIXME(eddyb) This could use per-"word" bitwise operations.
234+
for i in at_location.iter() {
235+
if out_of_block.add(block.index(), i.index()) {
236+
diff.insert(i);
237+
}
238+
}
239+
});
240+
}
241+
242+
let (block_entry, block_exit) = if D::FORWARD {
243+
(into_block, out_of_block)
244+
} else {
245+
(out_of_block, into_block)
246+
};
247+
248+
EventFlowResults {
249+
flat_locations: self.flat_locations,
250+
count: self.count,
251+
block_entry,
252+
block_exit,
253+
diff_at_location,
254+
_marker: PhantomData
255+
}
256+
}
257+
}
258+
259+
#[derive(Clone)]
260+
pub struct EventFlowResults<'a, D: Direction, I: Idx> {
261+
flat_locations: &'a FlatLocations,
262+
count: usize,
263+
264+
/// Bits propagated into the start of the block, from predecessors.
265+
block_entry: BitMatrix,
266+
267+
/// Bits propagated out of the end of the block, into successors.
268+
block_exit: BitMatrix,
269+
270+
/// Bits that change at each statement/terminator, because they're
271+
/// either the first occurence (in the past only after the location),
272+
/// or the last occurence (in the future only before the location).
273+
diff_at_location: IndexVec<FlatLocation, SparseBitSet<I>>,
274+
275+
_marker: PhantomData<D>
276+
}
277+
278+
impl<'a, D: Direction, I: Idx> EventFlowResults<'a, D, I> {
279+
pub fn observe(&'a self) -> Observer<'a, D, I> {
280+
Observer {
281+
results: self,
282+
location: Location {
283+
block: START_BLOCK,
284+
statement_index: !0
285+
},
286+
state_before: IdxSetBuf::new_empty(self.count),
287+
}
288+
}
289+
}
290+
291+
impl<'a, I: Idx> PastAndFuture<EventFlowResults<'a, Forward, I>,
292+
EventFlowResults<'a, Backward, I>> {
293+
pub fn observe(&'a self) -> PastAndFuture<Observer<'a, Forward, I>,
294+
Observer<'a, Backward, I>> {
295+
PastAndFuture {
296+
past: self.past.observe(),
297+
future: self.future.observe()
298+
}
299+
}
300+
}
301+
302+
pub struct Observer<'a, D: Direction, I: Idx> {
303+
results: &'a EventFlowResults<'a, D, I>,
304+
location: Location,
305+
state_before: IdxSetBuf<I>,
306+
}
307+
308+
#[derive(Copy, Clone)]
309+
pub enum SeekLocation {
310+
Before(Location),
311+
After(Location)
312+
}
313+
314+
impl<'a, D: Direction, I: Idx> Observer<'a, D, I> {
315+
pub fn seek(&mut self, to: SeekLocation) -> &IdxSet<I> {
316+
// Ensure the location is valid for a statement/terminator.
317+
match to {
318+
Before(location) | After(location) => {
319+
self.results.flat_locations.get(location);
320+
}
321+
}
322+
323+
let to = match to {
324+
Before(location) => location,
325+
After(location) => location.successor_within_block()
326+
};
327+
328+
// Seek to the start or end of the block if we were in a different one.
329+
if self.location.block != to.block || self.location.statement_index == !0 {
330+
self.state_before.clear();
331+
332+
let block_range = self.results.flat_locations.block_range(to.block);
333+
let locations_in_block = block_range.end.index() - block_range.start.index();
334+
335+
// FIXME(eddyb) These could use copies of whole rows.
336+
if to.statement_index < locations_in_block / 2 {
337+
for i in self.results.block_entry.iter(to.block.index()) {
338+
self.state_before.add(&I::new(i));
339+
}
340+
self.location.statement_index = 0;
341+
} else {
342+
for i in self.results.block_exit.iter(to.block.index()) {
343+
self.state_before.add(&I::new(i));
344+
}
345+
self.location.statement_index = locations_in_block;
346+
}
347+
self.location.block = to.block;
348+
}
349+
350+
while self.location.statement_index < to.statement_index {
351+
let flat_location = self.results.flat_locations.get(self.location);
352+
// FIXME(eddyb) These could use per-"word" bitwise operations.
353+
for i in self.results.diff_at_location[flat_location].iter() {
354+
if D::FORWARD {
355+
self.state_before.add(&i);
356+
} else {
357+
self.state_before.remove(&i);
358+
}
359+
}
360+
self.location.statement_index += 1;
361+
}
362+
363+
while self.location.statement_index > to.statement_index {
364+
self.location.statement_index -= 1;
365+
let flat_location = self.results.flat_locations.get(self.location);
366+
// FIXME(eddyb) These could use per-"word" bitwise operations.
367+
for i in self.results.diff_at_location[flat_location].iter() {
368+
if D::FORWARD {
369+
self.state_before.remove(&i);
370+
} else {
371+
self.state_before.add(&i);
372+
}
373+
}
374+
}
375+
376+
&self.state_before
377+
}
378+
}
379+
380+
impl<'a, I: Idx> PastAndFuture<Observer<'a, Forward, I>,
381+
Observer<'a, Backward, I>> {
382+
pub fn seek(&mut self, to: SeekLocation)
383+
-> PastAndFuture<&IdxSet<I>, &IdxSet<I>> {
384+
PastAndFuture {
385+
past: self.past.seek(to),
386+
future: self.future.seek(to)
387+
}
388+
}
389+
}

‎src/librustc_mir/analysis/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
pub mod alignment;
1212
pub mod dataflow;
1313
pub mod def_use;
14+
pub mod eventflow;
1415
pub mod liveness;
1516
pub mod local_paths;
1617
pub mod locations;

0 commit comments

Comments
 (0)
Please sign in to comment.