use crate::analysis::core::{DataflowAnalyzer, Direction, Edge};
use crate::{BlockEnd, BlockId, Lowered};
pub struct ForwardDataflowAnalysis<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> {
lowered: &'a Lowered<'db>,
pub analyzer: TAnalyzer,
predecessor_counts: Vec<usize>,
incoming: Vec<Option<TAnalyzer::Info>>,
}
impl<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> ForwardDataflowAnalysis<'db, 'a, TAnalyzer> {
pub fn new(lowered: &'a Lowered<'db>, analyzer: TAnalyzer) -> Self {
debug_assert!(
TAnalyzer::DIRECTION == Direction::Forward,
"FwdAnalysis requires an analyzer with DIRECTION == Forward"
);
let predecessor_counts = compute_predecessor_counts(lowered);
let incoming = vec![None; lowered.blocks.len()];
Self { lowered, analyzer, predecessor_counts, incoming }
}
pub fn run(&mut self) -> Vec<Option<TAnalyzer::Info>> {
let n_blocks = self.lowered.blocks.len();
let mut block_info: Vec<Option<TAnalyzer::Info>> = vec![None; n_blocks];
let root_id = BlockId::root();
self.incoming[root_id.0] =
Some(self.analyzer.initial_info(root_id, &self.lowered.blocks[root_id].end));
let mut ready: Vec<BlockId> = vec![root_id];
while let Some(block_id) = ready.pop() {
let block = &self.lowered.blocks[block_id];
let mut info = self.incoming[block_id.0].clone().unwrap();
self.analyzer.visit_block_start(&mut info, block_id, block);
self.analyzer.transfer_block(&mut info, block_id, block);
self.propagate_to_successors(block_id, &info, &mut ready);
block_info[block_id.0] = Some(info);
}
block_info
}
fn propagate_to_successors(
&mut self,
block_id: BlockId,
info: &TAnalyzer::Info,
ready: &mut Vec<BlockId>,
) {
let block = &self.lowered.blocks[block_id];
match &block.end {
BlockEnd::Goto(target, remapping) => {
let edge = Edge::Goto { target: *target, remapping };
let target_info = self.analyzer.transfer_edge(info, &edge);
self.add_and_maybe_ready(*target, target_info, ready);
}
BlockEnd::Match { info: match_info } => {
for arm in match_info.arms() {
let edge = Edge::MatchArm { arm, match_info };
let arm_info = self.analyzer.transfer_edge(info, &edge);
self.add_and_maybe_ready(arm.block_id, arm_info, ready);
}
}
BlockEnd::Return(..) | BlockEnd::Panic(_) => {
}
BlockEnd::NotSet => unreachable!("Block end not set"),
}
}
fn add_and_maybe_ready(
&mut self,
target: BlockId,
info: TAnalyzer::Info,
ready: &mut Vec<BlockId>,
) {
let merged_info = match self.incoming[target.0].take() {
Some(existing) => self.analyzer.merge(self.lowered, (target, 0), existing, info),
None => info,
};
self.incoming[target.0] = Some(merged_info);
self.predecessor_counts[target.0] -= 1;
if self.predecessor_counts[target.0] == 0 {
ready.push(target);
}
}
}
fn compute_predecessor_counts(lowered: &Lowered<'_>) -> Vec<usize> {
let n_blocks = lowered.blocks.len();
let mut counts = vec![0usize; n_blocks];
for (_, block) in lowered.blocks.iter() {
match &block.end {
BlockEnd::Goto(target, _) => {
counts[target.0] += 1;
}
BlockEnd::Match { info } => {
for arm in info.arms() {
counts[arm.block_id.0] += 1;
}
}
BlockEnd::Return(..) | BlockEnd::Panic(_) | BlockEnd::NotSet => {}
}
}
counts
}