use crate::codegen::cfg::{BasicBlock, ControlFlowGraph, Instr};
use crate::codegen::subexpression_elimination::anticipated_expressions::AnticipatedExpressions;
use crate::codegen::subexpression_elimination::available_variable::AvailableVariable;
use crate::codegen::subexpression_elimination::common_subexpression_tracker::CommonSubExpressionTracker;
use crate::codegen::subexpression_elimination::operator::Operator;
use crate::codegen::Expression;
use crate::sema::ast::Namespace;
use num_bigint::BigInt;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet, VecDeque};
use std::rc::Rc;
mod anticipated_expressions;
mod available_expression;
mod available_expression_set;
mod available_variable;
pub mod common_subexpression_tracker;
mod expression;
mod instruction;
mod operator;
mod tests;
pub type NodeId = usize;
#[derive(Default)]
pub struct AvailableExpression {
global_id_counter: NodeId,
cur_block: usize,
}
#[derive(Clone)]
pub struct BasicExpression<'a> {
expr_type: ExpressionType,
expression_id: NodeId,
children: HashMap<NodeId, Rc<RefCell<BasicExpression<'a>>>>,
pub reference: &'a Expression,
pub available_variable: AvailableVariable,
pub block: usize,
pub parent_block: Option<usize>,
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum ConstantType {
Bool(bool),
Bytes(Vec<u8>),
Number(BigInt),
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ExpressionType {
BinaryOperation(NodeId, NodeId, Operator),
UnaryOperation(NodeId, Operator),
Variable(usize),
FunctionArg(usize),
Literal(ConstantType),
}
#[derive(Default, Clone)]
pub struct AvailableExpressionSet<'a> {
expression_memory: HashMap<NodeId, Rc<RefCell<BasicExpression<'a>>>>,
expr_map: HashMap<ExpressionType, NodeId>,
mapped_variable: HashMap<usize, NodeId>,
}
struct CfgAsDag {
visiting_order: Vec<(usize, bool)>,
reverse_visiting_order: Vec<(usize, bool)>,
dag: Vec<Vec<usize>>,
reverse_dag: Vec<Vec<usize>>,
}
pub fn common_sub_expression_elimination(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
let cfg_as_dag = find_visiting_order(cfg);
let mut old_instr: Vec<Vec<Instr>> = vec![Vec::new(); cfg.blocks.len()];
for (block_no, block) in cfg.blocks.iter_mut().enumerate() {
std::mem::swap(&mut old_instr[block_no], &mut block.instr);
}
let mut anticipated_expressions = AnticipatedExpressions::new(
&cfg_as_dag.dag,
cfg_as_dag.reverse_dag,
cfg_as_dag.reverse_visiting_order,
);
anticipated_expressions.calculate_anticipated_expressions(&old_instr, cfg);
let mut ave = AvailableExpression::default();
let mut cst = CommonSubExpressionTracker::default();
let mut sets: HashMap<usize, AvailableExpressionSet> = HashMap::new();
sets.insert(0, AvailableExpressionSet::default());
cst.set_anticipated(anticipated_expressions);
for (block_no, cycle) in &cfg_as_dag.visiting_order {
let cur_block = &cfg.blocks[*block_no];
ave.set_cur_block(*block_no);
cst.set_cur_block(*block_no);
let mut cur_set = sets.remove(block_no).unwrap();
kill_loop_variables(cur_block, &mut cur_set, *cycle);
for instr in old_instr[*block_no].iter() {
cur_set.process_instruction(instr, &mut ave, &mut Some(&mut cst));
}
add_neighbor_blocks(cur_set, &cfg_as_dag.dag[*block_no], &mut sets, &cst);
}
cst.create_variables(ns, cfg);
sets.clear();
let mut ave = AvailableExpression::default();
sets.insert(0, AvailableExpressionSet::default());
for (block_no, cycle) in &cfg_as_dag.visiting_order {
let mut cur_set = sets.remove(block_no).unwrap();
let cur_block = &mut cfg.blocks[*block_no];
ave.set_cur_block(*block_no);
cst.set_cur_block(*block_no);
let mut new_instructions: Vec<Instr> = Vec::new();
kill_loop_variables(cur_block, &mut cur_set, *cycle);
for instr in old_instr[*block_no].iter() {
let instr = cur_set.regenerate_instruction(instr, &mut ave, &mut cst);
cst.add_new_instructions(&mut new_instructions);
new_instructions.push(instr);
}
cur_block.instr = new_instructions;
add_neighbor_blocks(cur_set, &cfg_as_dag.dag[*block_no], &mut sets, &cst);
}
cst.add_parent_block_instructions(cfg);
}
fn add_neighbor_blocks<'b>(
cur_set: AvailableExpressionSet<'b>,
edges: &[usize],
sets: &mut HashMap<usize, AvailableExpressionSet<'b>>,
cst: &CommonSubExpressionTracker,
) {
for edge in edges {
if let Some(set) = sets.get_mut(edge) {
set.intersect_sets(&cur_set, cst);
} else {
sets.insert(*edge, cur_set.deep_clone());
}
}
}
fn kill_loop_variables(block: &BasicBlock, cur_set: &mut AvailableExpressionSet, has_cycle: bool) {
if !has_cycle {
return;
}
for var_no in &block.loop_reaching_variables {
cur_set.remove_mapped(*var_no);
cur_set.kill(*var_no);
}
}
fn find_visiting_order(cfg: &ControlFlowGraph) -> CfgAsDag {
let mut order: Vec<(usize, bool)> = Vec::with_capacity(cfg.blocks.len());
let mut visited: HashSet<usize> = HashSet::new();
let mut stack: HashSet<usize> = HashSet::new();
let mut has_cycle: Vec<bool> = vec![false; cfg.blocks.len()];
let mut degrees: Vec<i32> = vec![0; cfg.blocks.len()];
let mut dag: Vec<Vec<usize>> = Vec::new();
let mut reverse_dag: Vec<Vec<usize>> = Vec::new();
dag.resize(cfg.blocks.len(), vec![]);
reverse_dag.resize(cfg.blocks.len(), vec![]);
cfg_dfs(
0,
cfg,
&mut visited,
&mut stack,
&mut degrees,
&mut has_cycle,
&mut dag,
&mut reverse_dag,
);
let mut queue: VecDeque<usize> = VecDeque::new();
queue.push_back(0);
while let Some(block_no) = queue.pop_front() {
order.push((block_no, has_cycle[block_no]));
for edge in cfg.blocks[block_no].successors() {
degrees[edge] -= 1;
if degrees[edge] == 0 {
queue.push_back(edge);
}
}
}
let mut reverse_visiting_order = order.clone();
reverse_visiting_order.reverse();
CfgAsDag {
visiting_order: order,
reverse_visiting_order,
dag,
reverse_dag,
}
}
fn cfg_dfs(
block_no: usize,
cfg: &ControlFlowGraph,
visited: &mut HashSet<usize>,
stack: &mut HashSet<usize>,
degrees: &mut Vec<i32>,
has_cycle: &mut Vec<bool>,
dag: &mut Vec<Vec<usize>>,
reverse_dag: &mut Vec<Vec<usize>>,
) -> bool {
if visited.contains(&block_no) {
return true;
}
if stack.contains(&block_no) {
degrees[block_no] -= 1;
has_cycle[block_no] = true;
return false;
}
stack.insert(block_no);
for edge in cfg.blocks[block_no].successors() {
degrees[edge] += 1;
if cfg_dfs(
edge,
cfg,
visited,
stack,
degrees,
has_cycle,
dag,
reverse_dag,
) {
dag[block_no].push(edge);
reverse_dag[edge].push(block_no);
}
}
stack.remove(&block_no);
visited.insert(block_no);
true
}