use std::collections::HashMap;
use peephole::{Statement, Program};
pub type LoopBody = Box<[Statement]>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum LoopBalance {
Exact(isize),
RightOnly,
LeftOnly,
Unknown,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct LoopIndex(usize);
#[derive(Debug)]
pub struct LoopBalanceMap(HashMap<LoopIndex, LoopBalance>);
impl LoopBalance {
pub fn is_balanced(self) -> bool {
self == LoopBalance::Exact(0)
}
pub fn is_right_only(self) -> bool {
use self::LoopBalance::*;
match self {
Exact(disp) => disp >= 0,
RightOnly => true,
LeftOnly | Unknown => false,
}
}
pub fn is_left_only(self) -> bool {
use self::LoopBalance::*;
match self {
Exact(disp) => disp <= 0,
LeftOnly => true,
RightOnly | Unknown => false,
}
}
}
impl LoopIndex {
fn from_loop_body(body: &LoopBody) -> Self {
LoopIndex(body.as_ptr() as usize)
}
}
impl LoopBalanceMap {
pub fn new(program: &Program) -> Self {
let mut result = LoopBalanceMap(HashMap::new());
for statement in program {
match *statement {
Statement::Instr(_) => (),
Statement::Loop(ref body) => {
result.analyze_loop(body);
}
}
}
result
}
pub fn get(&self, body: &LoopBody) -> LoopBalance {
*self.0.get(&LoopIndex::from_loop_body(body)).unwrap_or(&LoopBalance::Unknown)
}
fn analyze_loop(&mut self, body: &LoopBody) -> LoopBalance {
use peephole::Statement::*;
use common::Instruction::*;
use self::LoopBalance::*;
let mut net = Exact(0);
for statement in &**body {
match *statement {
Instr(Right(count)) => net = match net {
Exact(disp) => Exact(disp + count as isize),
RightOnly => RightOnly,
_ => Unknown,
},
Instr(Left(count)) => net = match net {
Exact(disp) => Exact(disp - count as isize),
LeftOnly => LeftOnly,
_ => Unknown,
},
Instr(Add(_)) | Instr(In) | Instr(Out) |
Instr(SetZero) | Instr(OffsetAddRight(_)) | Instr(OffsetAddLeft(_)) => (),
Instr(JumpZero(_)) | Instr(JumpNotZero(_)) =>
panic!("unexpected jump instruction"),
Instr(FindZeroRight(_)) =>
net = if net.is_right_only() { RightOnly } else { Unknown },
Instr(FindZeroLeft(_)) =>
net = if net.is_left_only() { LeftOnly } else { Unknown },
Loop(ref body) => {
let body = self.analyze_loop(body);
net = match net {
Exact(disp) if body.is_balanced() => Exact(disp),
_ if net.is_right_only() && body.is_right_only() => RightOnly,
_ if net.is_left_only() && body.is_left_only() => LeftOnly,
_ => Unknown,
}
}
}
}
self.0.insert(LoopIndex::from_loop_body(body), net);
net
}
}