def_use.rs 4.86 KiB
use crate::*;
/*
* Custom type for an immutable def_use map. This is a relatively efficient
* storage of def_use edges, requiring 2 heap allocations.
*/
#[derive(Debug, Clone)]
pub struct ImmutableDefUseMap {
first_edges: Vec<u32>,
users: Vec<NodeID>,
}
impl ImmutableDefUseMap {
pub fn num_edges(&self, id: NodeID) -> u32 {
if id.idx() + 1 < self.first_edges.len() {
self.first_edges[id.idx() + 1] - self.first_edges[id.idx()]
} else {
self.users.len() as u32 - self.first_edges[id.idx()]
}
}
pub fn get_users(&self, id: NodeID) -> &[NodeID] {
let first_edge = self.first_edges[id.idx()] as usize;
let num_edges = self.num_edges(id) as usize;
&self.users[first_edge..first_edge + num_edges]
}
pub fn num_nodes(&self) -> usize {
self.first_edges.len()
}
}
/*
* Top level def_use function.
*/
pub fn def_use(function: &Function) -> ImmutableDefUseMap {
// Step 1: get uses for each node.
let node_uses: Vec<NodeUses> = function.nodes.iter().map(|node| get_uses(node)).collect();
// Step 2: count number of users per node.
let mut num_users: Vec<u32> = vec![0; node_uses.len()];
for uses in node_uses.iter() {
for u in uses.as_ref() {
num_users[u.idx()] += 1;
}
}
// Step 3: assemble first_edges vector.
let mut first_edges: Vec<u32> = vec![];
let mut num_edges = 0;
for num_users in num_users {
first_edges.push(num_edges);
num_edges += num_users;
}
// Step 4: assemble users vector.
let mut users: Vec<NodeID> = vec![NodeID::new(0); num_edges as usize];
let mut num_users_per_node: Vec<u32> = vec![0; node_uses.len()];
for (idx, uses) in node_uses.iter().enumerate() {
for u in uses.as_ref() {
let first_edge = first_edges[u.idx()];
let num_users_so_far = num_users_per_node[u.idx()];
users[first_edge as usize + num_users_so_far as usize] = NodeID::new(idx);
num_users_per_node[u.idx()] = num_users_so_far + 1;
}
}
// Step 5: pack and return.
ImmutableDefUseMap { first_edges, users }
}
/*
* Enum for storing uses of node. Using get_uses, one can easily iterate over
* the defs that a node uses.
*/
#[derive(Debug, Clone)]
pub enum NodeUses<'a> {
Zero,
One([NodeID; 1]),
Two([NodeID; 2]),
Three([NodeID; 3]),
Variable(&'a Box<[NodeID]>),
// Phi nodes are special, and store both a NodeID locally *and* many in a
// boxed slice. Since these NodeIDs are not stored contiguously, we have to
// construct a new contiguous slice by copying. Sigh.
Phi(Box<[NodeID]>),
}
impl<'a> AsRef<[NodeID]> for NodeUses<'a> {
fn as_ref(&self) -> &[NodeID] {
match self {
NodeUses::Zero => &[],
NodeUses::One(x) => x,
NodeUses::Two(x) => x,
NodeUses::Three(x) => x,
NodeUses::Variable(x) => x,
NodeUses::Phi(x) => x,
}
}
}
/*
* Construct NodeUses for a Node.
*/
pub fn get_uses<'a>(node: &'a Node) -> NodeUses<'a> {
match node {
Node::Start => NodeUses::Zero,
Node::Region { preds } => NodeUses::Variable(preds),
Node::If { control, cond } => NodeUses::Two([*control, *cond]),
Node::Fork { control, factor: _ } => NodeUses::One([*control]),
Node::Join { control } => NodeUses::One([*control]),
Node::Phi { control, data } => {
let mut uses: Vec<NodeID> = Vec::from(&data[..]);
uses.push(*control);
NodeUses::Phi(uses.into_boxed_slice())
}
Node::ThreadID { control } => NodeUses::One([*control]),
Node::Collect { control, data } => NodeUses::Two([*control, *data]),
Node::Return { control, data } => NodeUses::Two([*control, *data]),
Node::Parameter { index: _ } => NodeUses::Zero,
Node::Constant { id: _ } => NodeUses::Zero,
Node::DynamicConstant { id: _ } => NodeUses::Zero,
Node::Unary { input, op: _ } => NodeUses::One([*input]),
Node::Binary { left, right, op: _ } => NodeUses::Two([*left, *right]),
Node::Call {
function: _,
dynamic_constants: _,
args,
} => NodeUses::Variable(args),
Node::ReadProd { prod, index: _ } => NodeUses::One([*prod]),
Node::WriteProd {
prod,
data,
index: _,
} => NodeUses::Two([*prod, *data]),
Node::ReadArray { array, index } => NodeUses::Two([*array, *index]),
Node::WriteArray { array, data, index } => NodeUses::Three([*array, *data, *index]),
Node::Match { control, sum } => NodeUses::Two([*control, *sum]),
Node::BuildSum {
data,
sum_ty: _,
variant: _,
} => NodeUses::One([*data]),
Node::ExtractSum { data, variant: _ } => NodeUses::One([*data]),
}
}