use std::collections::BTreeMap;
use std::iter::FusedIterator;
use std::mem;
use crate::compiler::args::Arg;
use crate::compiler::instr::flow::{Element, ElementPath, ElementPathBuf, ElementSelector};
use crate::compiler::instr::InstrDef;
use crate::compiler::variables::{Conversion, StackTracker, VarAssigner, VarAssignment, VarId};
use crate::microcode::{Microcode, ValType};
#[derive(Debug)]
pub enum StateElement {
Microcode(StateMicrocode),
Block(StateBlock),
Branch(StateBranch),
ChangeState(StateChange),
}
impl StateElement {
pub fn extend_block(&mut self, other: StateElement) {
match (self, other) {
(Self::Block(ref mut self_block), Self::Block(mut other_block)) => {
self_block.elements.append(&mut other_block.elements)
}
(Self::Block(ref mut self_block), other) => self_block.elements.push(other),
(this, mut other @ Self::Block(_)) => {
mem::swap(this, &mut other);
match this {
Self::Block(ref mut old_other_block) => {
old_other_block
.elements
.insert(0, other)
}
_ => unreachable!(),
}
}
(this, other) => {
let old_self = mem::replace(
this,
StateElement::Block(StateBlock {
elements: Vec::with_capacity(2),
}),
);
match this {
Self::Block(ref mut self_block) => {
self_block.elements.push(old_self);
self_block.elements.push(other);
}
_ => unreachable!(),
}
}
}
}
pub fn ends_in_state_change_or_terminal(&self) -> bool {
match self {
StateElement::Microcode(code) => code.microcode.is_terminal(),
StateElement::Block(StateBlock { elements }) => match elements.last() {
Some(last) => last.ends_in_state_change_or_terminal(),
None => false,
},
StateElement::Branch(StateBranch {
code_if_true,
code_if_false,
..
}) => {
code_if_true.ends_in_state_change_or_terminal()
&& code_if_false.ends_in_state_change_or_terminal()
}
StateElement::ChangeState(_) => true,
}
}
}
impl Default for StateElement {
fn default() -> Self {
StateElement::Block(Default::default())
}
}
#[derive(Debug)]
pub struct StateMicrocode {
pub microcode: Microcode,
pub conversions: Vec<Conversion>,
pub args: Vec<Arg<VarAssignment>>,
pub returns: Vec<VarAssignment>,
}
impl StateMicrocode {
fn build_assignment(microcode: Microcode, parent_stack: &mut StackTracker) -> Self {
let desc = microcode.descriptor();
let mut conversions = vec![];
let args = desc
.args
.into_iter()
.map(|arg| match arg {
Arg::Literal(lit) => Arg::Literal(lit),
Arg::StackValue(val) => {
let (assignment, c) = parent_stack.pop(val);
conversions.extend_from_slice(&c);
Arg::StackValue(assignment)
}
})
.collect();
let returns = desc
.returns
.into_iter()
.map(|ret| parent_stack.push(ret))
.collect();
Self {
microcode,
conversions,
args,
returns,
}
}
}
#[derive(Debug, Default)]
pub struct StateBlock {
pub elements: Vec<StateElement>,
}
#[derive(Debug)]
pub struct StateBranch {
pub cond_conversion: Vec<Conversion>,
pub cond: VarId,
pub code_if_true: Box<StateElement>,
pub true_returns: Vec<VarAssignment>,
pub true_end_conv: Vec<Conversion>,
pub code_if_false: Box<StateElement>,
pub false_end_conv: Vec<Conversion>,
pub false_returns: Vec<VarAssignment>,
pub pushes: Vec<VarAssignment>,
}
#[derive(Debug, Clone)]
pub struct StateChange {
pub next_state: ElementPathBuf,
pub saved_vars: Vec<VarAssignment>,
}
#[derive(Debug)]
pub struct State {
pub num: usize,
pub stored_stack: Vec<VarAssignment>,
pub element: StateElement,
}
pub struct InstrStates {
states: BTreeMap<ElementPathBuf, State>,
}
impl InstrStates {
pub fn new(instr: &InstrDef) -> Self {
Self::from_element(instr.flow())
}
fn from_element(root: &Element) -> Self {
let mut states: BTreeMap<ElementPathBuf, State> = Default::default();
let mut traversal = vec![StateChange {
next_state: ElementPathBuf::new(),
saved_vars: Vec::new(),
}];
let assigner = VarAssigner::default();
while let Some(prev_transition) = traversal.pop() {
if states.contains_key(&prev_transition.next_state) {
continue;
}
let mut stack =
StackTracker::root_with_stack(&assigner, prev_transition.saved_vars.clone());
let element = build_state_from(
prev_transition.next_state.clone(),
root,
&mut stack,
&mut traversal,
);
let state = State {
num: states.len(),
stored_stack: prev_transition.saved_vars,
element,
};
states.insert(prev_transition.next_state, state);
}
Self { states }
}
#[inline]
pub fn get_state(&self, path: &ElementPath) -> Option<&State> {
self.states.get(path)
}
pub fn state_starts(
&self,
) -> impl Iterator<Item = &ElementPath> + DoubleEndedIterator + ExactSizeIterator + FusedIterator
{
self.states.keys().map(|path| path.as_path())
}
pub fn states(
&self,
) -> impl Iterator<Item = (&ElementPath, &State)>
+ DoubleEndedIterator
+ ExactSizeIterator
+ FusedIterator {
self.states.iter().map(|(k, v)| (k.as_path(), v))
}
#[inline]
pub fn into_states(self) -> <BTreeMap<ElementPathBuf, State> as IntoIterator>::IntoIter {
self.states.into_iter()
}
}
impl IntoIterator for InstrStates {
type IntoIter = <BTreeMap<ElementPathBuf, State> as IntoIterator>::IntoIter;
type Item = <BTreeMap<ElementPathBuf, State> as IntoIterator>::Item;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.into_states()
}
}
fn build_state_from(
mut path: ElementPathBuf,
root: &Element,
parent_stack: &mut StackTracker,
traversal: &mut Vec<StateChange>,
) -> StateElement {
let mut state = StateElement::default();
loop {
state.extend_block(build_state_only(&path, root, parent_stack, traversal));
if state.ends_in_state_change_or_terminal() {
break;
}
path = next_in_block_or_parent(path, root)
.expect("Hit end of root element without a terminal");
}
state
}
fn build_state_only(
path: &ElementPath,
root: &Element,
parent_stack: &mut StackTracker,
traversal: &mut Vec<StateChange>,
) -> StateElement {
match &root[path] {
Element::Microcode(Microcode::Yield) => {
let next_state = next_in_block_or_parent(path.to_buf(), root)
.expect("Yield found at end of instruction");
let state_change = StateChange {
next_state,
saved_vars: parent_stack.snapshot(),
};
traversal.push(state_change.clone());
StateElement::ChangeState(state_change)
}
Element::Microcode(code) => {
StateElement::Microcode(StateMicrocode::build_assignment(*code, parent_stack))
}
Element::Block(block) => {
let mut child_path = path.to_buf();
child_path.push(0);
let mut elements = Vec::with_capacity(block.elements.len());
for idx in 0..block.elements.len() {
*child_path.last_mut().unwrap() = ElementSelector::Block(idx);
let element = build_state_only(&child_path, root, parent_stack, traversal);
let stop = element.ends_in_state_change_or_terminal();
elements.push(element);
if stop {
break;
}
}
StateElement::Block(StateBlock { elements })
}
Element::Branch(_) => {
let (cond, cond_conversion) = parent_stack.pop(ValType::Bool);
let mut child_path = path.to_buf();
child_path.push(true);
let mut true_stack = parent_stack.make_child();
let code_if_true = Box::new(build_state_only(
&child_path,
root,
&mut true_stack,
traversal,
));
*child_path.last_mut().unwrap() = ElementSelector::Branch(false);
let mut false_stack = parent_stack.make_child();
let code_if_false = Box::new(build_state_only(
&child_path,
root,
&mut false_stack,
traversal,
));
let true_terminal_or_yield = code_if_true.ends_in_state_change_or_terminal();
let false_terminal_or_yield = code_if_false.ends_in_state_change_or_terminal();
let mut true_end_conv = vec![];
let mut false_end_conv = vec![];
let pushes = if true_terminal_or_yield && false_terminal_or_yield {
true_stack.local_stack.clear();
false_stack.local_stack.clear();
vec![]
} else if true_terminal_or_yield {
*parent_stack = *false_stack.parent.unwrap();
true_stack.local_stack.clear();
false_stack
.local_stack
.iter()
.map(|asgn| parent_stack.push(asgn.val))
.collect()
} else if false_terminal_or_yield {
*parent_stack = *true_stack.parent.unwrap();
false_stack.local_stack.clear();
true_stack
.local_stack
.iter()
.map(|asgn| parent_stack.push(asgn.val))
.collect()
} else {
if true_stack.local_bytes() < false_stack.local_bytes() {
let mut true_pushes_rev = Vec::with_capacity(false_stack.local_stack.len());
for VarAssignment { val, .. } in false_stack.local_stack.iter().rev().copied() {
let (var, conv) = true_stack.pop(val);
true_end_conv.extend_from_slice(&conv);
true_pushes_rev.push(var);
}
true_pushes_rev.reverse();
assert!(true_stack.local_stack.is_empty());
true_stack.local_stack = true_pushes_rev;
} else if true_stack.local_bytes() > false_stack.local_bytes() {
let mut false_pushes_rev = Vec::with_capacity(true_stack.local_stack.len());
for VarAssignment { val, .. } in true_stack.local_stack.iter().rev().copied() {
let (var, conv) = false_stack.pop(val);
false_end_conv.extend_from_slice(&conv);
false_pushes_rev.push(var);
}
false_pushes_rev.reverse();
assert!(false_stack.local_stack.is_empty());
false_stack.local_stack = false_pushes_rev;
}
assert!(true_stack.parent == false_stack.parent);
assert!(
true_stack
.local_stack
.iter()
.map(|asgn| asgn.val)
.collect::<Vec<_>>()
== false_stack
.local_stack
.iter()
.map(|asgn| asgn.val)
.collect::<Vec<_>>()
);
*parent_stack = *true_stack.parent.unwrap();
true_stack
.local_stack
.iter()
.map(|asgn| parent_stack.push(asgn.val))
.collect()
};
StateElement::Branch(StateBranch {
cond_conversion,
cond: cond.var,
code_if_true,
true_end_conv,
true_returns: true_stack.local_stack,
code_if_false,
false_end_conv,
false_returns: false_stack.local_stack,
pushes,
})
}
}
}
fn next_in_block_or_parent(mut path: ElementPathBuf, root: &Element) -> Option<ElementPathBuf> {
match &root[path.parent()?] {
Element::Microcode(_) => panic!(
"Microcode instructions don't have children. Previous path must have been invalid."
),
Element::Block(block) => {
if path.last().unwrap().unwrap_block() + 1 < block.elements.len() {
*path.last_mut().unwrap().unwrap_block_mut() += 1;
Some(path)
} else {
path.pop().unwrap();
next_in_block_or_parent(path, root)
}
}
Element::Branch(_) => {
path.pop().unwrap();
next_in_block_or_parent(path, root)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::opcode::{CBOpcode, InternalFetch, Opcode};
#[test]
fn assign_all_instrs() {
InstrStates::new(&InternalFetch.into());
for opcode in 0..=0xffu8 {
InstrStates::new(&Opcode::decode(opcode).into());
}
for cbopcode in 0..=0xffu8 {
InstrStates::new(&CBOpcode::decode(cbopcode).into());
}
}
}