use std::{
cell::RefCell,
collections::{HashMap, HashSet, LinkedList, VecDeque},
ops::Deref,
};
use crate::{ControlFlow, NodeIndex, analyses::Analysis};
use smallvec::SmallVec;
use crate::{
Optimizer,
analyses::dominance::{Dominators, PostDominators},
};
use super::{Expression, Value, ValueTable, convert::value_of_var};
const MAX_SET_PASSES: usize = 10;
#[derive(Default)]
pub struct GlobalValues(pub RefCell<GvnState>);
impl Deref for GlobalValues {
type Target = RefCell<GvnState>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Default)]
pub struct GvnState {
pub values: ValueTable,
pub block_sets: HashMap<NodeIndex, BlockSets>,
}
impl Analysis for GlobalValues {
fn init(opt: &mut Optimizer) -> Self {
let mut this = GvnState::default();
this.build_sets(opt);
GlobalValues(RefCell::new(this))
}
}
#[derive(Debug, Clone, Default)]
pub struct BlockSets {
pub exp_gen: LinkedList<(u32, Expression)>,
pub phi_gen: HashMap<u32, Value>,
pub tmp_gen: HashSet<Value>,
pub leaders: HashMap<u32, Value>,
pub antic_out: LinkedList<(u32, Expression)>,
pub antic_in: LinkedList<(u32, Expression)>,
}
impl GvnState {
pub fn build_sets(&mut self, opt: &mut Optimizer) {
self.build_sets_forward(opt);
self.build_sets_backward(opt);
let global_leaders = self.values.value_numbers.iter();
let global_leaders = global_leaders
.filter(|(k, _)| {
matches!(
k,
Value::Constant(_, _)
| Value::Input(_, _)
| Value::Scalar(_, _)
| Value::ConstArray(_, _, _, _)
| Value::Builtin(..)
| Value::Output(_, _)
)
})
.map(|(k, v)| (*v, *k))
.collect::<HashMap<_, _>>();
for set in self.block_sets.values_mut() {
set.leaders.extend(global_leaders.clone());
}
}
fn build_sets_forward(&mut self, opt: &mut Optimizer) {
let mut worklist = VecDeque::new();
let dominators = opt.analysis::<Dominators>();
worklist.push_back((vec![opt.entry()], HashMap::new(), HashSet::new()));
while let Some((successors, leaders, tmp_gen)) = worklist.pop_front() {
for block in successors {
let (leaders, tmp_gen) =
self.build_block_sets_forward(opt, block, leaders.clone(), tmp_gen.clone());
let successors = dominators.immediately_dominated_by(block);
worklist.push_back((successors.collect(), leaders, tmp_gen));
}
}
}
fn build_sets_backward(&mut self, opt: &mut Optimizer) {
let mut build_passes = 0;
let mut changed = true;
let mut worklist = VecDeque::new();
let post_doms = opt.analysis::<PostDominators>();
worklist.push_back(opt.ret);
while changed && build_passes < MAX_SET_PASSES {
changed = false;
while let Some(current) = worklist.pop_front() {
changed |= self.build_block_sets_backward(opt, current);
let predecessors = post_doms.immediately_dominated_by(current);
worklist.extend(predecessors);
}
build_passes += 1;
}
}
fn build_block_sets_forward(
&mut self,
opt: &mut Optimizer,
block: NodeIndex,
mut leaders: HashMap<u32, Value>,
tmp_gen: HashSet<Value>,
) -> (HashMap<u32, Value>, HashSet<Value>) {
let mut exp_gen = LinkedList::new();
let mut phi_gen = HashMap::new();
let mut tmp_gen = tmp_gen;
let mut added_exprs = HashSet::new();
for phi in opt.program[block].phi_nodes.borrow().iter() {
let (num, val) = self.values.lookup_or_add_phi(phi);
leaders.entry(num).or_insert(val);
phi_gen.entry(num).or_insert(val);
}
for op in opt.program[block].ops.borrow().values() {
match self
.values
.maybe_insert_op(op, &mut exp_gen, &mut added_exprs)
{
Ok((num, Some(val), _)) => {
leaders.entry(num).or_insert(val);
}
Err(Some(killed)) => {
tmp_gen.insert(killed);
}
_ => {}
}
}
let sets = BlockSets {
exp_gen,
phi_gen,
tmp_gen: tmp_gen.clone(),
leaders: leaders.clone(),
antic_out: Default::default(),
antic_in: Default::default(),
};
self.block_sets.insert(block, sets);
(leaders, tmp_gen)
}
fn build_block_sets_backward(&mut self, opt: &mut Optimizer, current: NodeIndex) -> bool {
let mut changed = false;
let successors = opt.successors(current);
#[allow(clippy::comparison_chain)]
if let ControlFlow::Loop { body, .. } | ControlFlow::LoopBreak { body, .. } =
opt.block(current).control_flow.borrow().clone()
{
let antic_in_succ = &self.block_sets[&body].antic_in;
let phi_gen = &self.block_sets[&body].phi_gen;
let result =
phi_translate(opt, phi_gen, antic_in_succ, body, current, &mut self.values);
if self.block_sets[¤t].antic_out != result {
changed = true;
}
self.block_sets.get_mut(¤t).unwrap().antic_out = result;
} else if successors.len() > 1 {
let potential_out = &self.block_sets[&successors[0]].antic_in;
let mut result = LinkedList::new();
let rest = successors[1..]
.iter()
.map(|child| &self.block_sets[child].antic_in);
for (val, expr) in potential_out {
if rest.clone().all(|child| child.iter().any(|v| v.0 == *val)) {
result.push_back((*val, expr.clone()));
}
}
if self.block_sets[¤t].antic_out != result {
changed = true;
}
self.block_sets.get_mut(¤t).unwrap().antic_out = result;
} else if successors.len() == 1 {
let child = successors[0];
let antic_in_succ = &self.block_sets[&child].antic_in;
let phi_gen = &self.block_sets[&child].phi_gen;
let result = phi_translate(
opt,
phi_gen,
antic_in_succ,
child,
current,
&mut self.values,
);
if self.block_sets[¤t].antic_out != result {
changed = true;
}
self.block_sets.get_mut(¤t).unwrap().antic_out = result;
}
let mut killed = self.block_sets[¤t]
.tmp_gen
.iter()
.map(|tmp| self.values.lookup_or_add_value(*tmp))
.collect::<HashSet<_>>();
let cleaned = self.block_sets[¤t]
.exp_gen
.iter()
.chain(self.block_sets[¤t].antic_out.iter())
.filter_map(|(val, exp)| {
for dependency in exp.depends_on() {
if killed.contains(&dependency) {
killed.insert(*val);
return None;
}
}
if let Expression::Volatile(_) = exp {
killed.insert(*val);
return None;
}
Some((*val, exp.clone()))
});
let mut added = HashSet::new();
let mut result = LinkedList::new();
for v in cleaned {
if !added.contains(&v.0) {
added.insert(v.0);
result.push_back(v);
}
}
if self.block_sets[¤t].antic_in != result {
changed = true;
}
self.block_sets.get_mut(¤t).unwrap().antic_in = result;
changed
}
}
pub fn phi_translate(
opt: &Optimizer,
phi_gen: &HashMap<u32, Value>,
antic: &LinkedList<(u32, Expression)>,
child: NodeIndex,
parent: NodeIndex,
values: &mut ValueTable,
) -> LinkedList<(u32, Expression)> {
let mut result = LinkedList::new();
let mut translated = HashMap::new();
for phi in opt.block(child).phi_nodes.borrow().iter() {
let (num, _) = values.lookup_or_add_phi(phi);
let here = phi.entries.iter().find(|it| it.block == parent).unwrap();
let num_here = values.lookup_or_add_var(&here.value).unwrap();
translated.insert(num, num_here);
}
for (val, expr) in antic {
if let Some(value) = phi_gen.get(val) {
let nodes = opt.block(child).phi_nodes.borrow();
let phi = nodes
.iter()
.find(|it| &value_of_var(&it.out).unwrap() == value);
if let Some(phi) = phi {
let value_here = phi.entries.iter().find(|it| it.block == parent).unwrap();
let value_here = value_of_var(&value_here.value).unwrap();
let num_here = values.lookup_or_add_expr(Expression::Value(value_here), None);
result.push_back((num_here, Expression::Value(value_here)));
translated.insert(*val, num_here);
}
} else {
let t = |val: &u32| *translated.get(val).unwrap_or(val);
let updated = match expr {
Expression::Instruction(inst) => {
let args = inst.args.iter().map(t).collect::<SmallVec<[u32; 4]>>();
let mut inst = inst.clone();
inst.args = args;
Expression::Instruction(inst)
}
Expression::Copy(val, item) => Expression::Copy(t(val), *item),
Expression::Phi(_) => continue,
other => other.clone(),
};
let updated_val = values.lookup_or_add_expr(updated.clone(), None);
result.push_back((updated_val, updated));
translated.insert(*val, updated_val);
}
}
result
}