use std::collections::BTreeMap;
use crate::utils::BitSet;
pub fn compute_live_in_blocks(
defs: &BTreeMap<u32, BitSet>,
uses: &BTreeMap<u32, BitSet>,
successors: &[Vec<usize>],
block_count: usize,
) -> BTreeMap<u32, BitSet> {
let mut live_in: BTreeMap<u32, BitSet> = BTreeMap::new();
let mut predecessors: Vec<Vec<usize>> = vec![Vec::new(); block_count];
for (block_idx, succs) in successors.iter().enumerate() {
for &succ in succs {
if succ < block_count {
predecessors[succ].push(block_idx);
}
}
}
for (group, def_blocks) in defs {
let use_blocks = match uses.get(group) {
Some(blocks) => blocks,
None => continue, };
let mut live_in_set = BitSet::new(block_count);
let mut worklist: Vec<usize> = Vec::new();
for use_block in use_blocks.iter() {
if live_in_set.insert(use_block) {
worklist.push(use_block);
}
}
while let Some(block_idx) = worklist.pop() {
for &pred in &predecessors[block_idx] {
if def_blocks.contains(pred) {
continue;
}
if live_in_set.insert(pred) {
worklist.push(pred);
}
}
}
live_in.insert(*group, live_in_set);
}
live_in
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use crate::utils::BitSet;
use super::compute_live_in_blocks;
fn bitset_from(cap: usize, indices: &[usize]) -> BitSet {
let mut bs = BitSet::new(cap);
for &i in indices {
bs.insert(i);
}
bs
}
#[test]
fn test_diamond_liveness() {
let mut defs = BTreeMap::new();
let mut uses = BTreeMap::new();
let group: u32 = 0;
defs.insert(group, bitset_from(4, &[0]));
uses.insert(group, bitset_from(4, &[3]));
let successors = vec![
vec![1, 2], vec![3], vec![3], vec![], ];
let live_in = compute_live_in_blocks(&defs, &uses, &successors, 4);
let live = live_in.get(&group).unwrap();
assert!(live.contains(3), "use block should be live-in");
assert!(live.contains(1), "path block 1 should be live-in");
assert!(live.contains(2), "path block 2 should be live-in");
assert!(!live.contains(0), "def block should NOT be live-in");
}
#[test]
fn test_loop_liveness() {
let mut defs = BTreeMap::new();
let mut uses = BTreeMap::new();
let group: u32 = 0;
defs.insert(group, bitset_from(4, &[0, 2]));
uses.insert(group, bitset_from(4, &[1]));
let successors = vec![
vec![1], vec![2, 3], vec![1], vec![], ];
let live_in = compute_live_in_blocks(&defs, &uses, &successors, 4);
let live = live_in.get(&group).unwrap();
assert!(live.contains(1), "use/header block should be live-in");
assert!(!live.contains(0), "def block 0 should NOT be live-in");
assert!(!live.contains(2), "def block 2 should NOT be live-in");
}
#[test]
fn test_no_uses() {
let mut defs = BTreeMap::new();
let uses = BTreeMap::new();
let group: u32 = 0;
defs.insert(group, bitset_from(2, &[0]));
let successors = vec![vec![1], vec![]];
let live_in = compute_live_in_blocks(&defs, &uses, &successors, 2);
assert!(
!live_in.contains_key(&group),
"no uses means no liveness entry"
);
}
#[test]
fn test_nested_if_liveness() {
let mut defs = BTreeMap::new();
let mut uses = BTreeMap::new();
let group: u32 = 0;
defs.insert(group, bitset_from(6, &[1, 2]));
uses.insert(group, bitset_from(6, &[5]));
let successors = vec![
vec![1, 2], vec![3, 4], vec![5], vec![5], vec![5], vec![], ];
let live_in = compute_live_in_blocks(&defs, &uses, &successors, 6);
let live = live_in.get(&group).unwrap();
assert!(live.contains(5), "use block should be live-in");
assert!(
live.contains(3),
"block 3 should be live-in (no def, on path)"
);
assert!(
live.contains(4),
"block 4 should be live-in (no def, on path)"
);
assert!(!live.contains(1), "def block 1 should NOT be live-in");
assert!(!live.contains(2), "def block 2 should NOT be live-in");
}
}