use std::collections::HashMap;
use crate::{
Add, AddressingMode, CanAllocate, CanVisitInstructions as _,
DependencyAnalyzer, Div, DropHighest, DropLowest, Exp, Function,
Instruction, InstructionVisitor, Mod, Mul, Neg, Optimizer, RegisterIndex,
Return, RollCustomDice, RollRange, RollStandardDice, RollingRecordIndex,
Sub, SumRollingRecord
};
#[derive(Debug, Clone, Default)]
pub struct DeadCodeEliminator
{
replacements: HashMap<AddressingMode, AddressingMode>,
next_register: RegisterIndex,
next_rolling_record: RollingRecordIndex,
instructions: Vec<Instruction>
}
impl Optimizer<()> for DeadCodeEliminator
{
fn optimize(mut self, mut function: Function) -> Result<Function, ()>
{
let start_register =
function.parameters.len() + function.externals.len();
loop
{
self.replacements.clear();
self.next_register = RegisterIndex(start_register);
self.next_rolling_record = RollingRecordIndex::default();
self.instructions.clear();
let analyzer = DependencyAnalyzer::analyze(&function.instructions);
for instruction in &function.instructions
{
instruction
.visit(&mut AnalyticalVisitor(&analyzer, &mut self))
.unwrap();
}
if function.instructions == self.instructions
{
return Ok(function)
}
function.register_count = self.next_register.0;
function.instructions = self.instructions.clone();
self.replacements.clear();
self.next_register = RegisterIndex(start_register);
self.next_rolling_record = RollingRecordIndex::default();
self.instructions.clear();
}
}
}
struct AnalyticalVisitor<'inst>(
&'inst DependencyAnalyzer<'inst>,
&'inst mut DeadCodeEliminator
);
impl InstructionVisitor<()> for AnalyticalVisitor<'_>
{
fn visit_roll_range(&mut self, range: &RollRange) -> Result<(), ()>
{
if self.has_readers(*range)
{
let dest = self.next_rolling_record();
self.replace(range.dest, dest);
self.emit(RollRange {
dest,
start: self.replacement(range.start),
end: self.replacement(range.end)
});
}
Ok(())
}
fn visit_roll_standard_dice(
&mut self,
roll: &RollStandardDice
) -> Result<(), ()>
{
if self.has_readers(*roll)
{
let dest = self.next_rolling_record();
self.replace(roll.dest, dest);
self.emit(RollStandardDice {
dest,
count: self.replacement(roll.count),
faces: self.replacement(roll.faces)
});
}
Ok(())
}
fn visit_roll_custom_dice(
&mut self,
roll: &RollCustomDice
) -> Result<(), ()>
{
if self.has_readers(roll.clone())
{
let dest = self.next_rolling_record();
self.replace(roll.dest, dest);
self.emit(RollCustomDice {
dest,
count: self.replacement(roll.count),
faces: roll.faces.clone()
});
}
Ok(())
}
fn visit_drop_lowest(&mut self, drop: &DropLowest) -> Result<(), ()>
{
if self.has_readers(*drop)
{
self.emit(DropLowest {
dest: self.replacement(drop.dest).try_into().unwrap(),
count: self.replacement(drop.count)
});
}
Ok(())
}
fn visit_drop_highest(&mut self, drop: &DropHighest) -> Result<(), ()>
{
if self.has_readers(*drop)
{
self.emit(DropHighest {
dest: self.replacement(drop.dest).try_into().unwrap(),
count: self.replacement(drop.count)
});
}
Ok(())
}
fn visit_sum_rolling_record(
&mut self,
sum: &SumRollingRecord
) -> Result<(), ()>
{
if self.has_readers(*sum)
{
let dest = self.next_register();
self.replace(sum.dest, dest);
self.emit(SumRollingRecord {
dest,
src: self.replacement(sum.src).try_into().unwrap()
});
}
Ok(())
}
fn visit_add(&mut self, inst: &Add) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Add {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_sub(&mut self, inst: &Sub) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Sub {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_mul(&mut self, inst: &Mul) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Mul {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_div(&mut self, inst: &Div) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Div {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_mod(&mut self, inst: &Mod) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Mod {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_exp(&mut self, inst: &Exp) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Exp {
dest,
op1: self.replacement(inst.op1),
op2: self.replacement(inst.op2)
});
}
Ok(())
}
fn visit_neg(&mut self, inst: &Neg) -> Result<(), ()>
{
if self.has_readers(*inst)
{
let dest = self.next_register();
self.replace(inst.dest, dest);
self.emit(Neg {
dest,
op: self.replacement(inst.op)
});
}
Ok(())
}
fn visit_return(&mut self, inst: &Return) -> Result<(), ()>
{
self.emit(Return {
src: self.replacement(inst.src)
});
Ok(())
}
}
impl AnalyticalVisitor<'_>
{
fn next_register(&mut self) -> RegisterIndex
{
self.1.next_register.allocate()
}
fn next_rolling_record(&mut self) -> RollingRecordIndex
{
self.1.next_rolling_record.allocate()
}
fn replace(
&mut self,
old: impl Into<AddressingMode>,
new: impl Into<AddressingMode>
)
{
self.1.replacements.insert(old.into(), new.into());
}
fn has_readers(&self, inst: impl Into<Instruction>) -> bool
{
self.0
.readers()
.get(&inst.into().destination().unwrap())
.map(|readers| !readers.is_empty())
.unwrap_or(false)
}
#[inline]
fn replacement(&self, op: impl Into<AddressingMode>) -> AddressingMode
{
let op: AddressingMode = op.into();
*self.1.replacements.get(&op).unwrap_or(&op)
}
#[inline]
fn emit(&mut self, inst: impl Into<Instruction>)
{
self.1.instructions.push(inst.into());
}
}