use std::fmt::{Debug};
use std::ops::{Index, IndexMut};
use std::marker::{PhantomData};
use crate::util::{AsUsize};
use super::target::{Label, Word, Lower, Execute, Target, RESULT};
use super::code::{Precision, Variable, Switch, Action, Convention, Marshal, Propagator, EBB, Ending};
use super::optimizer::{LookupLeaf, optimize};
use Precision::*;
array_index! {
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct CaseId(std::num::NonZeroUsize) {
debug_name: "CaseId",
UInt: usize,
}
}
#[derive(Debug)]
struct Retire {
actions: Box<[Action]>,
jump: Option<CaseId>,
}
#[derive(Debug)]
struct Fetch {
actions: Box<[Action]>,
discriminant: Variable,
switch: Switch<CaseId>,
}
#[derive(Debug)]
struct Case {
fetch_parent: Option<CaseId>,
before: Option<Convention>,
label: Label,
retire: Option<Retire>,
fetch: Option<Fetch>,
}
impl Case {
pub fn convention(&self) -> &Convention {
assert!(self.retire.is_some() || self.fetch.is_some());
self.before.as_ref().expect("Incompletely constructed")
}
fn set_convention(&mut self, new: Convention) {
if let Some(old) = &self.before {
assert!(new.refines(old));
} else {
self.before = Some(new);
}
}
}
#[derive(Debug)]
struct Internals {
convention: Convention,
cases: Vec<Case>,
}
impl Internals {
fn new_case(&mut self, fetch_parent: impl Into<Option<CaseId>>) -> CaseId {
let id = CaseId::new(self.cases.len()).unwrap();
self.cases.push(Case {
fetch_parent: fetch_parent.into(),
before: None,
label: Label::new(None),
retire: None,
fetch: None,
});
id
}
fn convention(&self, id: impl Into<Option<CaseId>>) -> &Convention {
id.into().map_or(&self.convention, |id| self[id].convention())
}
fn add_retire(&mut self, lo: &mut impl Lower, id: CaseId, retire: Retire) {
assert!(self[id].fetch.is_none());
let mut propagator = Propagator::new(self.convention(retire.jump));
for &action in retire.actions.iter().rev() {
propagator.action(action);
}
let before = propagator.before();
*lo.slots_used_mut() = before.slots_used;
let mut here = lo.here();
lo.steal(&mut self[id].label, &mut here);
self[id].label = here;
lo.actions(&retire.actions);
let slots_used = *lo.slots_used_mut();
assert_eq!(self.convention(retire.jump).slots_used, slots_used);
if let Some(jump) = retire.jump {
lo.jump(&mut self[jump].label);
} else {
lo.epilogue()
}
self[id].set_convention(before);
self[id].retire = Some(retire);
}
fn add_fetch(&mut self, lo: &mut impl Lower, id: CaseId, fetch: Fetch) {
assert!(self[id].fetch.is_none());
let mut propagator = Propagator::switch(
fetch.discriminant,
&fetch.switch,
|&child| self[child].convention(),
);
for &action in fetch.actions.iter().rev() {
propagator.action(action);
}
if self[id].retire.is_some() {
propagator.branch(self[id].convention());
}
let before = propagator.before();
*lo.slots_used_mut() = before.slots_used;
let mut here = lo.here();
lo.steal(&mut self[id].label, &mut here);
self[id].label = here;
lo.actions(&fetch.actions);
let slots_used = *lo.slots_used_mut();
let check_child = |child: &Case| {
assert_eq!(child.convention().slots_used, slots_used);
assert_eq!(child.fetch_parent, Some(id));
};
let Switch {ref cases, ref default_} = fetch.switch;
for (index, &case) in cases.iter().enumerate() {
check_child(&self[case]);
lo.if_eq((fetch.discriminant, index as u64), &mut self[case].label);
}
check_child(&self[**default_]);
lo.jump(&mut self[**default_].label);
self[id].set_convention(before);
self[id].fetch = Some(fetch);
}
}
impl Index<CaseId> for Internals {
type Output = Case;
fn index(&self, id: CaseId) -> &Self::Output {
&self.cases[id.as_usize()]
}
}
impl IndexMut<CaseId> for Internals {
fn index_mut(&mut self, id: CaseId) -> &mut Self::Output {
&mut self.cases[id.as_usize()]
}
}
#[allow(clippy::module_name_repetitions)]
pub struct Engine<T: Target> {
_target: T,
lowerer: T::Lowerer,
i: Internals,
}
impl<T: Target> std::fmt::Debug for Engine<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.debug_struct("Engine")
.field("cases", &self.i.cases)
.finish()
}
}
impl<T: Target> Engine<T> {
pub fn new(target: T) -> Self {
let lowerer = target.lowerer();
let i = Internals {
convention: Convention::default(),
cases: Vec::new(),
};
Engine {_target: target, lowerer, i}
}
pub fn build<L: Debug + Clone>(
&mut self,
id: CaseId,
ebb: &EBB<L>,
to_case: &impl Fn(L) -> CaseId,
) {
let engine_wrapper = EngineWrapper {engine: &*self, to_case, _l: PhantomData};
let ebb = optimize(self.i.convention(id), ebb, &engine_wrapper);
self.build_inner(id, &ebb, to_case)
}
fn build_inner<L: Clone>(
&mut self,
id: CaseId,
ebb: &EBB<L>,
to_case: &impl Fn(L) -> CaseId,
) {
let ebb_actions = ebb.actions.iter().copied().collect();
match ebb.ending {
Ending::Leaf(ref leaf) => {
let jump = to_case(leaf.clone());
let retire = Retire {actions: ebb_actions, jump: Some(jump)};
self.i.add_retire(&mut self.lowerer, id, retire);
},
Ending::Switch(discriminant, ref switch) => {
let switch = switch.map(|child_ebb| {
let child = self.i.new_case(Some(id));
self.build_inner(child, child_ebb, to_case);
child
});
let fetch = Fetch {actions: ebb_actions, discriminant, switch};
self.i.add_fetch(&mut self.lowerer, id, fetch);
},
}
}
pub fn new_entry(&mut self, marshal: &Marshal, exit_value: i64) -> (Label, CaseId) {
assert!(exit_value >= 0);
let id = self.i.new_case(None);
let mut actions = Vec::new();
actions.extend(marshal.epilogue.iter().copied());
actions.push(Action::Constant(P64, RESULT, exit_value));
self.i.add_retire(&mut self.lowerer, id, Retire {actions: actions.into(), jump: None});
let lo = &mut self.lowerer;
*lo.slots_used_mut() = 0;
let label = lo.here();
lo.prologue();
lo.actions(&marshal.prologue);
assert_eq!(*lo.slots_used_mut(), self.i[id].convention().slots_used);
lo.jump(&mut self.i[id].label);
(label, id)
}
fn hot_path(&self, mut id: CaseId) -> Option<EBB<CaseId>> {
let mut actions = Vec::new();
loop {
if let Some(fetch) = &self.i[id].fetch {
actions.extend(fetch.actions.iter().copied());
return Some(EBB {
actions: actions.into(),
ending: Ending::Switch(
fetch.discriminant,
fetch.switch.map(|&jump| EBB {
actions: Box::new([]),
ending: Ending::Leaf(jump),
},
)),
});
}
if let Some(retire) = &self.i[id].retire {
actions.extend(retire.actions.iter().copied());
if let Some(jump) = retire.jump {
id = jump;
continue;
} else {
return None;
}
}
panic!("Case has neither Fetch nor Retire");
}
}
#[allow(unused)] fn specialize(&mut self, id: CaseId) {
assert!(self.i[id].fetch.is_none());
if let Some(ebb) = self.hot_path(id) {
self.build(id, &ebb, &|c| c);
}
}
pub unsafe fn run(&mut self, label: &Label, global: *mut ()) -> Word {
self.lowerer.execute(label, |f| {
f(global)
})
}
}
struct EngineWrapper<'a, T: Target, L: Debug + Clone, F: Fn(L) -> CaseId> {
engine: &'a Engine<T>,
to_case: &'a F,
_l: PhantomData<L>,
}
impl<'a, T: Target, L: Debug + Clone, F: Fn(L) -> CaseId> LookupLeaf for EngineWrapper<'a, T, L, F> {
type Leaf = L;
fn after(&self, leaf: &L) -> &Convention {
self.engine.i.convention((self.to_case)(leaf.clone()))
}
fn weight(&self, _leaf: &L) -> usize {
1 }
}