use super::expression_values::expression_values;
use super::value::Value;
use super::{track, Variables, MAX_VALUES};
use crate::codegen::cfg::{ControlFlowGraph, Instr};
use crate::sema::ast::Namespace;
use std::collections::{HashMap, HashSet};
pub(super) fn reaching_values(
block_no: usize,
cfg: &ControlFlowGraph,
vars: &mut Variables,
block_vars: &mut HashMap<usize, Variables>,
ns: &Namespace,
) {
if let Some(map) = block_vars.get_mut(&block_no) {
let mut changes = false;
for (var_no, set) in vars.iter() {
changes |= update_map(*var_no, set, map);
}
if !changes {
return;
}
} else {
block_vars.insert(block_no, vars.clone());
}
for instr in &cfg.blocks[block_no].instr {
transfer(instr, vars, ns);
match instr {
Instr::Branch { block } => {
reaching_values(*block, cfg, vars, block_vars, ns);
}
Instr::BranchCond {
cond,
true_block,
false_block,
} => {
let v = expression_values(cond, vars, ns);
if v.len() == 1 {
let v = v.iter().next().unwrap();
if v.known_bits[0] {
reaching_values(
if v.value[0] {
*true_block
} else {
*false_block
},
cfg,
vars,
block_vars,
ns,
);
continue;
}
}
let mut vars_copy = vars.clone();
reaching_values(*true_block, cfg, &mut vars_copy, block_vars, ns);
reaching_values(*false_block, cfg, vars, block_vars, ns);
}
_ => (),
}
}
}
fn update_map(var_no: usize, set: &HashSet<Value>, map: &mut Variables) -> bool {
if let Some(existing) = map.get_mut(&var_no) {
if existing.iter().next().is_some_and(|v| v.all_unknown()) {
false
} else if let Some(v) = set.iter().find(|v| v.all_unknown()) {
let mut set = HashSet::new();
set.insert(v.clone());
map.insert(var_no, set);
true
} else {
let mut changes = false;
for v in set {
if !existing.contains(v) {
existing.insert(v.clone());
changes = true;
}
}
if existing.len() > MAX_VALUES {
let bits = existing.iter().next().unwrap().bits;
let mut set = HashSet::new();
set.insert(Value::unknown(bits));
changes = true;
map.insert(var_no, set);
}
changes
}
} else {
if set.len() > MAX_VALUES || set.iter().any(|v| v.all_unknown()) {
let bits = set.iter().next().unwrap().bits;
let mut set = HashSet::new();
set.insert(Value::unknown(bits));
map.insert(var_no, set);
} else {
map.insert(var_no, set.clone());
}
true
}
}
pub(super) fn transfer(instr: &Instr, vars: &mut Variables, ns: &Namespace) {
match instr {
Instr::Set { res, expr, .. } => {
let v = expression_values(expr, vars, ns);
vars.insert(*res, v);
}
Instr::Call {
res, return_tys, ..
} => {
for (i, var_no) in res.iter().enumerate() {
let mut set = HashSet::new();
let ty = &return_tys[i];
if track(ty) {
let bits = ty.bits(ns) as usize;
set.insert(Value::unknown(bits));
vars.insert(*var_no, set);
}
}
}
Instr::PopStorage { res: Some(res), .. } => {
let mut set = HashSet::new();
set.insert(Value::unknown(8));
vars.insert(*res, set);
}
Instr::PopMemory { res, ty, .. } => {
if track(ty) {
let mut set = HashSet::new();
let bits = ty.bits(ns) as usize;
set.insert(Value::unknown(bits));
vars.insert(*res, set);
}
}
_ => (),
}
}