use std::collections::BTreeMap;
mod fold;
mod indexes;
mod rewrite;
use super::form::{SsaExpr, SsaForm, SsaStmt};
use super::variable::SsaVariable;
use fold::{as_literal, fold_binary, fold_unary};
use indexes::{collect_used, rebuild_indexes};
use rewrite::{resolve_once, rewrite_expr};
#[cfg(test)]
mod tests;
pub fn optimize(ssa: &mut SsaForm) -> usize {
let mut rounds = 0usize;
loop {
let rewrites = one_round(ssa);
if rewrites == 0 {
break;
}
rounds += 1;
if rounds > ssa.blocks.len() + 4 {
break;
}
}
rounds
}
type Subst = BTreeMap<SsaVariable, SsaExpr>;
fn one_round(ssa: &mut SsaForm) -> usize {
let mut subst: Subst = BTreeMap::new();
for (_bid, block) in ssa.blocks.iter() {
for stmt in &block.stmts {
if let SsaStmt::Assign { target, value } = stmt {
match value {
SsaExpr::Literal(_) => {
if !is_slot_var(target) {
subst.insert(target.clone(), value.clone());
}
}
SsaExpr::Variable(src) => {
if !is_slot_var(target) || is_slot_var(src) {
subst.insert(target.clone(), value.clone());
}
}
SsaExpr::Binary { op, left, right } => {
if let (Some(a), Some(b)) = (as_literal(left), as_literal(right)) {
if let Some(folded) = fold_binary(*op, &a, &b) {
subst.insert(target.clone(), SsaExpr::lit(folded));
}
}
}
SsaExpr::Unary { op, operand } => {
if let Some(a) = as_literal(operand) {
if let Some(folded) = fold_unary(*op, &a) {
subst.insert(target.clone(), SsaExpr::lit(folded));
}
}
}
_ => {}
}
}
}
}
for (_bid, block) in ssa.blocks.iter() {
for stmt in &block.stmts {
if let SsaStmt::Assign { target, value } = stmt {
if !subst.contains_key(target) {
continue;
}
if let SsaExpr::Variable(src) = value {
if let Some(resolved) = subst.get(src) {
subst.insert(target.clone(), resolved.clone());
}
}
}
}
}
for (_bid, block) in ssa.blocks.iter() {
for phi in &block.phi_nodes {
let operands: Vec<&SsaVariable> = phi.operands.values().collect();
if operands.is_empty() {
continue;
}
let first = operands[0];
let mut resolved_first = subst
.get(first)
.cloned()
.unwrap_or_else(|| SsaExpr::var(first.clone()));
resolved_first = resolve_once(&subst, &resolved_first);
let all_same = operands.iter().all(|v| {
let r = subst
.get(*v)
.cloned()
.unwrap_or_else(|| SsaExpr::var((*v).clone()));
resolve_once(&subst, &r) == resolved_first
});
if all_same {
subst.insert(phi.target.clone(), resolved_first);
}
}
}
if subst.is_empty() {
return 0;
}
let used = collect_used(ssa, &subst);
let mut rewrites = 0usize;
for (_bid, block) in ssa.blocks.iter_mut() {
for phi in &mut block.phi_nodes {
for (_pred, var) in phi.operands.iter_mut() {
if let Some(SsaExpr::Variable(rep_var)) = subst.get(var) {
*var = rep_var.clone();
rewrites += 1;
}
}
}
block.stmts.retain(|stmt| match stmt {
SsaStmt::Assign { target, .. } => {
!subst.contains_key(target) || used.contains(target)
}
_ => true,
});
for stmt in &mut block.stmts {
if let SsaStmt::Assign { target, value } = stmt {
let new_value = rewrite_expr(value, &subst);
if new_value != *value {
*value = new_value;
rewrites += 1;
}
if let SsaExpr::Variable(src) = value {
if src == target {
}
}
}
}
}
rebuild_indexes(ssa);
rewrites.max(1)
}
fn is_slot_var(v: &SsaVariable) -> bool {
let b = v.base.as_str();
b.starts_with("loc") || b.starts_with("arg") || b.starts_with("static")
}