Skip to content

Commit

Permalink
chapter09: IterPeeps, WorkList, deps, Node::walk
Browse files Browse the repository at this point in the history
  • Loading branch information
RobertObkircher committed Aug 21, 2024
1 parent 51639c1 commit e4a1ab0
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 1 deletion.
10 changes: 10 additions & 0 deletions src/datastructures/id_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ impl<E: Id> IdSet<E> {
let (word, bit) = IdSet::index(&element);
self.words.get(word).is_some_and(|w| (w & (1 << bit)) != 0)
}

pub fn clear(&mut self) {
self.words.iter_mut().for_each(|w| *w = 0);
}

pub fn is_empty(&self) -> bool {
self.words.iter().all(|w| *w == 0)
}
}

#[cfg(test)]
Expand All @@ -66,6 +74,7 @@ mod tests {
let mut set = IdSet::zeros(0);
set.add(42);
assert!(set.get(42));
assert!(!set.is_empty());
set.add(43);
set.remove(42);
assert!(!set.get(42));
Expand All @@ -77,5 +86,6 @@ mod tests {
}

assert_eq!(set.words.iter().sum::<usize>(), 0);
assert!(set.is_empty());
}
}
1 change: 1 addition & 0 deletions src/datastructures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub mod arena;
pub mod id;
pub mod id_set;
pub mod id_vec;
pub mod random;
82 changes: 82 additions & 0 deletions src/datastructures/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::num::Wrapping;

/// a simple rng for the worklist that behaves like java
pub struct Random {
seed: Wrapping<u64>,
}

const MULTIPLIER: Wrapping<u64> = Wrapping(0x5DEECE66D);
const ADDEND: Wrapping<u64> = Wrapping(0xB);
const MASK: Wrapping<u64> = Wrapping((1 << 48) - 1);

impl Random {
pub fn with_seed(seed: u64) -> Self {
Self {
seed: (Wrapping(seed) ^ MULTIPLIER) & MASK,
}
}

fn next(&mut self, bits: u8) -> Wrapping<u64> {
self.seed = ((self.seed * MULTIPLIER) + ADDEND) & MASK;
self.seed >> (48 - bits) as usize
}

pub fn next_int(&mut self, exclusive_bound: i32) -> usize {
assert!(exclusive_bound > 0);
let bound = Wrapping(exclusive_bound as u64);

let mut r = self.next(31);
let m = bound - Wrapping(1);

if (bound & m).0 == 0 {
r = (bound * r) >> 31 // if power of two
} else {
let mut u = r;
loop {
r = u % bound;
if u + m >= r {
break;
}
u = self.next(31)
}
}
r.0 as usize
}
}

#[cfg(test)]
mod tests {
use crate::datastructures::random::Random;

#[test]
fn test1() {
let mut r = Random::with_seed(123);
assert_eq!(r.next_int(2), 1);
assert_eq!(r.next_int(2), 0);
assert_eq!(r.next_int(4), 3);
assert_eq!(r.next_int(321), 227);
assert_eq!(r.next_int(64), 16);
assert_eq!(r.next_int(93), 14);
assert_eq!(r.next_int(64), 38);
assert_eq!(r.next_int(64), 16);
assert_eq!(r.next_int(11), 8);
}

#[test]
fn test2() {
// var r = new Random(123);
// IntStream.range(1, 100).map(r::nextInt).toArray()
let mut r = Random::with_seed(123);
let array = (1..100).map(|i| r.next_int(i)).collect::<Vec<_>>();
assert_eq!(
array,
&[
0, 0, 2, 1, 0, 5, 3, 2, 8, 3, 0, 6, 7, 5, 7, 10, 10, 5, 18, 16, 14, 3, 4, 2, 3, 3,
10, 20, 1, 4, 28, 10, 24, 33, 7, 15, 8, 2, 32, 26, 3, 2, 5, 14, 14, 10, 18, 18, 35,
40, 48, 38, 27, 44, 38, 13, 10, 52, 47, 37, 35, 55, 14, 25, 32, 8, 61, 39, 47, 50,
64, 37, 3, 13, 51, 73, 44, 16, 9, 30, 48, 23, 59, 81, 14, 73, 5, 82, 51, 64, 42,
20, 67, 66, 78, 45, 8, 55, 12
]
);
}
}
65 changes: 65 additions & 0 deletions src/sea_of_nodes/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ pub use node::{BoolOp, Node, ProjNode};
pub use scope::ScopeNode;

use crate::datastructures::id::Id;
use crate::datastructures::id_set::IdSet;
use crate::datastructures::id_vec::IdVec;
use crate::sea_of_nodes::types::{Ty, Types};
use iter_peeps::IterPeeps;

mod id;
mod idealize;
mod iter_peeps;
mod node;
mod peephole;
mod print;
Expand Down Expand Up @@ -45,6 +48,13 @@ pub struct Nodes<'t> {
/// meaning
pub outputs: IdVec<NodeId, Vec<NodeId>>,

/// Some of the peephole rules get complex, and search further afield than
/// just the nearest neighbor. These peepholes can fail the pattern match
/// on a node some distance away, and if that node ever changes we should
/// retry the peephole. Track a set of Nodes dependent on `this`, and
/// revisit them if `this` changes.
pub deps: IdVec<NodeId, Vec<NodeId>>,

/// Immediate dominator tree depth, used to approximate a real IDOM during
/// parsing where we do not have the whole program, and also peepholes
/// change the CFG incrementally.
Expand All @@ -59,6 +69,14 @@ pub struct Nodes<'t> {
/// Creating nodes such as constants and computing peepholes requires
/// interning new types and operations such as meet and join.
pub types: &'t Types<'t>,

/// Worklist for iterative peepholes
pub iter_peeps: IterPeeps,

pub iter_cnt: usize,
pub iter_nop_cnt: usize,

pub walk_visited: IdSet<NodeId>,
}

pub type NodeCreation<'t> = (Node<'t>, Vec<Option<NodeId>>);
Expand All @@ -71,10 +89,15 @@ impl<'t> Nodes<'t> {
inputs: IdVec::new(vec![vec![]]),
outputs: IdVec::new(vec![vec![]]),
ty: IdVec::new(vec![None]),
deps: IdVec::new(vec![vec![]]),
idepth: IdVec::new(vec![0]),
disable_peephole: false,
start: NodeId::DUMMY,
types,
iter_peeps: IterPeeps::new(),
iter_cnt: 0,
iter_nop_cnt: 0,
walk_visited: IdSet::zeros(0),
}
}
pub fn len(&self) -> usize {
Expand All @@ -90,6 +113,7 @@ impl<'t> Nodes<'t> {
self.inputs.push(inputs);
self.outputs.push(vec![]);
self.ty.push(None);
self.deps.push(vec![]);
self.idepth.push(0);
for i in 0..self.inputs[id].len() {
if let Some(input) = self.inputs[id][i] {
Expand Down Expand Up @@ -348,6 +372,47 @@ impl<'t> Nodes<'t> {
debug_assert!(matches!(self[region], Node::Region { .. } | Node::Loop));
self.inputs[region].last().unwrap().is_none()
}

/// Utility to walk the entire graph applying a function; return the first
/// not-null result.
fn walk_non_reentrant<T, F: FnMut(&mut Self, NodeId) -> Option<T>>(
&mut self,
node: NodeId,
mut f: F,
) -> Option<T> {
assert!(self.walk_visited.is_empty());
let result = self.walk_non_reentrant_inner(node, &mut f);
self.walk_visited.clear();
result
}

fn walk_non_reentrant_inner<T, F: FnMut(&mut Self, NodeId) -> Option<T>>(
&mut self,
node: NodeId,
f: &mut F,
) -> Option<T> {
if self.walk_visited.get(node) {
return None; // Been there, done that
}
self.walk_visited.add(node);
if let result @ Some(_) = f(self, node) {
return result;
};
for i in 0..self.inputs[node].len() {
if let Some(node) = self.inputs[node][i] {
if let result @ Some(_) = self.walk_non_reentrant_inner(node, f) {
return result;
};
}
}
for i in 0..self.outputs[node].len() {
let node = self.outputs[node][i];
if let result @ Some(_) = self.walk_non_reentrant_inner(node, f) {
return result;
};
}
None
}
}

impl<'t> Index<NodeId> for Nodes<'t> {
Expand Down
Loading

0 comments on commit e4a1ab0

Please sign in to comment.