Checker analysis: change order of block processing for better efficiency. (#29)
After going through the checker with @fitzgen, we discussed the dataflow analysis and @fitzgen noted that it would likely be more efficient to, for example, process an inner cycle of blocks in a loop nest and converge before returning to the outer loop. I had written a BFS-style workqueue loop to converge the dataflow analysis without much thought, with a FIFO workqueue. Any workqueue ordering will work and will converge to the same fixpoint (as long as we are operating on a lattice), but indeed some orderings will be more efficient, and a DFS-style (LIFO stack) workqueue will give us this property of converging inner loops first. In measurements, there doesn't seem to be much of a difference for small fuzz testcases, but this will likely matter more if/when we try to run the checker to validate register allocation on large functions.
This commit is contained in:
@@ -102,7 +102,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
@@ -726,16 +725,14 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
|
|
||||||
/// Perform the dataflow analysis to compute checker state at each BB entry.
|
/// Perform the dataflow analysis to compute checker state at each BB entry.
|
||||||
fn analyze(&mut self) {
|
fn analyze(&mut self) {
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = Vec::new();
|
||||||
let mut queue_set = FxHashSet::default();
|
let mut queue_set = FxHashSet::default();
|
||||||
for block in 0..self.f.num_blocks() {
|
|
||||||
let block = Block::new(block);
|
queue.push(self.f.entry_block());
|
||||||
queue.push_back(block);
|
queue_set.insert(self.f.entry_block());
|
||||||
queue_set.insert(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
while !queue.is_empty() {
|
while !queue.is_empty() {
|
||||||
let block = queue.pop_front().unwrap();
|
let block = queue.pop().unwrap();
|
||||||
queue_set.remove(&block);
|
queue_set.remove(&block);
|
||||||
let mut state = self.bb_in.get(&block).cloned().unwrap();
|
let mut state = self.bb_in.get(&block).cloned().unwrap();
|
||||||
trace!("analyze: block {} has state {:?}", block.index(), state);
|
trace!("analyze: block {} has state {:?}", block.index(), state);
|
||||||
@@ -777,7 +774,7 @@ impl<'a, F: Function> Checker<'a, F> {
|
|||||||
);
|
);
|
||||||
self.bb_in.insert(succ, new_state);
|
self.bb_in.insert(succ, new_state);
|
||||||
if !queue_set.contains(&succ) {
|
if !queue_set.contains(&succ) {
|
||||||
queue.push_back(succ);
|
queue.push(succ);
|
||||||
queue_set.insert(succ);
|
queue_set.insert(succ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user