use crate::{
analysis::{
dataflow::{
framework::{DataFlowAnalysis, Direction},
lattice::MeetSemiLattice,
},
SsaBlock, SsaFunction, SsaVarId,
},
utils::BitSet,
};
pub struct LiveVariables {
num_vars: usize,
use_sets: Vec<BitSet>,
def_sets: Vec<BitSet>,
}
impl LiveVariables {
#[must_use]
pub fn new(ssa: &SsaFunction) -> Self {
let num_vars = ssa.variable_count();
let num_blocks = ssa.block_count();
let mut use_sets = Vec::with_capacity(num_blocks);
let mut def_sets = Vec::with_capacity(num_blocks);
for block in ssa.blocks() {
let mut uses = BitSet::new(num_vars);
let mut defs = BitSet::new(num_vars);
for phi in block.phi_nodes() {
if let Some(def_idx) = ssa.var_index(phi.result()) {
defs.insert(def_idx);
}
for op in phi.operands() {
if let Some(var_idx) = ssa.var_index(op.value()) {
if !defs.contains(var_idx) {
uses.insert(var_idx);
}
}
}
}
for instr in block.instructions() {
for &use_var in &instr.uses() {
if let Some(var_idx) = ssa.var_index(use_var) {
if !defs.contains(var_idx) {
uses.insert(var_idx);
}
}
}
if let Some(def) = instr.def() {
if let Some(def_idx) = ssa.var_index(def) {
defs.insert(def_idx);
}
}
}
use_sets.push(uses);
def_sets.push(defs);
}
Self {
num_vars,
use_sets,
def_sets,
}
}
#[must_use]
pub const fn num_variables(&self) -> usize {
self.num_vars
}
#[must_use]
pub fn use_set(&self, block: usize) -> Option<&BitSet> {
self.use_sets.get(block)
}
#[must_use]
pub fn def_set(&self, block: usize) -> Option<&BitSet> {
self.def_sets.get(block)
}
}
impl DataFlowAnalysis for LiveVariables {
type Lattice = LivenessResult;
const DIRECTION: Direction = Direction::Backward;
fn boundary(&self, _ssa: &SsaFunction) -> Self::Lattice {
LivenessResult {
live: BitSet::new(self.num_vars),
}
}
fn initial(&self, _ssa: &SsaFunction) -> Self::Lattice {
LivenessResult {
live: BitSet::new(self.num_vars),
}
}
fn transfer(
&self,
block_id: usize,
_block: &SsaBlock,
output: &Self::Lattice,
_ssa: &SsaFunction,
) -> Self::Lattice {
let mut result = output.live.clone();
result.difference_with(&self.def_sets[block_id]);
result.union_with(&self.use_sets[block_id]);
LivenessResult { live: result }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LivenessResult {
live: BitSet,
}
impl LivenessResult {
#[must_use]
pub fn new(num_vars: usize) -> Self {
Self {
live: BitSet::new(num_vars),
}
}
#[must_use]
pub fn is_live(&self, var: SsaVarId) -> bool {
let idx = var.index();
idx < self.live.len() && self.live.contains(idx)
}
pub fn variables(&self) -> impl Iterator<Item = SsaVarId> + '_ {
self.live.iter().map(SsaVarId::from_index)
}
#[must_use]
pub fn count(&self) -> usize {
self.live.count()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.live.is_empty()
}
pub fn add(&mut self, var: SsaVarId) {
let idx = var.index();
if idx < self.live.len() {
self.live.insert(idx);
}
}
pub fn remove(&mut self, var: SsaVarId) {
let idx = var.index();
if idx < self.live.len() {
self.live.remove(idx);
}
}
#[must_use]
pub const fn as_bitset(&self) -> &BitSet {
&self.live
}
}
impl MeetSemiLattice for LivenessResult {
fn meet(&self, other: &Self) -> Self {
let mut result = self.live.clone();
result.union_with(&other.live);
Self { live: result }
}
fn is_bottom(&self) -> bool {
self.live.count() == self.live.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_liveness_result() {
let mut result = LivenessResult::new(10);
assert!(result.is_empty());
result.add(SsaVarId::from_index(0));
result.add(SsaVarId::from_index(5));
assert!(!result.is_empty());
assert_eq!(result.count(), 2);
assert!(result.is_live(SsaVarId::from_index(0)));
assert!(result.is_live(SsaVarId::from_index(5)));
assert!(!result.is_live(SsaVarId::from_index(1)));
result.remove(SsaVarId::from_index(0));
assert!(!result.is_live(SsaVarId::from_index(0)));
assert_eq!(result.count(), 1);
}
#[test]
fn test_liveness_meet() {
let mut a = LivenessResult::new(10);
let mut b = LivenessResult::new(10);
a.add(SsaVarId::from_index(0));
a.add(SsaVarId::from_index(1));
b.add(SsaVarId::from_index(1));
b.add(SsaVarId::from_index(2));
let result = a.meet(&b);
assert!(result.is_live(SsaVarId::from_index(0)));
assert!(result.is_live(SsaVarId::from_index(1)));
assert!(result.is_live(SsaVarId::from_index(2)));
assert_eq!(result.count(), 3);
}
#[test]
fn test_liveness_iterator() {
let mut result = LivenessResult::new(100);
result.add(SsaVarId::from_index(5));
result.add(SsaVarId::from_index(42));
result.add(SsaVarId::from_index(99));
let vars: Vec<_> = result.variables().collect();
assert_eq!(vars.len(), 3);
assert!(vars.contains(&SsaVarId::from_index(5)));
assert!(vars.contains(&SsaVarId::from_index(42)));
assert!(vars.contains(&SsaVarId::from_index(99)));
}
}