From 9e4f4345244ba5f8936b2c3db074142fbc9fe4cb Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Tue, 5 Dec 2023 17:55:30 -0600 Subject: [PATCH 01/62] Adding `Concat` and `ConcatField` --- starlight/Cargo.toml | 4 +-- starlight/src/ensemble/state.rs | 45 ++++++++++++++++++++++++++++++ starlight/src/lower/lower_state.rs | 4 +-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/starlight/Cargo.toml b/starlight/Cargo.toml index 0605a77f..26d2094f 100644 --- a/starlight/Cargo.toml +++ b/starlight/Cargo.toml @@ -12,8 +12,8 @@ keywords = ["dag", "rtl", "hdl"] categories = [] [dependencies] -#awint = { path = "../../awint/awint", default-features = false, features = ["rand_support", "dag"] } -awint = { version = "0.14", default-features = false, features = ["rand_support", "dag"] } +awint = { path = "../../awint/awint", default-features = false, features = ["rand_support", "dag"] } +#awint = { version = "0.14", default-features = false, features = ["rand_support", "dag"] } rand_xoshiro = { version = "0.6", default-features = false } [features] diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index ab8767f9..3eeed5d6 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -619,6 +619,51 @@ impl Ensemble { self.union_equiv(p_equiv0, p_equiv1).unwrap(); } } + Concat(ref concat) => { + let concat_len = concat.len(); + self.initialize_state_bits_if_needed(p_state).unwrap(); + let total_len = self.stator.states[p_state].p_self_bits.len(); + let mut to = 0; + for c_i in 0..concat_len { + let c = if let Concat(ref concat) = self.stator.states[p_state].op { + concat.as_slice()[c_i] + } else { + unreachable!() + }; + let len = self.stator.states[c].p_self_bits.len(); + for i in 0..len { + let p_equiv0 = + self.stator.states[p_state].p_self_bits[to + i].unwrap(); + let p_equiv1 = self.stator.states[c].p_self_bits[i].unwrap(); + self.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + to += len; + } + assert_eq!(total_len, to); + } + ConcatFields(ref concat) => { + let concat_len = concat.len(); + self.initialize_state_bits_if_needed(p_state).unwrap(); + let total_len = self.stator.states[p_state].p_self_bits.len(); + let mut to = 0; + for c_i in 0..concat_len { + let (c, (from, width)) = + if let ConcatFields(ref concat) = self.stator.states[p_state].op { + (concat.t_as_slice()[c_i], concat.field_as_slice()[c_i]) + } else { + unreachable!() + }; + let len = width.get(); + for i in 0..len { + let p_equiv0 = + self.stator.states[p_state].p_self_bits[to + i].unwrap(); + let p_equiv1 = self.stator.states[c].p_self_bits[from + i].unwrap(); + self.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + to += len; + } + assert_eq!(total_len, to); + } StaticGet([bits], inx) => { self.initialize_state_bits_if_needed(p_state).unwrap(); let len = self.stator.states[bits].p_self_bits.len(); diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 0ac3349d..55f5ca85 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -35,8 +35,8 @@ pub fn lower_state( ) -> Result { match start_op { Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticLut(..) | StaticGet(..) - | StaticSet(..) => return Ok(true), + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | Concat(_) | ConcatFields(_) + | StaticLut(..) | StaticGet(..) | StaticSet(..) => return Ok(true), Lut([lut, inx]) => { if m.is_literal(lut) { return Err(EvalError::OtherStr( From 2aa7cfdc9dba753582c85696cd9286d8f38506e5 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Tue, 5 Dec 2023 18:00:48 -0600 Subject: [PATCH 02/62] move around lowering --- starlight/src/ensemble/state.rs | 356 +--------- starlight/src/lower.rs | 3 +- starlight/src/lower/lower_op.rs | 708 ++++++++++++++++++++ starlight/src/lower/lower_state.rs | 1006 +++++++++------------------- 4 files changed, 1042 insertions(+), 1031 deletions(-) create mode 100644 starlight/src/lower/lower_op.rs diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 3eeed5d6..e81ee09e 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -1,7 +1,7 @@ use std::{fmt::Write, num::NonZeroUsize}; use awint::awint_dag::{ - smallvec::{smallvec, SmallVec}, + smallvec::SmallVec, triple_arena::{Advancer, Arena}, EAwi, EvalError, EvalResult, Location, Op::{self, *}, @@ -15,7 +15,6 @@ use crate::{ Ensemble, PBack, Referent, Value, }, epoch::EpochShared, - lower::{lower_state, LowerManagement}, }; /// Represents a single state that `awint_dag::mimick::Bits` is in at one point @@ -200,359 +199,6 @@ impl Ensemble { } } - /// Used for forbidden meta psuedo-DSL techniques in which a single state is - /// replaced by more basic states. - pub fn graft(&mut self, p_state: PState, operands: &[PState]) -> Result<(), EvalError> { - #[cfg(debug_assertions)] - { - if (self.stator.states[p_state].op.operands_len() + 1) != operands.len() { - return Err(EvalError::WrongNumberOfOperands) - } - for (i, op) in self.stator.states[p_state].op.operands().iter().enumerate() { - let current_nzbw = self.stator.states[operands[i + 1]].nzbw; - let current_is_opaque = self.stator.states[operands[i + 1]].op.is_opaque(); - if self.stator.states[op].nzbw != current_nzbw { - return Err(EvalError::OtherString(format!( - "operand {}: a bitwidth of {:?} is trying to be grafted to a bitwidth of \ - {:?}", - i, current_nzbw, self.stator.states[op].nzbw - ))) - } - if !current_is_opaque { - return Err(EvalError::ExpectedOpaque) - } - } - if self.stator.states[p_state].nzbw != self.stator.states[operands[0]].nzbw { - return Err(EvalError::WrongBitwidth) - } - } - - // TODO what do we do when we make multi-output things - // graft input - for i in 1..operands.len() { - let grafted = operands[i]; - let graftee = self.stator.states.get(p_state).unwrap().op.operands()[i - 1]; - if let Some(grafted) = self.stator.states.get_mut(grafted) { - // change the grafted `Opaque` into a `Copy` that routes to the graftee instead - // of needing to change all the operands of potentially many nodes - grafted.op = Copy([graftee]); - } else { - // else the operand is not used because it was optimized away, this is removing - // a tree outside of the grafted part - self.dec_rc(graftee).unwrap(); - } - } - - // graft output - let grafted = operands[0]; - self.stator.states.get_mut(p_state).unwrap().op = Copy([grafted]); - self.stator.states[grafted].rc = self.stator.states[grafted].rc.checked_add(1).unwrap(); - - Ok(()) - } - - pub fn lower_state(epoch_shared: &EpochShared, p_state: PState) -> Result { - // TODO optimization to remove unused nodes early - //let epoch = StateEpoch::new(); - struct Tmp<'a> { - ptr: PState, - epoch_shared: &'a EpochShared, - } - impl<'a> LowerManagement for Tmp<'a> { - fn graft(&mut self, operands: &[PState]) { - self.epoch_shared - .epoch_data - .borrow_mut() - .ensemble - .graft(self.ptr, operands) - .unwrap(); - } - - fn get_nzbw(&self, p: PState) -> NonZeroUsize { - self.epoch_shared - .epoch_data - .borrow() - .ensemble - .stator - .states - .get(p) - .unwrap() - .nzbw - } - - fn is_literal(&self, p: PState) -> bool { - self.epoch_shared - .epoch_data - .borrow() - .ensemble - .stator - .states - .get(p) - .unwrap() - .op - .is_literal() - } - - fn usize(&self, p: PState) -> usize { - if let Literal(ref lit) = self - .epoch_shared - .epoch_data - .borrow() - .ensemble - .stator - .states - .get(p) - .unwrap() - .op - { - if lit.bw() != 64 { - panic!() - } - lit.to_usize() - } else { - panic!() - } - } - - fn bool(&self, p: PState) -> bool { - if let Literal(ref lit) = self - .epoch_shared - .epoch_data - .borrow() - .ensemble - .stator - .states - .get(p) - .unwrap() - .op - { - if lit.bw() != 1 { - panic!() - } - lit.to_bool() - } else { - panic!() - } - } - - fn dec_rc(&mut self, p: PState) { - self.epoch_shared - .epoch_data - .borrow_mut() - .ensemble - .dec_rc(p) - .unwrap() - } - } - let lock = epoch_shared.epoch_data.borrow(); - let state = lock.ensemble.stator.states.get(p_state).unwrap(); - let start_op = state.op.clone(); - let out_w = state.nzbw; - drop(lock); - lower_state(start_op, out_w, Tmp { - ptr: p_state, - epoch_shared, - }) - } - - /// Lowers the rootward tree from `p_state` down to the elementary `Op`s - pub fn dfs_lower_states_to_elementary( - epoch_shared: &EpochShared, - p_state: PState, - ) -> Result<(), EvalError> { - let mut unimplemented = false; - let mut lock = epoch_shared.epoch_data.borrow_mut(); - if let Some(state) = lock.ensemble.stator.states.get(p_state) { - if state.lowered_to_elementary { - return Ok(()) - } - } else { - return Err(EvalError::InvalidPtr) - } - lock.ensemble.stator.states[p_state].lowered_to_elementary = true; - - // NOTE be sure to reset this before returning from the function - lock.keep_flag = false; - drop(lock); - let mut path: Vec<(usize, PState)> = vec![(0, p_state)]; - loop { - let (i, p_state) = path[path.len() - 1]; - let mut lock = epoch_shared.epoch_data.borrow_mut(); - let state = &lock.ensemble.stator.states[p_state]; - let ops = state.op.operands(); - if ops.is_empty() { - // reached a root - path.pop().unwrap(); - if path.is_empty() { - break - } - path.last_mut().unwrap().0 += 1; - } else if i >= ops.len() { - // checked all sources, attempt evaluation first, this is crucial in preventing - // wasted work in multiple layer lowerings - match lock.ensemble.eval_state(p_state) { - Ok(()) => { - path.pop().unwrap(); - if path.is_empty() { - break - } else { - continue - } - } - // Continue on to lowering - Err(EvalError::Unevaluatable) => (), - Err(e) => { - lock.ensemble.stator.states[p_state].err = Some(e.clone()); - return Err(e) - } - } - let needs_lower = match lock.ensemble.stator.states[p_state].op { - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) - | StaticSet(..) | StaticLut(..) => false, - Lut([lut, inx]) => { - if let Literal(ref lit) = lock.ensemble.stator.states[lut].op { - let lit = lit.clone(); - let out_w = lock.ensemble.stator.states[p_state].nzbw.get(); - let inx_w = lock.ensemble.stator.states[inx].nzbw.get(); - let no_op = if let Ok(inx_w) = u32::try_from(inx_w) { - if let Some(num_entries) = 1usize.checked_shl(inx_w) { - (out_w * num_entries) != lit.bw() - } else { - true - } - } else { - true - }; - if no_op { - // TODO should I add the extra arg to `Lut` to fix this edge case? - lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); - lock.ensemble.dec_rc(inx).unwrap(); - } else { - lock.ensemble.stator.states[p_state].op = StaticLut([inx], lit); - } - lock.ensemble.dec_rc(lut).unwrap(); - false - } else { - true - } - } - Get([bits, inx]) => { - if let Literal(ref lit) = lock.ensemble.stator.states[inx].op { - let lit = lit.clone(); - let lit_u = lit.to_usize(); - if lit_u >= lock.ensemble.stator.states[bits].nzbw.get() { - // TODO I realize now that no-op `get` specifically is fundamentally - // ill-defined to some extend because it returns `Option`, it - // must be asserted against, this - // provides the next best thing - lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); - lock.ensemble.dec_rc(bits).unwrap(); - } else { - lock.ensemble.stator.states[p_state].op = StaticGet([bits], lit_u); - } - lock.ensemble.dec_rc(inx).unwrap(); - false - } else { - true - } - } - Set([bits, inx, bit]) => { - if let Literal(ref lit) = lock.ensemble.stator.states[inx].op { - let lit = lit.clone(); - let lit_u = lit.to_usize(); - if lit_u >= lock.ensemble.stator.states[bits].nzbw.get() { - // no-op - lock.ensemble.stator.states[p_state].op = Copy([bits]); - lock.ensemble.dec_rc(bit).unwrap(); - } else { - lock.ensemble.stator.states[p_state].op = - StaticSet([bits, bit], lit.to_usize()); - } - lock.ensemble.dec_rc(inx).unwrap(); - false - } else { - true - } - } - _ => true, - }; - drop(lock); - let lowering_done = if needs_lower { - // this is used to be able to remove ultimately unused temporaries - let mut temporary = EpochShared::shared_with(epoch_shared); - temporary.set_as_current(); - let lowering_done = match Ensemble::lower_state(&temporary, p_state) { - Ok(lowering_done) => lowering_done, - Err(EvalError::Unimplemented) => { - // finish lowering as much as possible - unimplemented = true; - true - } - Err(e) => { - temporary.remove_as_current(); - let mut lock = epoch_shared.epoch_data.borrow_mut(); - lock.ensemble.stator.states[p_state].err = Some(e.clone()); - lock.keep_flag = true; - return Err(e) - } - }; - // shouldn't be adding additional assertions - // TODO after migrating the old lowering tests to a starlight-like system, make - // sure there are none using assertions assert!(temporary. - // assertions_empty()); - let states = temporary.take_states_added(); - temporary.remove_as_current(); - let mut lock = epoch_shared.epoch_data.borrow_mut(); - for p_state in states { - let state = &lock.ensemble.stator.states[p_state]; - if (!state.keep) && (state.rc == 0) { - lock.ensemble.remove_state(p_state).unwrap(); - } - } - lowering_done - } else { - true - }; - if lowering_done { - path.pop().unwrap(); - if path.is_empty() { - break - } - } else { - // else do not call `path.pop`, restart the DFS here - path.last_mut().unwrap().0 = 0; - } - } else { - let mut p_next = ops[i]; - if lock.ensemble.stator.states[p_next].lowered_to_elementary { - // do not visit - path.last_mut().unwrap().0 += 1; - } else { - while let Copy([a]) = lock.ensemble.stator.states[p_next].op { - // special optimization case: forward Copies - lock.ensemble.stator.states[p_state].op.operands_mut()[i] = a; - let rc = &mut lock.ensemble.stator.states[a].rc; - *rc = (*rc).checked_add(1).unwrap(); - lock.ensemble.dec_rc(p_next).unwrap(); - p_next = a; - } - lock.ensemble.stator.states[p_next].lowered_to_elementary = true; - path.push((0, p_next)); - } - drop(lock); - } - } - - let mut lock = epoch_shared.epoch_data.borrow_mut(); - lock.keep_flag = true; - - if unimplemented { - Err(EvalError::Unimplemented) - } else { - Ok(()) - } - } - /// Assuming that the rootward tree from `p_state` is lowered down to the /// elementary `Op`s, this will create the `TNode` network pub fn dfs_lower_elementary_to_tnodes(&mut self, p_state: PState) -> Result<(), EvalError> { diff --git a/starlight/src/lower.rs b/starlight/src/lower.rs index 4c0f0e6d..9a145437 100644 --- a/starlight/src/lower.rs +++ b/starlight/src/lower.rs @@ -1,4 +1,5 @@ +mod lower_op; mod lower_state; mod meta; -pub use lower_state::{lower_state, LowerManagement}; +pub use lower_op::{lower_op, LowerManagement}; diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs new file mode 100644 index 00000000..40697077 --- /dev/null +++ b/starlight/src/lower/lower_op.rs @@ -0,0 +1,708 @@ +//! Lowers everything into LUT form + +// TODO https://github.com/rust-lang/rust-clippy/issues/10577 +#![allow(clippy::redundant_clone)] + +use std::{cmp::min, num::NonZeroUsize}; + +use awint::{ + awint_dag::{ + triple_arena::Ptr, + DummyDefault, EvalError, Lineage, + Op::{self, *}, + PState, + }, + bw, + dag::{awi, inlawi, Awi, Bits, InlAwi}, +}; + +use super::meta::*; + +pub trait LowerManagement { + fn graft(&mut self, output_and_operands: &[PState]); + fn get_nzbw(&self, p: P) -> NonZeroUsize; + fn is_literal(&self, p: P) -> bool; + fn usize(&self, p: P) -> usize; + fn bool(&self, p: P) -> bool; + fn dec_rc(&mut self, p: P); +} + +/// Returns if the lowering is done +pub fn lower_op( + start_op: Op

, + out_w: NonZeroUsize, + mut m: impl LowerManagement

, +) -> Result { + match start_op { + Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | Concat(_) | ConcatFields(_) + | StaticLut(..) | StaticGet(..) | StaticSet(..) => return Ok(true), + Lut([lut, inx]) => { + if m.is_literal(lut) { + return Err(EvalError::OtherStr( + "this needs to be handled before this function", + )); + } else { + let mut out = Awi::zero(out_w); + let lut = Awi::opaque(m.get_nzbw(lut)); + let inx = Awi::opaque(m.get_nzbw(inx)); + dynamic_to_static_lut(&mut out, &lut, &inx); + m.graft(&[out.state(), lut.state(), inx.state()]); + } + } + Get([bits, inx]) => { + if m.is_literal(inx) { + return Err(EvalError::OtherStr( + "this needs to be handled before this function", + )); + } else { + let bits = Awi::opaque(m.get_nzbw(bits)); + let inx = Awi::opaque(m.get_nzbw(inx)); + let out = dynamic_to_static_get(&bits, &inx); + m.graft(&[out.state(), bits.state(), inx.state()]); + } + } + Set([bits, inx, bit]) => { + if m.is_literal(inx) { + return Err(EvalError::OtherStr( + "this needs to be handled before this function", + )); + } else { + let bits = Awi::opaque(m.get_nzbw(bits)); + let inx = Awi::opaque(m.get_nzbw(inx)); + let bit = Awi::opaque(m.get_nzbw(bit)); + let out = dynamic_to_static_set(&bits, &inx, &bit); + m.graft(&[out.state(), bits.state(), inx.state(), bit.state()]); + } + } + FieldBit([lhs, to, rhs, from]) => { + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let from = Awi::opaque(m.get_nzbw(from)); + let bit = rhs.get(from.to_usize()).unwrap(); + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let to = Awi::opaque(m.get_nzbw(to)); + // keep `lhs` the same, `out` has the set bit + let mut out = lhs.clone(); + out.set(to.to_usize(), bit).unwrap(); + m.graft(&[ + out.state(), + lhs.state(), + to.state(), + rhs.state(), + from.state(), + ]); + } + ZeroResize([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = resize(&x, out_w, false); + m.graft(&[out.state(), x.state()]); + } + SignResize([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = resize(&x, out_w, true); + m.graft(&[out.state(), x.state()]); + } + Resize([x, b]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let b = Awi::opaque(m.get_nzbw(b)); + let out = resize_cond(&x, out_w, &b); + m.graft(&[out.state(), x.state(), b.state()]); + } + Lsb([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = x.get(0).unwrap(); + m.graft(&[out.state(), x.state()]); + } + Msb([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = x.get(x.bw() - 1).unwrap(); + m.graft(&[out.state(), x.state()]); + } + FieldWidth([lhs, rhs, width]) => { + let lhs_w = m.get_nzbw(lhs); + let rhs_w = m.get_nzbw(rhs); + let width_w = m.get_nzbw(width); + if m.is_literal(width) { + let width_u = m.usize(width); + let lhs = Awi::opaque(lhs_w); + let rhs = Awi::opaque(rhs_w); + // If `width_u` is out of bounds `out` is created as a no-op of `lhs` as + // expected + let out = static_field(&lhs, 0, &rhs, 0, width_u).0; + m.graft(&[ + out.state(), + lhs.state(), + rhs.state(), + Awi::opaque(width_w).state(), + ]); + } else { + let lhs = Awi::opaque(lhs_w); + let rhs = Awi::opaque(rhs_w); + let width = Awi::opaque(width_w); + let fail = width.ugt(&InlAwi::from_usize(lhs_w.get())).unwrap() + | width.ugt(&InlAwi::from_usize(rhs_w.get())).unwrap(); + let mut tmp_width = width.clone(); + tmp_width.mux_(&InlAwi::from_usize(0), fail).unwrap(); + let out = field_width(&lhs, &rhs, &tmp_width); + m.graft(&[out.state(), lhs.state(), rhs.state(), width.state()]); + } + } + Funnel([x, s]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = funnel_(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + FieldFrom([lhs, rhs, from, width]) => { + let lhs_w = m.get_nzbw(lhs); + let rhs_w = m.get_nzbw(rhs); + let width_w = m.get_nzbw(width); + if m.is_literal(from) { + let lhs = Awi::opaque(lhs_w); + let rhs = Awi::opaque(rhs_w); + let width = Awi::opaque(m.get_nzbw(width)); + let from_u = m.usize(from); + let out = if rhs.bw() <= from_u { + lhs.clone() + } else { + // since `from_u` is known the less significant part of `rhs` can be disregarded + let sub_rhs_w = rhs.bw() - from_u; + if let Some(w) = NonZeroUsize::new(sub_rhs_w) { + let tmp0 = Awi::zero(w); + let (tmp1, o) = static_field(&tmp0, 0, &rhs, from_u, sub_rhs_w); + let mut out = lhs.clone(); + if o { + out + } else { + out.field_width(&tmp1, width.to_usize()).unwrap(); + out + } + } else { + lhs.clone() + } + }; + m.graft(&[ + out.state(), + lhs.state(), + rhs.state(), + Awi::opaque(m.get_nzbw(from)).state(), + width.state(), + ]); + } else { + let lhs = Awi::opaque(lhs_w); + let rhs = Awi::opaque(rhs_w); + let from = Awi::opaque(m.get_nzbw(from)); + let width = Awi::opaque(width_w); + let mut tmp = InlAwi::from_usize(rhs_w.get()); + tmp.sub_(&width).unwrap(); + // the other two fail conditions are in `field_width` + let fail = from.ugt(&tmp).unwrap(); + let mut tmp_width = width.clone(); + tmp_width.mux_(&InlAwi::from_usize(0), fail).unwrap(); + // the optimizations on `width` are done later on an inner `field_width` call + let out = field_from(&lhs, &rhs, &from, &tmp_width); + m.graft(&[ + out.state(), + lhs.state(), + rhs.state(), + from.state(), + width.state(), + ]); + } + } + Shl([x, s]) => { + if m.is_literal(s) { + let x = Awi::opaque(m.get_nzbw(x)); + let s_u = m.usize(s); + let out = if (s_u == 0) || (x.bw() <= s_u) { + x.clone() + } else { + let tmp = Awi::zero(x.nzbw()); + static_field(&tmp, s_u, &x, 0, x.bw() - s_u).0 + }; + m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); + } else { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = shl(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + } + Lshr([x, s]) => { + if m.is_literal(s) { + let x = Awi::opaque(m.get_nzbw(x)); + let s_u = m.usize(s); + let out = if (s_u == 0) || (x.bw() <= s_u) { + x.clone() + } else { + let tmp = Awi::zero(x.nzbw()); + static_field(&tmp, 0, &x, s_u, x.bw() - s_u).0 + }; + m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); + } else { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = lshr(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + } + Ashr([x, s]) => { + if m.is_literal(s) { + let x = Awi::opaque(m.get_nzbw(x)); + let s_u = m.usize(s); + let out = if (s_u == 0) || (x.bw() <= s_u) { + x.clone() + } else { + let mut tmp = Awi::zero(x.nzbw()); + for i in 0..x.bw() { + tmp.set(i, x.msb()).unwrap(); + } + static_field(&tmp, 0, &x, s_u, x.bw() - s_u).0 + }; + m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); + } else { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = ashr(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + } + Rotl([x, s]) => { + if m.is_literal(s) { + let x = Awi::opaque(m.get_nzbw(x)); + let s_u = m.usize(s); + let out = if (s_u == 0) || (x.bw() <= s_u) { + x.clone() + } else { + let tmp = static_field(&Awi::zero(x.nzbw()), s_u, &x, 0, x.bw() - s_u).0; + static_field(&tmp, 0, &x, x.bw() - s_u, s_u).0 + }; + m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); + } else { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = rotl(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + } + Rotr([x, s]) => { + if m.is_literal(s) { + let x = Awi::opaque(m.get_nzbw(x)); + let s_u = m.usize(s); + let out = if (s_u == 0) || (x.bw() <= s_u) { + x.clone() + } else { + let tmp = static_field(&Awi::zero(x.nzbw()), 0, &x, s_u, x.bw() - s_u).0; + static_field(&tmp, x.bw() - s_u, &x, 0, s_u).0 + }; + m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); + } else { + let x = Awi::opaque(m.get_nzbw(x)); + let s = Awi::opaque(m.get_nzbw(s)); + let out = rotr(&x, &s); + m.graft(&[out.state(), x.state(), s.state()]); + } + } + Not([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = bitwise_not(&x); + m.graft(&[out.state(), x.state()]); + } + Or([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = bitwise(&lhs, &rhs, inlawi!(1110)); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + And([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = bitwise(&lhs, &rhs, inlawi!(1000)); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Xor([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = bitwise(&lhs, &rhs, inlawi!(0110)); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Inc([x, cin]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let cin = Awi::opaque(m.get_nzbw(cin)); + let out = incrementer(&x, &cin, false).0; + m.graft(&[out.state(), x.state(), cin.state()]); + } + IncCout([x, cin]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let cin = Awi::opaque(m.get_nzbw(cin)); + let out = incrementer(&x, &cin, false).1; + m.graft(&[out.state(), x.state(), cin.state()]); + } + Dec([x, cin]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let cin = Awi::opaque(m.get_nzbw(cin)); + let out = incrementer(&x, &cin, true).0; + m.graft(&[out.state(), x.state(), cin.state()]); + } + DecCout([x, cin]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let cin = Awi::opaque(m.get_nzbw(cin)); + let out = incrementer(&x, &cin, true).1; + m.graft(&[out.state(), x.state(), cin.state()]); + } + CinSum([cin, lhs, rhs]) => { + let cin = Awi::opaque(m.get_nzbw(cin)); + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = cin_sum(&cin, &lhs, &rhs).0; + m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); + } + UnsignedOverflow([cin, lhs, rhs]) => { + let cin = Awi::opaque(m.get_nzbw(cin)); + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = cin_sum(&cin, &lhs, &rhs).1; + m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); + } + SignedOverflow([cin, lhs, rhs]) => { + let cin = Awi::opaque(m.get_nzbw(cin)); + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = cin_sum(&cin, &lhs, &rhs).2; + m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); + } + Neg([x, neg]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let neg = Awi::opaque(m.get_nzbw(neg)); + assert_eq!(neg.bw(), 1); + let out = negator(&x, &neg); + m.graft(&[out.state(), x.state(), neg.state()]); + } + Abs([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let mut out = x.clone(); + out.neg_(x.msb()); + m.graft(&[out.state(), x.state()]); + } + Add([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = cin_sum(&inlawi!(0), &lhs, &rhs).0; + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Sub([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut rhs_tmp = rhs.clone(); + rhs_tmp.neg_(true); + let mut out = lhs.clone(); + out.add_(&rhs_tmp).unwrap(); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Rsb([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut out = lhs.clone(); + out.neg_(true); + out.add_(&rhs).unwrap(); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + FieldTo([lhs, to, rhs, width]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let width = Awi::opaque(m.get_nzbw(width)); + if m.is_literal(to) { + let to_u = m.usize(to); + + let out = if lhs.bw() < to_u { + lhs.clone() + } else if let Some(w) = NonZeroUsize::new(lhs.bw() - to_u) { + let (mut lhs_hi, o) = static_field(&Awi::zero(w), 0, &lhs, to_u, w.get()); + lhs_hi.field_width(&rhs, width.to_usize()).unwrap(); + if o { + lhs.clone() + } else { + static_field(&lhs, to_u, &lhs_hi, 0, w.get()).0 + } + } else { + lhs.clone() + }; + m.graft(&[ + out.state(), + lhs.state(), + Awi::opaque(m.get_nzbw(to)).state(), + rhs.state(), + width.state(), + ]); + } else { + let to = Awi::opaque(m.get_nzbw(to)); + let out = field_to(&lhs, &to, &rhs, &width); + m.graft(&[ + out.state(), + lhs.state(), + to.state(), + rhs.state(), + width.state(), + ]); + } + } + Field([lhs, to, rhs, from, width]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let width = Awi::opaque(m.get_nzbw(width)); + if m.is_literal(to) || m.is_literal(from) { + let to = Awi::opaque(m.get_nzbw(to)); + let from = Awi::opaque(m.get_nzbw(from)); + let min_w = min(lhs.bw(), rhs.bw()); + let mut tmp = Awi::zero(NonZeroUsize::new(min_w).unwrap()); + tmp.field_from(&rhs, from.to_usize(), width.to_usize()) + .unwrap(); + let mut out = lhs.clone(); + out.field_to(to.to_usize(), &tmp, width.to_usize()).unwrap(); + + m.graft(&[ + out.state(), + lhs.state(), + to.state(), + rhs.state(), + from.state(), + width.state(), + ]); + } else { + let to = Awi::opaque(m.get_nzbw(to)); + let from = Awi::opaque(m.get_nzbw(from)); + let out = field(&lhs, &to, &rhs, &from, &width); + m.graft(&[ + out.state(), + lhs.state(), + to.state(), + rhs.state(), + from.state(), + width.state(), + ]); + } + } + Rev([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let mut out = Awi::zero(x.nzbw()); + for i in 0..x.bw() { + out.set(i, x.get(x.bw() - 1 - i).unwrap()).unwrap() + } + m.graft(&[out.state(), x.state()]); + } + Eq([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = equal(&lhs, &rhs); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Ne([lhs, rhs]) => { + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut out = equal(&lhs, &rhs); + out.not_(); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Ult([lhs, rhs]) => { + let w = m.get_nzbw(lhs); + let lhs = Awi::opaque(w); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut not_lhs = lhs.clone(); + not_lhs.not_(); + let mut tmp = Awi::zero(w); + // TODO should probably use some short termination circuit like what + // `tsmear_inx` uses + let (out, _) = tmp.cin_sum_(false, ¬_lhs, &rhs).unwrap(); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Ule([lhs, rhs]) => { + let w = m.get_nzbw(lhs); + let lhs = Awi::opaque(w); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut not_lhs = lhs.clone(); + not_lhs.not_(); + let mut tmp = Awi::zero(w); + let (out, _) = tmp.cin_sum_(true, ¬_lhs, &rhs).unwrap(); + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Ilt([lhs, rhs]) => { + let w = m.get_nzbw(lhs); + let lhs = Awi::opaque(w); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut out = inlawi!(0); + if w.get() == 1 { + let mut tmp = inlawi!(00); + tmp.set(0, lhs.msb()).unwrap(); + tmp.set(1, rhs.msb()).unwrap(); + out.lut_(&inlawi!(0010), &tmp).unwrap(); + } else { + let lhs_lo = awi!(lhs[..(lhs.bw() - 1)]).unwrap(); + let rhs_lo = awi!(rhs[..(rhs.bw() - 1)]).unwrap(); + let lo_lt = lhs_lo.ult(&rhs_lo).unwrap(); + let mut tmp = inlawi!(000); + tmp.set(0, lo_lt).unwrap(); + tmp.set(1, lhs.msb()).unwrap(); + tmp.set(2, rhs.msb()).unwrap(); + // if `lhs.msb() != rhs.msb()` then `lhs.msb()` determines signed-less-than, + // otherwise `lo_lt` determines + out.lut_(&inlawi!(10001110), &tmp).unwrap(); + } + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + Ile([lhs, rhs]) => { + let w = m.get_nzbw(lhs); + let lhs = Awi::opaque(w); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let mut out = inlawi!(0); + if w.get() == 1 { + let mut tmp = inlawi!(00); + tmp.set(0, lhs.msb()).unwrap(); + tmp.set(1, rhs.msb()).unwrap(); + out.lut_(&inlawi!(1011), &tmp).unwrap(); + } else { + let lhs_lo = awi!(lhs[..(lhs.bw() - 1)]).unwrap(); + let rhs_lo = awi!(rhs[..(rhs.bw() - 1)]).unwrap(); + let lo_lt = lhs_lo.ule(&rhs_lo).unwrap(); + let mut tmp = inlawi!(000); + tmp.set(0, lo_lt).unwrap(); + tmp.set(1, lhs.msb()).unwrap(); + tmp.set(2, rhs.msb()).unwrap(); + out.lut_(&inlawi!(10001110), &tmp).unwrap(); + } + m.graft(&[out.state(), lhs.state(), rhs.state()]); + } + op @ (IsZero(_) | IsUmax(_) | IsImax(_) | IsImin(_) | IsUone(_)) => { + let x = Awi::opaque(m.get_nzbw(op.operands()[0])); + let w = x.bw(); + let out = InlAwi::from(match op { + IsZero(_) => x.const_eq(&awi!(zero: ..w).unwrap()).unwrap(), + IsUmax(_) => x.const_eq(&awi!(umax: ..w).unwrap()).unwrap(), + IsImax(_) => x.const_eq(&awi!(imax: ..w).unwrap()).unwrap(), + IsImin(_) => x.const_eq(&awi!(imin: ..w).unwrap()).unwrap(), + IsUone(_) => x.const_eq(&awi!(uone: ..w).unwrap()).unwrap(), + _ => unreachable!(), + }); + m.graft(&[out.state(), x.state()]); + } + CountOnes([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = count_ones(&x).to_usize(); + m.graft(&[out.state(), x.state()]); + } + Lz([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = leading_zeros(&x).to_usize(); + m.graft(&[out.state(), x.state()]); + } + Tz([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = trailing_zeros(&x).to_usize(); + m.graft(&[out.state(), x.state()]); + } + Sig([x]) => { + let x = Awi::opaque(m.get_nzbw(x)); + let out = significant_bits(&x).to_usize(); + m.graft(&[out.state(), x.state()]); + } + LutSet([table, entry, inx]) => { + let table = Awi::opaque(m.get_nzbw(table)); + let entry = Awi::opaque(m.get_nzbw(entry)); + let inx = Awi::opaque(m.get_nzbw(inx)); + let out = lut_set(&table, &entry, &inx); + m.graft(&[out.state(), table.state(), entry.state(), inx.state()]); + } + ZeroResizeOverflow([x], w) => { + let x = Awi::opaque(m.get_nzbw(x)); + let mut out = Awi::zero(bw(1)); + let w = w.get(); + if w < x.bw() { + out.bool_(!awi!(x[w..]).unwrap().is_zero()); + } + m.graft(&[out.state(), x.state()]); + } + SignResizeOverflow([x], w) => { + let x = Awi::opaque(m.get_nzbw(x)); + let mut out = Awi::zero(bw(1)); + let w = w.get(); + if w < x.bw() { + // the new msb and the bits above it should equal the old msb + let critical = awi!(x[(w - 1)..]).unwrap(); + let mut tmp = inlawi!(00); + tmp.set(0, critical.is_zero()).unwrap(); + tmp.set(1, critical.is_umax()).unwrap(); + out.lut_(&inlawi!(1001), &tmp).unwrap(); + } + m.graft(&[out.state(), x.state()]); + } + ArbMulAdd([add, lhs, rhs]) => { + let w = m.get_nzbw(add); + let add = Awi::opaque(w); + let lhs = Awi::opaque(m.get_nzbw(lhs)); + let rhs = Awi::opaque(m.get_nzbw(rhs)); + let out = mul_add(w, Some(&add), &lhs, &rhs); + m.graft(&[out.state(), add.state(), lhs.state(), rhs.state()]); + } + Mux([x0, x1, inx]) => { + let x0 = Awi::opaque(m.get_nzbw(x0)); + let x1 = Awi::opaque(m.get_nzbw(x1)); + let inx_tmp = Awi::opaque(m.get_nzbw(inx)); + let out = if m.is_literal(inx) { + let b = m.bool(inx); + if b { + x1.clone() + } else { + x0.clone() + } + } else { + mux_(&x0, &x1, &inx_tmp) + }; + m.graft(&[out.state(), x0.state(), x1.state(), inx_tmp.state()]); + } + // TODO in the divisions especially and in other operations, we need to look at the + // operand tree and combine multiple ops together in a single lowering operation + UQuo([duo, div]) => { + let duo = Awi::opaque(m.get_nzbw(duo)); + let div = Awi::opaque(m.get_nzbw(div)); + let quo = division(&duo, &div).0; + m.graft(&[quo.state(), duo.state(), div.state()]); + } + URem([duo, div]) => { + let duo = Awi::opaque(m.get_nzbw(duo)); + let div = Awi::opaque(m.get_nzbw(div)); + let rem = division(&duo, &div).1; + m.graft(&[rem.state(), duo.state(), div.state()]); + } + IQuo([duo, div]) => { + let duo = Awi::opaque(m.get_nzbw(duo)); + let div = Awi::opaque(m.get_nzbw(div)); + let duo_msb = duo.msb(); + let div_msb = div.msb(); + // keeping arguments opaque + let mut tmp_duo = duo.clone(); + let mut tmp_div = div.clone(); + tmp_duo.neg_(duo_msb); + tmp_div.neg_(div_msb); + let mut quo = division(&tmp_duo, &tmp_div).0; + let mut tmp0 = InlAwi::from(duo_msb); + let tmp1 = InlAwi::from(div_msb); + tmp0.xor_(&tmp1).unwrap(); + quo.neg_(tmp0.to_bool()); + m.graft(&[quo.state(), duo.state(), div.state()]); + } + IRem([duo, div]) => { + let duo = Awi::opaque(m.get_nzbw(duo)); + let div = Awi::opaque(m.get_nzbw(div)); + let duo_msb = duo.msb(); + let div_msb = div.msb(); + // keeping arguments opaque + let mut tmp_duo = duo.clone(); + let mut tmp_div = div.clone(); + tmp_duo.neg_(duo_msb); + tmp_div.neg_(div_msb); + let mut rem = division(&tmp_duo, &tmp_div).1; + rem.neg_(duo_msb); + m.graft(&[rem.state(), duo.state(), div.state()]); + } + } + Ok(false) +} diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 55f5ca85..9b4f36a0 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -1,708 +1,364 @@ -//! Lowers everything into LUT form +use std::num::NonZeroUsize; -// TODO https://github.com/rust-lang/rust-clippy/issues/10577 -#![allow(clippy::redundant_clone)] +use awint::awint_dag::{smallvec::smallvec, EvalError, Op::*, PState}; -use std::{cmp::min, num::NonZeroUsize}; - -use awint::{ - awint_dag::{ - triple_arena::Ptr, - DummyDefault, EvalError, Lineage, - Op::{self, *}, - PState, - }, - bw, - dag::{awi, inlawi, Awi, Bits, InlAwi}, +use crate::{ + ensemble::Ensemble, + epoch::EpochShared, + lower::{lower_op, LowerManagement}, }; -use super::meta::*; - -pub trait LowerManagement { - fn graft(&mut self, output_and_operands: &[PState]); - fn get_nzbw(&self, p: P) -> NonZeroUsize; - fn is_literal(&self, p: P) -> bool; - fn usize(&self, p: P) -> usize; - fn bool(&self, p: P) -> bool; - fn dec_rc(&mut self, p: P); -} - -/// Returns if the lowering is done -pub fn lower_state( - start_op: Op

, - out_w: NonZeroUsize, - mut m: impl LowerManagement

, -) -> Result { - match start_op { - Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | Concat(_) | ConcatFields(_) - | StaticLut(..) | StaticGet(..) | StaticSet(..) => return Ok(true), - Lut([lut, inx]) => { - if m.is_literal(lut) { - return Err(EvalError::OtherStr( - "this needs to be handled before this function", - )); - } else { - let mut out = Awi::zero(out_w); - let lut = Awi::opaque(m.get_nzbw(lut)); - let inx = Awi::opaque(m.get_nzbw(inx)); - dynamic_to_static_lut(&mut out, &lut, &inx); - m.graft(&[out.state(), lut.state(), inx.state()]); +impl Ensemble { + /// Used for forbidden meta psuedo-DSL techniques in which a single state is + /// replaced by more basic states. + pub fn graft(&mut self, p_state: PState, operands: &[PState]) -> Result<(), EvalError> { + #[cfg(debug_assertions)] + { + if (self.stator.states[p_state].op.operands_len() + 1) != operands.len() { + return Err(EvalError::WrongNumberOfOperands) } - } - Get([bits, inx]) => { - if m.is_literal(inx) { - return Err(EvalError::OtherStr( - "this needs to be handled before this function", - )); - } else { - let bits = Awi::opaque(m.get_nzbw(bits)); - let inx = Awi::opaque(m.get_nzbw(inx)); - let out = dynamic_to_static_get(&bits, &inx); - m.graft(&[out.state(), bits.state(), inx.state()]); + for (i, op) in self.stator.states[p_state].op.operands().iter().enumerate() { + let current_nzbw = self.stator.states[operands[i + 1]].nzbw; + let current_is_opaque = self.stator.states[operands[i + 1]].op.is_opaque(); + if self.stator.states[op].nzbw != current_nzbw { + return Err(EvalError::OtherString(format!( + "operand {}: a bitwidth of {:?} is trying to be grafted to a bitwidth of \ + {:?}", + i, current_nzbw, self.stator.states[op].nzbw + ))) + } + if !current_is_opaque { + return Err(EvalError::ExpectedOpaque) + } } - } - Set([bits, inx, bit]) => { - if m.is_literal(inx) { - return Err(EvalError::OtherStr( - "this needs to be handled before this function", - )); - } else { - let bits = Awi::opaque(m.get_nzbw(bits)); - let inx = Awi::opaque(m.get_nzbw(inx)); - let bit = Awi::opaque(m.get_nzbw(bit)); - let out = dynamic_to_static_set(&bits, &inx, &bit); - m.graft(&[out.state(), bits.state(), inx.state(), bit.state()]); + if self.stator.states[p_state].nzbw != self.stator.states[operands[0]].nzbw { + return Err(EvalError::WrongBitwidth) } } - FieldBit([lhs, to, rhs, from]) => { - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let from = Awi::opaque(m.get_nzbw(from)); - let bit = rhs.get(from.to_usize()).unwrap(); - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let to = Awi::opaque(m.get_nzbw(to)); - // keep `lhs` the same, `out` has the set bit - let mut out = lhs.clone(); - out.set(to.to_usize(), bit).unwrap(); - m.graft(&[ - out.state(), - lhs.state(), - to.state(), - rhs.state(), - from.state(), - ]); - } - ZeroResize([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = resize(&x, out_w, false); - m.graft(&[out.state(), x.state()]); - } - SignResize([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = resize(&x, out_w, true); - m.graft(&[out.state(), x.state()]); - } - Resize([x, b]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let b = Awi::opaque(m.get_nzbw(b)); - let out = resize_cond(&x, out_w, &b); - m.graft(&[out.state(), x.state(), b.state()]); - } - Lsb([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = x.get(0).unwrap(); - m.graft(&[out.state(), x.state()]); - } - Msb([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = x.get(x.bw() - 1).unwrap(); - m.graft(&[out.state(), x.state()]); - } - FieldWidth([lhs, rhs, width]) => { - let lhs_w = m.get_nzbw(lhs); - let rhs_w = m.get_nzbw(rhs); - let width_w = m.get_nzbw(width); - if m.is_literal(width) { - let width_u = m.usize(width); - let lhs = Awi::opaque(lhs_w); - let rhs = Awi::opaque(rhs_w); - // If `width_u` is out of bounds `out` is created as a no-op of `lhs` as - // expected - let out = static_field(&lhs, 0, &rhs, 0, width_u).0; - m.graft(&[ - out.state(), - lhs.state(), - rhs.state(), - Awi::opaque(width_w).state(), - ]); + + // TODO what do we do when we make multi-output things + // graft input + for i in 1..operands.len() { + let grafted = operands[i]; + let graftee = self.stator.states.get(p_state).unwrap().op.operands()[i - 1]; + if let Some(grafted) = self.stator.states.get_mut(grafted) { + // change the grafted `Opaque` into a `Copy` that routes to the graftee instead + // of needing to change all the operands of potentially many nodes + grafted.op = Copy([graftee]); } else { - let lhs = Awi::opaque(lhs_w); - let rhs = Awi::opaque(rhs_w); - let width = Awi::opaque(width_w); - let fail = width.ugt(&InlAwi::from_usize(lhs_w.get())).unwrap() - | width.ugt(&InlAwi::from_usize(rhs_w.get())).unwrap(); - let mut tmp_width = width.clone(); - tmp_width.mux_(&InlAwi::from_usize(0), fail).unwrap(); - let out = field_width(&lhs, &rhs, &tmp_width); - m.graft(&[out.state(), lhs.state(), rhs.state(), width.state()]); + // else the operand is not used because it was optimized away, this is removing + // a tree outside of the grafted part + self.dec_rc(graftee).unwrap(); } } - Funnel([x, s]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = funnel_(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); - } - FieldFrom([lhs, rhs, from, width]) => { - let lhs_w = m.get_nzbw(lhs); - let rhs_w = m.get_nzbw(rhs); - let width_w = m.get_nzbw(width); - if m.is_literal(from) { - let lhs = Awi::opaque(lhs_w); - let rhs = Awi::opaque(rhs_w); - let width = Awi::opaque(m.get_nzbw(width)); - let from_u = m.usize(from); - let out = if rhs.bw() <= from_u { - lhs.clone() - } else { - // since `from_u` is known the less significant part of `rhs` can be disregarded - let sub_rhs_w = rhs.bw() - from_u; - if let Some(w) = NonZeroUsize::new(sub_rhs_w) { - let tmp0 = Awi::zero(w); - let (tmp1, o) = static_field(&tmp0, 0, &rhs, from_u, sub_rhs_w); - let mut out = lhs.clone(); - if o { - out - } else { - out.field_width(&tmp1, width.to_usize()).unwrap(); - out - } - } else { - lhs.clone() - } - }; - m.graft(&[ - out.state(), - lhs.state(), - rhs.state(), - Awi::opaque(m.get_nzbw(from)).state(), - width.state(), - ]); - } else { - let lhs = Awi::opaque(lhs_w); - let rhs = Awi::opaque(rhs_w); - let from = Awi::opaque(m.get_nzbw(from)); - let width = Awi::opaque(width_w); - let mut tmp = InlAwi::from_usize(rhs_w.get()); - tmp.sub_(&width).unwrap(); - // the other two fail conditions are in `field_width` - let fail = from.ugt(&tmp).unwrap(); - let mut tmp_width = width.clone(); - tmp_width.mux_(&InlAwi::from_usize(0), fail).unwrap(); - // the optimizations on `width` are done later on an inner `field_width` call - let out = field_from(&lhs, &rhs, &from, &tmp_width); - m.graft(&[ - out.state(), - lhs.state(), - rhs.state(), - from.state(), - width.state(), - ]); + + // graft output + let grafted = operands[0]; + self.stator.states.get_mut(p_state).unwrap().op = Copy([grafted]); + self.stator.states[grafted].rc = self.stator.states[grafted].rc.checked_add(1).unwrap(); + + Ok(()) + } + + pub fn lower_op(epoch_shared: &EpochShared, p_state: PState) -> Result { + // TODO optimization to remove unused nodes early + //let epoch = StateEpoch::new(); + struct Tmp<'a> { + ptr: PState, + epoch_shared: &'a EpochShared, + } + impl<'a> LowerManagement for Tmp<'a> { + fn graft(&mut self, operands: &[PState]) { + self.epoch_shared + .epoch_data + .borrow_mut() + .ensemble + .graft(self.ptr, operands) + .unwrap(); } - } - Shl([x, s]) => { - if m.is_literal(s) { - let x = Awi::opaque(m.get_nzbw(x)); - let s_u = m.usize(s); - let out = if (s_u == 0) || (x.bw() <= s_u) { - x.clone() - } else { - let tmp = Awi::zero(x.nzbw()); - static_field(&tmp, s_u, &x, 0, x.bw() - s_u).0 - }; - m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); - } else { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = shl(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); + + fn get_nzbw(&self, p: PState) -> NonZeroUsize { + self.epoch_shared + .epoch_data + .borrow() + .ensemble + .stator + .states + .get(p) + .unwrap() + .nzbw } - } - Lshr([x, s]) => { - if m.is_literal(s) { - let x = Awi::opaque(m.get_nzbw(x)); - let s_u = m.usize(s); - let out = if (s_u == 0) || (x.bw() <= s_u) { - x.clone() - } else { - let tmp = Awi::zero(x.nzbw()); - static_field(&tmp, 0, &x, s_u, x.bw() - s_u).0 - }; - m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); - } else { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = lshr(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); + + fn is_literal(&self, p: PState) -> bool { + self.epoch_shared + .epoch_data + .borrow() + .ensemble + .stator + .states + .get(p) + .unwrap() + .op + .is_literal() } - } - Ashr([x, s]) => { - if m.is_literal(s) { - let x = Awi::opaque(m.get_nzbw(x)); - let s_u = m.usize(s); - let out = if (s_u == 0) || (x.bw() <= s_u) { - x.clone() - } else { - let mut tmp = Awi::zero(x.nzbw()); - for i in 0..x.bw() { - tmp.set(i, x.msb()).unwrap(); + + fn usize(&self, p: PState) -> usize { + if let Literal(ref lit) = self + .epoch_shared + .epoch_data + .borrow() + .ensemble + .stator + .states + .get(p) + .unwrap() + .op + { + if lit.bw() != 64 { + panic!() } - static_field(&tmp, 0, &x, s_u, x.bw() - s_u).0 - }; - m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); - } else { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = ashr(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); - } - } - Rotl([x, s]) => { - if m.is_literal(s) { - let x = Awi::opaque(m.get_nzbw(x)); - let s_u = m.usize(s); - let out = if (s_u == 0) || (x.bw() <= s_u) { - x.clone() + lit.to_usize() } else { - let tmp = static_field(&Awi::zero(x.nzbw()), s_u, &x, 0, x.bw() - s_u).0; - static_field(&tmp, 0, &x, x.bw() - s_u, s_u).0 - }; - m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); - } else { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = rotl(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); - } - } - Rotr([x, s]) => { - if m.is_literal(s) { - let x = Awi::opaque(m.get_nzbw(x)); - let s_u = m.usize(s); - let out = if (s_u == 0) || (x.bw() <= s_u) { - x.clone() - } else { - let tmp = static_field(&Awi::zero(x.nzbw()), 0, &x, s_u, x.bw() - s_u).0; - static_field(&tmp, x.bw() - s_u, &x, 0, s_u).0 - }; - m.graft(&[out.state(), x.state(), Awi::opaque(m.get_nzbw(s)).state()]); - } else { - let x = Awi::opaque(m.get_nzbw(x)); - let s = Awi::opaque(m.get_nzbw(s)); - let out = rotr(&x, &s); - m.graft(&[out.state(), x.state(), s.state()]); + panic!() + } } - } - Not([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = bitwise_not(&x); - m.graft(&[out.state(), x.state()]); - } - Or([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(1110)); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - And([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(1000)); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Xor([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(0110)); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Inc([x, cin]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let cin = Awi::opaque(m.get_nzbw(cin)); - let out = incrementer(&x, &cin, false).0; - m.graft(&[out.state(), x.state(), cin.state()]); - } - IncCout([x, cin]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let cin = Awi::opaque(m.get_nzbw(cin)); - let out = incrementer(&x, &cin, false).1; - m.graft(&[out.state(), x.state(), cin.state()]); - } - Dec([x, cin]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let cin = Awi::opaque(m.get_nzbw(cin)); - let out = incrementer(&x, &cin, true).0; - m.graft(&[out.state(), x.state(), cin.state()]); - } - DecCout([x, cin]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let cin = Awi::opaque(m.get_nzbw(cin)); - let out = incrementer(&x, &cin, true).1; - m.graft(&[out.state(), x.state(), cin.state()]); - } - CinSum([cin, lhs, rhs]) => { - let cin = Awi::opaque(m.get_nzbw(cin)); - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = cin_sum(&cin, &lhs, &rhs).0; - m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); - } - UnsignedOverflow([cin, lhs, rhs]) => { - let cin = Awi::opaque(m.get_nzbw(cin)); - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = cin_sum(&cin, &lhs, &rhs).1; - m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); - } - SignedOverflow([cin, lhs, rhs]) => { - let cin = Awi::opaque(m.get_nzbw(cin)); - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = cin_sum(&cin, &lhs, &rhs).2; - m.graft(&[out.state(), cin.state(), lhs.state(), rhs.state()]); - } - Neg([x, neg]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let neg = Awi::opaque(m.get_nzbw(neg)); - assert_eq!(neg.bw(), 1); - let out = negator(&x, &neg); - m.graft(&[out.state(), x.state(), neg.state()]); - } - Abs([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let mut out = x.clone(); - out.neg_(x.msb()); - m.graft(&[out.state(), x.state()]); - } - Add([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = cin_sum(&inlawi!(0), &lhs, &rhs).0; - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Sub([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut rhs_tmp = rhs.clone(); - rhs_tmp.neg_(true); - let mut out = lhs.clone(); - out.add_(&rhs_tmp).unwrap(); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Rsb([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut out = lhs.clone(); - out.neg_(true); - out.add_(&rhs).unwrap(); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - FieldTo([lhs, to, rhs, width]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let width = Awi::opaque(m.get_nzbw(width)); - if m.is_literal(to) { - let to_u = m.usize(to); - let out = if lhs.bw() < to_u { - lhs.clone() - } else if let Some(w) = NonZeroUsize::new(lhs.bw() - to_u) { - let (mut lhs_hi, o) = static_field(&Awi::zero(w), 0, &lhs, to_u, w.get()); - lhs_hi.field_width(&rhs, width.to_usize()).unwrap(); - if o { - lhs.clone() - } else { - static_field(&lhs, to_u, &lhs_hi, 0, w.get()).0 + fn bool(&self, p: PState) -> bool { + if let Literal(ref lit) = self + .epoch_shared + .epoch_data + .borrow() + .ensemble + .stator + .states + .get(p) + .unwrap() + .op + { + if lit.bw() != 1 { + panic!() } + lit.to_bool() } else { - lhs.clone() - }; - m.graft(&[ - out.state(), - lhs.state(), - Awi::opaque(m.get_nzbw(to)).state(), - rhs.state(), - width.state(), - ]); - } else { - let to = Awi::opaque(m.get_nzbw(to)); - let out = field_to(&lhs, &to, &rhs, &width); - m.graft(&[ - out.state(), - lhs.state(), - to.state(), - rhs.state(), - width.state(), - ]); + panic!() + } } - } - Field([lhs, to, rhs, from, width]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let width = Awi::opaque(m.get_nzbw(width)); - if m.is_literal(to) || m.is_literal(from) { - let to = Awi::opaque(m.get_nzbw(to)); - let from = Awi::opaque(m.get_nzbw(from)); - let min_w = min(lhs.bw(), rhs.bw()); - let mut tmp = Awi::zero(NonZeroUsize::new(min_w).unwrap()); - tmp.field_from(&rhs, from.to_usize(), width.to_usize()) - .unwrap(); - let mut out = lhs.clone(); - out.field_to(to.to_usize(), &tmp, width.to_usize()).unwrap(); - m.graft(&[ - out.state(), - lhs.state(), - to.state(), - rhs.state(), - from.state(), - width.state(), - ]); - } else { - let to = Awi::opaque(m.get_nzbw(to)); - let from = Awi::opaque(m.get_nzbw(from)); - let out = field(&lhs, &to, &rhs, &from, &width); - m.graft(&[ - out.state(), - lhs.state(), - to.state(), - rhs.state(), - from.state(), - width.state(), - ]); - } - } - Rev([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let mut out = Awi::zero(x.nzbw()); - for i in 0..x.bw() { - out.set(i, x.get(x.bw() - 1 - i).unwrap()).unwrap() - } - m.graft(&[out.state(), x.state()]); - } - Eq([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = equal(&lhs, &rhs); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Ne([lhs, rhs]) => { - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut out = equal(&lhs, &rhs); - out.not_(); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Ult([lhs, rhs]) => { - let w = m.get_nzbw(lhs); - let lhs = Awi::opaque(w); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut not_lhs = lhs.clone(); - not_lhs.not_(); - let mut tmp = Awi::zero(w); - // TODO should probably use some short termination circuit like what - // `tsmear_inx` uses - let (out, _) = tmp.cin_sum_(false, ¬_lhs, &rhs).unwrap(); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Ule([lhs, rhs]) => { - let w = m.get_nzbw(lhs); - let lhs = Awi::opaque(w); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut not_lhs = lhs.clone(); - not_lhs.not_(); - let mut tmp = Awi::zero(w); - let (out, _) = tmp.cin_sum_(true, ¬_lhs, &rhs).unwrap(); - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Ilt([lhs, rhs]) => { - let w = m.get_nzbw(lhs); - let lhs = Awi::opaque(w); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut out = inlawi!(0); - if w.get() == 1 { - let mut tmp = inlawi!(00); - tmp.set(0, lhs.msb()).unwrap(); - tmp.set(1, rhs.msb()).unwrap(); - out.lut_(&inlawi!(0010), &tmp).unwrap(); - } else { - let lhs_lo = awi!(lhs[..(lhs.bw() - 1)]).unwrap(); - let rhs_lo = awi!(rhs[..(rhs.bw() - 1)]).unwrap(); - let lo_lt = lhs_lo.ult(&rhs_lo).unwrap(); - let mut tmp = inlawi!(000); - tmp.set(0, lo_lt).unwrap(); - tmp.set(1, lhs.msb()).unwrap(); - tmp.set(2, rhs.msb()).unwrap(); - // if `lhs.msb() != rhs.msb()` then `lhs.msb()` determines signed-less-than, - // otherwise `lo_lt` determines - out.lut_(&inlawi!(10001110), &tmp).unwrap(); + fn dec_rc(&mut self, p: PState) { + self.epoch_shared + .epoch_data + .borrow_mut() + .ensemble + .dec_rc(p) + .unwrap() } - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - Ile([lhs, rhs]) => { - let w = m.get_nzbw(lhs); - let lhs = Awi::opaque(w); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let mut out = inlawi!(0); - if w.get() == 1 { - let mut tmp = inlawi!(00); - tmp.set(0, lhs.msb()).unwrap(); - tmp.set(1, rhs.msb()).unwrap(); - out.lut_(&inlawi!(1011), &tmp).unwrap(); - } else { - let lhs_lo = awi!(lhs[..(lhs.bw() - 1)]).unwrap(); - let rhs_lo = awi!(rhs[..(rhs.bw() - 1)]).unwrap(); - let lo_lt = lhs_lo.ule(&rhs_lo).unwrap(); - let mut tmp = inlawi!(000); - tmp.set(0, lo_lt).unwrap(); - tmp.set(1, lhs.msb()).unwrap(); - tmp.set(2, rhs.msb()).unwrap(); - out.lut_(&inlawi!(10001110), &tmp).unwrap(); - } - m.graft(&[out.state(), lhs.state(), rhs.state()]); - } - op @ (IsZero(_) | IsUmax(_) | IsImax(_) | IsImin(_) | IsUone(_)) => { - let x = Awi::opaque(m.get_nzbw(op.operands()[0])); - let w = x.bw(); - let out = InlAwi::from(match op { - IsZero(_) => x.const_eq(&awi!(zero: ..w).unwrap()).unwrap(), - IsUmax(_) => x.const_eq(&awi!(umax: ..w).unwrap()).unwrap(), - IsImax(_) => x.const_eq(&awi!(imax: ..w).unwrap()).unwrap(), - IsImin(_) => x.const_eq(&awi!(imin: ..w).unwrap()).unwrap(), - IsUone(_) => x.const_eq(&awi!(uone: ..w).unwrap()).unwrap(), - _ => unreachable!(), - }); - m.graft(&[out.state(), x.state()]); - } - CountOnes([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = count_ones(&x).to_usize(); - m.graft(&[out.state(), x.state()]); - } - Lz([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = leading_zeros(&x).to_usize(); - m.graft(&[out.state(), x.state()]); } - Tz([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = trailing_zeros(&x).to_usize(); - m.graft(&[out.state(), x.state()]); - } - Sig([x]) => { - let x = Awi::opaque(m.get_nzbw(x)); - let out = significant_bits(&x).to_usize(); - m.graft(&[out.state(), x.state()]); - } - LutSet([table, entry, inx]) => { - let table = Awi::opaque(m.get_nzbw(table)); - let entry = Awi::opaque(m.get_nzbw(entry)); - let inx = Awi::opaque(m.get_nzbw(inx)); - let out = lut_set(&table, &entry, &inx); - m.graft(&[out.state(), table.state(), entry.state(), inx.state()]); - } - ZeroResizeOverflow([x], w) => { - let x = Awi::opaque(m.get_nzbw(x)); - let mut out = Awi::zero(bw(1)); - let w = w.get(); - if w < x.bw() { - out.bool_(!awi!(x[w..]).unwrap().is_zero()); - } - m.graft(&[out.state(), x.state()]); - } - SignResizeOverflow([x], w) => { - let x = Awi::opaque(m.get_nzbw(x)); - let mut out = Awi::zero(bw(1)); - let w = w.get(); - if w < x.bw() { - // the new msb and the bits above it should equal the old msb - let critical = awi!(x[(w - 1)..]).unwrap(); - let mut tmp = inlawi!(00); - tmp.set(0, critical.is_zero()).unwrap(); - tmp.set(1, critical.is_umax()).unwrap(); - out.lut_(&inlawi!(1001), &tmp).unwrap(); + let lock = epoch_shared.epoch_data.borrow(); + let state = lock.ensemble.stator.states.get(p_state).unwrap(); + let start_op = state.op.clone(); + let out_w = state.nzbw; + drop(lock); + lower_op(start_op, out_w, Tmp { + ptr: p_state, + epoch_shared, + }) + } + + /// Lowers the rootward tree from `p_state` down to the elementary `Op`s + pub fn dfs_lower_states_to_elementary( + epoch_shared: &EpochShared, + p_state: PState, + ) -> Result<(), EvalError> { + let mut unimplemented = false; + let mut lock = epoch_shared.epoch_data.borrow_mut(); + if let Some(state) = lock.ensemble.stator.states.get(p_state) { + if state.lowered_to_elementary { + return Ok(()) } - m.graft(&[out.state(), x.state()]); - } - ArbMulAdd([add, lhs, rhs]) => { - let w = m.get_nzbw(add); - let add = Awi::opaque(w); - let lhs = Awi::opaque(m.get_nzbw(lhs)); - let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = mul_add(w, Some(&add), &lhs, &rhs); - m.graft(&[out.state(), add.state(), lhs.state(), rhs.state()]); + } else { + return Err(EvalError::InvalidPtr) } - Mux([x0, x1, inx]) => { - let x0 = Awi::opaque(m.get_nzbw(x0)); - let x1 = Awi::opaque(m.get_nzbw(x1)); - let inx_tmp = Awi::opaque(m.get_nzbw(inx)); - let out = if m.is_literal(inx) { - let b = m.bool(inx); - if b { - x1.clone() + lock.ensemble.stator.states[p_state].lowered_to_elementary = true; + + // NOTE be sure to reset this before returning from the function + lock.keep_flag = false; + drop(lock); + let mut path: Vec<(usize, PState)> = vec![(0, p_state)]; + loop { + let (i, p_state) = path[path.len() - 1]; + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let state = &lock.ensemble.stator.states[p_state]; + let ops = state.op.operands(); + if ops.is_empty() { + // reached a root + path.pop().unwrap(); + if path.is_empty() { + break + } + path.last_mut().unwrap().0 += 1; + } else if i >= ops.len() { + // checked all sources, attempt evaluation first, this is crucial in preventing + // wasted work in multiple layer lowerings + match lock.ensemble.eval_state(p_state) { + Ok(()) => { + path.pop().unwrap(); + if path.is_empty() { + break + } else { + continue + } + } + // Continue on to lowering + Err(EvalError::Unevaluatable) => (), + Err(e) => { + lock.ensemble.stator.states[p_state].err = Some(e.clone()); + return Err(e) + } + } + let needs_lower = match lock.ensemble.stator.states[p_state].op { + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) + | StaticSet(..) | StaticLut(..) => false, + Lut([lut, inx]) => { + if let Literal(ref lit) = lock.ensemble.stator.states[lut].op { + let lit = lit.clone(); + let out_w = lock.ensemble.stator.states[p_state].nzbw.get(); + let inx_w = lock.ensemble.stator.states[inx].nzbw.get(); + let no_op = if let Ok(inx_w) = u32::try_from(inx_w) { + if let Some(num_entries) = 1usize.checked_shl(inx_w) { + (out_w * num_entries) != lit.bw() + } else { + true + } + } else { + true + }; + if no_op { + // TODO should I add the extra arg to `Lut` to fix this edge case? + lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); + lock.ensemble.dec_rc(inx).unwrap(); + } else { + lock.ensemble.stator.states[p_state].op = StaticLut([inx], lit); + } + lock.ensemble.dec_rc(lut).unwrap(); + false + } else { + true + } + } + Get([bits, inx]) => { + if let Literal(ref lit) = lock.ensemble.stator.states[inx].op { + let lit = lit.clone(); + let lit_u = lit.to_usize(); + if lit_u >= lock.ensemble.stator.states[bits].nzbw.get() { + // TODO I realize now that no-op `get` specifically is fundamentally + // ill-defined to some extend because it returns `Option`, it + // must be asserted against, this + // provides the next best thing + lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); + lock.ensemble.dec_rc(bits).unwrap(); + } else { + lock.ensemble.stator.states[p_state].op = StaticGet([bits], lit_u); + } + lock.ensemble.dec_rc(inx).unwrap(); + false + } else { + true + } + } + Set([bits, inx, bit]) => { + if let Literal(ref lit) = lock.ensemble.stator.states[inx].op { + let lit = lit.clone(); + let lit_u = lit.to_usize(); + if lit_u >= lock.ensemble.stator.states[bits].nzbw.get() { + // no-op + lock.ensemble.stator.states[p_state].op = Copy([bits]); + lock.ensemble.dec_rc(bit).unwrap(); + } else { + lock.ensemble.stator.states[p_state].op = + StaticSet([bits, bit], lit.to_usize()); + } + lock.ensemble.dec_rc(inx).unwrap(); + false + } else { + true + } + } + _ => true, + }; + drop(lock); + let lowering_done = if needs_lower { + // this is used to be able to remove ultimately unused temporaries + let mut temporary = EpochShared::shared_with(epoch_shared); + temporary.set_as_current(); + let lowering_done = match Ensemble::lower_op(&temporary, p_state) { + Ok(lowering_done) => lowering_done, + Err(EvalError::Unimplemented) => { + // finish lowering as much as possible + unimplemented = true; + true + } + Err(e) => { + temporary.remove_as_current(); + let mut lock = epoch_shared.epoch_data.borrow_mut(); + lock.ensemble.stator.states[p_state].err = Some(e.clone()); + lock.keep_flag = true; + return Err(e) + } + }; + // shouldn't be adding additional assertions + // TODO after migrating the old lowering tests to a starlight-like system, make + // sure there are none using assertions assert!(temporary. + // assertions_empty()); + let states = temporary.take_states_added(); + temporary.remove_as_current(); + let mut lock = epoch_shared.epoch_data.borrow_mut(); + for p_state in states { + let state = &lock.ensemble.stator.states[p_state]; + if (!state.keep) && (state.rc == 0) { + lock.ensemble.remove_state(p_state).unwrap(); + } + } + lowering_done } else { - x0.clone() + true + }; + if lowering_done { + path.pop().unwrap(); + if path.is_empty() { + break + } + } else { + // else do not call `path.pop`, restart the DFS here + path.last_mut().unwrap().0 = 0; } } else { - mux_(&x0, &x1, &inx_tmp) - }; - m.graft(&[out.state(), x0.state(), x1.state(), inx_tmp.state()]); - } - // TODO in the divisions especially and in other operations, we need to look at the - // operand tree and combine multiple ops together in a single lowering operation - UQuo([duo, div]) => { - let duo = Awi::opaque(m.get_nzbw(duo)); - let div = Awi::opaque(m.get_nzbw(div)); - let quo = division(&duo, &div).0; - m.graft(&[quo.state(), duo.state(), div.state()]); - } - URem([duo, div]) => { - let duo = Awi::opaque(m.get_nzbw(duo)); - let div = Awi::opaque(m.get_nzbw(div)); - let rem = division(&duo, &div).1; - m.graft(&[rem.state(), duo.state(), div.state()]); - } - IQuo([duo, div]) => { - let duo = Awi::opaque(m.get_nzbw(duo)); - let div = Awi::opaque(m.get_nzbw(div)); - let duo_msb = duo.msb(); - let div_msb = div.msb(); - // keeping arguments opaque - let mut tmp_duo = duo.clone(); - let mut tmp_div = div.clone(); - tmp_duo.neg_(duo_msb); - tmp_div.neg_(div_msb); - let mut quo = division(&tmp_duo, &tmp_div).0; - let mut tmp0 = InlAwi::from(duo_msb); - let tmp1 = InlAwi::from(div_msb); - tmp0.xor_(&tmp1).unwrap(); - quo.neg_(tmp0.to_bool()); - m.graft(&[quo.state(), duo.state(), div.state()]); + let mut p_next = ops[i]; + if lock.ensemble.stator.states[p_next].lowered_to_elementary { + // do not visit + path.last_mut().unwrap().0 += 1; + } else { + while let Copy([a]) = lock.ensemble.stator.states[p_next].op { + // special optimization case: forward Copies + lock.ensemble.stator.states[p_state].op.operands_mut()[i] = a; + let rc = &mut lock.ensemble.stator.states[a].rc; + *rc = (*rc).checked_add(1).unwrap(); + lock.ensemble.dec_rc(p_next).unwrap(); + p_next = a; + } + lock.ensemble.stator.states[p_next].lowered_to_elementary = true; + path.push((0, p_next)); + } + drop(lock); + } } - IRem([duo, div]) => { - let duo = Awi::opaque(m.get_nzbw(duo)); - let div = Awi::opaque(m.get_nzbw(div)); - let duo_msb = duo.msb(); - let div_msb = div.msb(); - // keeping arguments opaque - let mut tmp_duo = duo.clone(); - let mut tmp_div = div.clone(); - tmp_duo.neg_(duo_msb); - tmp_div.neg_(div_msb); - let mut rem = division(&tmp_duo, &tmp_div).1; - rem.neg_(duo_msb); - m.graft(&[rem.state(), duo.state(), div.state()]); + + let mut lock = epoch_shared.epoch_data.borrow_mut(); + lock.keep_flag = true; + + if unimplemented { + Err(EvalError::Unimplemented) + } else { + Ok(()) } } - Ok(false) } From 0da4f2cbdbe4f48d35e8e4b59643f9ceb8420c89 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Tue, 5 Dec 2023 18:31:10 -0600 Subject: [PATCH 03/62] begin switchover to `Concat` and `ConcatField` --- starlight/src/lower/lower_state.rs | 40 ++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 9b4f36a0..2629ff49 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -1,6 +1,9 @@ use std::num::NonZeroUsize; -use awint::awint_dag::{smallvec::smallvec, EvalError, Op::*, PState}; +use awint::{ + awint_dag::{smallvec::smallvec, ConcatFieldsType, EvalError, Op::*, PState}, + bw, +}; use crate::{ ensemble::Ensemble, @@ -257,7 +260,9 @@ impl Ensemble { lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); lock.ensemble.dec_rc(bits).unwrap(); } else { - lock.ensemble.stator.states[p_state].op = StaticGet([bits], lit_u); + lock.ensemble.stator.states[p_state].op = ConcatFields( + ConcatFieldsType::from_iter(1, [(bits, lit_u, bw(1))]), + ); } lock.ensemble.dec_rc(inx).unwrap(); false @@ -269,13 +274,38 @@ impl Ensemble { if let Literal(ref lit) = lock.ensemble.stator.states[inx].op { let lit = lit.clone(); let lit_u = lit.to_usize(); - if lit_u >= lock.ensemble.stator.states[bits].nzbw.get() { + let bits_w = lock.ensemble.stator.states[bits].nzbw.get(); + if lit_u >= bits_w { // no-op lock.ensemble.stator.states[p_state].op = Copy([bits]); lock.ensemble.dec_rc(bit).unwrap(); - } else { + } else if let Some(lo_rem) = NonZeroUsize::new(lit_u) { + if let Some(hi_rem) = NonZeroUsize::new(bits_w - 1 - lit_u) { + lock.ensemble.stator.states[p_state].op = + ConcatFields(ConcatFieldsType::from_iter(3, [ + (bits, 0, lo_rem), + (bit, 0, bw(1)), + (bits, lit_u + 1, hi_rem), + ])); + } else { + // setting the last bit + lock.ensemble.stator.states[p_state].op = + ConcatFields(ConcatFieldsType::from_iter(2, [ + (bits, 0, lo_rem), + (bit, 0, bw(1)), + ])); + } + } else if let Some(rem) = NonZeroUsize::new(bits_w - 1) { + // setting the first bit lock.ensemble.stator.states[p_state].op = - StaticSet([bits, bit], lit.to_usize()); + ConcatFields(ConcatFieldsType::from_iter(2, [ + (bit, 0, bw(1)), + (bits, 1, rem), + ])); + } else { + // setting a single bit + lock.ensemble.stator.states[p_state].op = Copy([bit]); + lock.ensemble.dec_rc(bits).unwrap(); } lock.ensemble.dec_rc(inx).unwrap(); false From 6d2c423202e2416ccd9fc843a365ec7b7156339a Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Tue, 5 Dec 2023 18:43:30 -0600 Subject: [PATCH 04/62] remove `StaticGet` and `StaticSet` --- starlight/src/ensemble/debug.rs | 6 ------ starlight/src/ensemble/state.rs | 29 ----------------------------- starlight/src/lower/lower_op.rs | 2 +- starlight/src/lower/lower_state.rs | 3 +-- 4 files changed, 2 insertions(+), 38 deletions(-) diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index 793d6c0a..a08e5334 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -28,12 +28,6 @@ impl DebugNodeTrait for State { Op::Literal(ref lit) => { v.push(format!("{}", lit)); } - Op::StaticGet(_, inx) => { - v.push(format!("{} get({})", this.nzbw, inx)); - } - Op::StaticSet(_, inx) => { - v.push(format!("{} set({})", this.nzbw, inx)); - } Op::StaticLut(_, ref lut) => { v.push(format!("{} lut({})", this.nzbw, lut)); } diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index e81ee09e..5605aa04 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -310,35 +310,6 @@ impl Ensemble { } assert_eq!(total_len, to); } - StaticGet([bits], inx) => { - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[bits].p_self_bits.len(); - assert!(inx < len); - let p_self_bits = &self.stator.states[p_state].p_self_bits; - assert_eq!(p_self_bits.len(), 1); - let p_equiv0 = p_self_bits[0].unwrap(); - let p_equiv1 = self.stator.states[bits].p_self_bits[inx].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - StaticSet([bits, bit], inx) => { - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[p_state].p_self_bits.len(); - assert_eq!(len, self.stator.states[bits].p_self_bits.len()); - // this must be handled upstream - assert!(inx < len); - for i in 0..len { - let p_equiv0 = self.stator.states[p_state].p_self_bits[i].unwrap(); - if i == inx { - let p_bit = &self.stator.states[bit].p_self_bits; - assert_eq!(p_bit.len(), 1); - let p_equiv1 = p_bit[0].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } else { - let p_equiv1 = self.stator.states[bits].p_self_bits[i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - }; - } - } StaticLut([inx], ref table) => { let table = table.clone(); self.initialize_state_bits_if_needed(p_state).unwrap(); diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs index 40697077..5baffa8d 100644 --- a/starlight/src/lower/lower_op.rs +++ b/starlight/src/lower/lower_op.rs @@ -36,7 +36,7 @@ pub fn lower_op( match start_op { Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), Opaque(..) | Literal(_) | Assert(_) | Copy(_) | Concat(_) | ConcatFields(_) - | StaticLut(..) | StaticGet(..) | StaticSet(..) => return Ok(true), + | StaticLut(..) => return Ok(true), Lut([lut, inx]) => { if m.is_literal(lut) { return Err(EvalError::OtherStr( diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 2629ff49..487bc43b 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -219,8 +219,7 @@ impl Ensemble { } } let needs_lower = match lock.ensemble.stator.states[p_state].op { - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) - | StaticSet(..) | StaticLut(..) => false, + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticLut(..) => false, Lut([lut, inx]) => { if let Literal(ref lit) = lock.ensemble.stator.states[lut].op { let lit = lit.clone(); From c0dafb8ab4149ec0a9f5fc05d3e5bdf00de57e28 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 14:58:05 -0600 Subject: [PATCH 05/62] fixes --- starlight/src/ensemble/debug.rs | 3 +++ starlight/src/ensemble/state.rs | 29 ++++++++++++++++++++++++++--- starlight/src/lower/lower_op.rs | 4 ++-- starlight/src/lower/lower_state.rs | 16 +++++++++------- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index a08e5334..46780d4f 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -28,6 +28,9 @@ impl DebugNodeTrait for State { Op::Literal(ref lit) => { v.push(format!("{}", lit)); } + Op::StaticGet(_, inx) => { + v.push(format!("{} get({})", this.nzbw, inx)); + } Op::StaticLut(_, ref lut) => { v.push(format!("{} lut({})", this.nzbw, lut)); } diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 5605aa04..ba4998ab 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -1,7 +1,7 @@ use std::{fmt::Write, num::NonZeroUsize}; use awint::awint_dag::{ - smallvec::SmallVec, + smallvec::{smallvec, SmallVec}, triple_arena::{Advancer, Arena}, EAwi, EvalError, EvalResult, Location, Op::{self, *}, @@ -265,6 +265,16 @@ impl Ensemble { self.union_equiv(p_equiv0, p_equiv1).unwrap(); } } + StaticGet([bits], inx) => { + self.initialize_state_bits_if_needed(p_state).unwrap(); + let len = self.stator.states[bits].p_self_bits.len(); + assert!(inx < len); + let p_self_bits = &self.stator.states[p_state].p_self_bits; + assert_eq!(p_self_bits.len(), 1); + let p_equiv0 = p_self_bits[0].unwrap(); + let p_equiv1 = self.stator.states[bits].p_self_bits[inx].unwrap(); + self.union_equiv(p_equiv0, p_equiv1).unwrap(); + } Concat(ref concat) => { let concat_len = concat.len(); self.initialize_state_bits_if_needed(p_state).unwrap(); @@ -310,10 +320,23 @@ impl Ensemble { } assert_eq!(total_len, to); } - StaticLut([inx], ref table) => { + StaticLut(ref concat, ref table) => { let table = table.clone(); + let concat_len = concat.len(); + self.initialize_state_bits_if_needed(p_state).unwrap(); + let mut inx_bits: SmallVec<[Option; 8]> = smallvec![]; + for c_i in 0..concat_len { + let c = if let StaticLut(ref concat, _) = self.stator.states[p_state].op + { + concat.as_slice()[c_i] + } else { + unreachable!() + }; + let bits = &self.stator.states[c].p_self_bits; + inx_bits.extend(bits.iter().cloned()); + } + self.initialize_state_bits_if_needed(p_state).unwrap(); - let inx_bits = self.stator.states[inx].p_self_bits.clone(); let inx_len = inx_bits.len(); let out_bw = self.stator.states[p_state].p_self_bits.len(); let num_entries = diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs index 5baffa8d..b0b0b435 100644 --- a/starlight/src/lower/lower_op.rs +++ b/starlight/src/lower/lower_op.rs @@ -35,8 +35,8 @@ pub fn lower_op( ) -> Result { match start_op { Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | Concat(_) | ConcatFields(_) - | StaticLut(..) => return Ok(true), + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) | Concat(_) + | ConcatFields(_) | StaticLut(..) => return Ok(true), Lut([lut, inx]) => { if m.is_literal(lut) { return Err(EvalError::OtherStr( diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 487bc43b..34969c74 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -1,7 +1,7 @@ use std::num::NonZeroUsize; use awint::{ - awint_dag::{smallvec::smallvec, ConcatFieldsType, EvalError, Op::*, PState}, + awint_dag::{smallvec::smallvec, ConcatFieldsType, ConcatType, EvalError, Op::*, PState}, bw, }; @@ -219,7 +219,8 @@ impl Ensemble { } } let needs_lower = match lock.ensemble.stator.states[p_state].op { - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticLut(..) => false, + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) + | StaticLut(..) => false, Lut([lut, inx]) => { if let Literal(ref lit) = lock.ensemble.stator.states[lut].op { let lit = lit.clone(); @@ -239,7 +240,8 @@ impl Ensemble { lock.ensemble.stator.states[p_state].op = Opaque(smallvec![], None); lock.ensemble.dec_rc(inx).unwrap(); } else { - lock.ensemble.stator.states[p_state].op = StaticLut([inx], lit); + lock.ensemble.stator.states[p_state].op = + StaticLut(ConcatType::from_iter([inx]), lit); } lock.ensemble.dec_rc(lut).unwrap(); false @@ -260,7 +262,7 @@ impl Ensemble { lock.ensemble.dec_rc(bits).unwrap(); } else { lock.ensemble.stator.states[p_state].op = ConcatFields( - ConcatFieldsType::from_iter(1, [(bits, lit_u, bw(1))]), + ConcatFieldsType::from_iter([(bits, lit_u, bw(1))]), ); } lock.ensemble.dec_rc(inx).unwrap(); @@ -281,7 +283,7 @@ impl Ensemble { } else if let Some(lo_rem) = NonZeroUsize::new(lit_u) { if let Some(hi_rem) = NonZeroUsize::new(bits_w - 1 - lit_u) { lock.ensemble.stator.states[p_state].op = - ConcatFields(ConcatFieldsType::from_iter(3, [ + ConcatFields(ConcatFieldsType::from_iter([ (bits, 0, lo_rem), (bit, 0, bw(1)), (bits, lit_u + 1, hi_rem), @@ -289,7 +291,7 @@ impl Ensemble { } else { // setting the last bit lock.ensemble.stator.states[p_state].op = - ConcatFields(ConcatFieldsType::from_iter(2, [ + ConcatFields(ConcatFieldsType::from_iter([ (bits, 0, lo_rem), (bit, 0, bw(1)), ])); @@ -297,7 +299,7 @@ impl Ensemble { } else if let Some(rem) = NonZeroUsize::new(bits_w - 1) { // setting the first bit lock.ensemble.stator.states[p_state].op = - ConcatFields(ConcatFieldsType::from_iter(2, [ + ConcatFields(ConcatFieldsType::from_iter([ (bit, 0, bw(1)), (bits, 1, rem), ])); From fb85a91bee387aacd5745711310d79a26d79173a Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 15:12:20 -0600 Subject: [PATCH 06/62] Update meta.rs --- starlight/src/lower/meta.rs | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 0eee0c83..c8b7fcde 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -4,14 +4,32 @@ use std::{cmp::min, mem, num::NonZeroUsize}; use crate::{ awi, + awint_dag::{ConcatType, Lineage, Op}, dag::{awi, inlawi, inlawi_ty, Awi, Bits, InlAwi}, }; const USIZE_BITS: usize = usize::BITS as usize; // This code here is especially messy because we do not want to get into -// infinite lowering loops. These first few functions need to use manual `get` -// and `set` and only literal macros within loop blocks. +// infinite lowering loops. These first few functions need to use manual +// concatenation and only literal macros within loop blocks. + +// note that the $inx arguments are in order from least to most significant +macro_rules! static_lut { + ($lhs:ident; $lut:expr; $($inx:expr),*) => {{ + let nzbw = $lhs.state_nzbw(); + let op = Op::StaticLut( + ConcatType::from_iter([$( + $inx.state(), + )*]), + {use awi::*; awi!($lut)} + ); + $lhs.update_state( + nzbw, + op, + ).unwrap_at_runtime() + }}; +} /// Given `inx.bw()` bits, this returns `2^inx.bw()` signals for every possible /// state of `inx`. The `i`th signal is true only if `inx.to_usize() == i`. @@ -28,19 +46,14 @@ pub fn selector(inx: &Bits, cap: Option) -> Vec { } let lb_num = num.next_power_of_two().trailing_zeros() as usize; let mut signals = vec![]; - let lut0 = inlawi!(0100); - let lut1 = inlawi!(1000); for i in 0..num { let mut signal = inlawi!(1); for j in 0..lb_num { - let mut tmp = inlawi!(00); - tmp.set(0, inx.get(j).unwrap()).unwrap(); - tmp.set(1, signal.to_bool()).unwrap(); // depending on the `j`th bit of `i`, keep the signal line true if (i & (1 << j)) == 0 { - signal.lut_(&lut0, &tmp).unwrap(); + static_lut!(signal; 0100; inx.get(j).unwrap(), signal.to_bool()); } else { - signal.lut_(&lut1, &tmp).unwrap(); + static_lut!(signal; 1000; inx.get(j).unwrap(), signal.to_bool()); } } signals.push(signal); @@ -59,19 +72,14 @@ pub fn selector_awi(inx: &Bits, cap: Option) -> Awi { } let lb_num = num.next_power_of_two().trailing_zeros() as usize; let mut signals = Awi::zero(NonZeroUsize::new(num).unwrap()); - let lut0 = inlawi!(0100); - let lut1 = inlawi!(1000); for i in 0..num { let mut signal = inlawi!(1); for j in 0..lb_num { - let mut tmp = inlawi!(00); - tmp.set(0, inx.get(j).unwrap()).unwrap(); - tmp.set(1, signal.to_bool()).unwrap(); // depending on the `j`th bit of `i`, keep the signal line true if (i & (1 << j)) == 0 { - signal.lut_(&lut0, &tmp).unwrap(); + static_lut!(signal; 0100; inx.get(j).unwrap(), signal.to_bool()); } else { - signal.lut_(&lut1, &tmp).unwrap(); + static_lut!(signal; 1000; inx.get(j).unwrap(), signal.to_bool()); } } signals.set(i, signal.to_bool()).unwrap(); From 2c9aaac3692a3f0fcd4548f69059b3c2428c63ee Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 15:20:27 -0600 Subject: [PATCH 07/62] Update meta.rs --- starlight/src/lower/meta.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index c8b7fcde..30a0b6bb 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -2,6 +2,8 @@ use std::{cmp::min, mem, num::NonZeroUsize}; +use awint::awint_dag::smallvec::smallvec; + use crate::{ awi, awint_dag::{ConcatType, Lineage, Op}, @@ -51,9 +53,9 @@ pub fn selector(inx: &Bits, cap: Option) -> Vec { for j in 0..lb_num { // depending on the `j`th bit of `i`, keep the signal line true if (i & (1 << j)) == 0 { - static_lut!(signal; 0100; inx.get(j).unwrap(), signal.to_bool()); + static_lut!(signal; 0100; inx.get(j).unwrap(), signal); } else { - static_lut!(signal; 1000; inx.get(j).unwrap(), signal.to_bool()); + static_lut!(signal; 1000; inx.get(j).unwrap(), signal); } } signals.push(signal); @@ -71,20 +73,21 @@ pub fn selector_awi(inx: &Bits, cap: Option) -> Awi { return awi!(1) } let lb_num = num.next_power_of_two().trailing_zeros() as usize; - let mut signals = Awi::zero(NonZeroUsize::new(num).unwrap()); + let nzbw = NonZeroUsize::new(num).unwrap(); + let mut signals = smallvec![]; for i in 0..num { let mut signal = inlawi!(1); for j in 0..lb_num { // depending on the `j`th bit of `i`, keep the signal line true if (i & (1 << j)) == 0 { - static_lut!(signal; 0100; inx.get(j).unwrap(), signal.to_bool()); + static_lut!(signal; 0100; inx.get(j).unwrap(), signal); } else { - static_lut!(signal; 1000; inx.get(j).unwrap(), signal.to_bool()); + static_lut!(signal; 1000; inx.get(j).unwrap(), signal); } } - signals.set(i, signal.to_bool()).unwrap(); + signals.push(signal.state()); } - signals + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(signals))) } /// Trailing smear, given the value of `inx` it will set all bits in the vector From 1f07420adfba698915e18a4cd219ec4feacc13ce Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:06:53 -0600 Subject: [PATCH 08/62] Update meta.rs --- starlight/src/lower/meta.rs | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 30a0b6bb..075d8763 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -103,9 +103,6 @@ pub fn tsmear_inx(inx: &Bits, num_signals: usize) -> Vec { lb_num += 1; } let mut signals = vec![]; - let lut_s0 = inlawi!(10010000); - let lut_and = inlawi!(1000); - let lut_or = inlawi!(1110); for i in 0..num_signals { // if `inx < i` let mut signal = inlawi!(0); @@ -116,25 +113,14 @@ pub fn tsmear_inx(inx: &Bits, num_signals: usize) -> Vec { if (i & (1 << j)) == 0 { // update equality, and if the prefix is true and the `j` bit of `inx` is set // then the signal is set - let mut tmp0 = inlawi!(00); - tmp0.set(0, inx.get(j).unwrap()).unwrap(); - tmp0.set(1, prefix_equal.to_bool()).unwrap(); - let mut tmp1 = inlawi!(00); - tmp1.lut_(&lut_s0, &tmp0).unwrap(); - prefix_equal.set(0, tmp1.get(0).unwrap()).unwrap(); - // or into `signal` - let mut tmp = inlawi!(00); - tmp.set(0, tmp1.get(1).unwrap()).unwrap(); - tmp.set(1, signal.to_bool()).unwrap(); - signal.lut_(&lut_or, &tmp).unwrap(); + static_lut!(signal; 11111000; inx.get(j).unwrap(), prefix_equal, signal); + + static_lut!(prefix_equal; 0100; inx.get(j).unwrap(), prefix_equal); } else { // just update equality, the `j`th bit of `i` is 1 and cannot be less than // whatever the `inx` bit is - let mut tmp = inlawi!(00); - tmp.set(0, inx.get(j).unwrap()).unwrap(); - tmp.set(1, prefix_equal.to_bool()).unwrap(); - prefix_equal.lut_(&lut_and, &tmp).unwrap(); + static_lut!(prefix_equal; 1000; inx.get(j).unwrap(), prefix_equal); } } signals.push(signal); From 75fe4992a1072c425b2c3d5cddef3a83c35f8dd0 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:10:07 -0600 Subject: [PATCH 09/62] Update meta.rs --- starlight/src/lower/meta.rs | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 075d8763..10871722 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -2,7 +2,7 @@ use std::{cmp::min, mem, num::NonZeroUsize}; -use awint::awint_dag::smallvec::smallvec; +use awint::awint_dag::smallvec::SmallVec; use crate::{ awi, @@ -74,7 +74,7 @@ pub fn selector_awi(inx: &Bits, cap: Option) -> Awi { } let lb_num = num.next_power_of_two().trailing_zeros() as usize; let nzbw = NonZeroUsize::new(num).unwrap(); - let mut signals = smallvec![]; + let mut signals = SmallVec::with_capacity(num); for i in 0..num { let mut signal = inlawi!(1); for j in 0..lb_num { @@ -135,10 +135,8 @@ pub fn tsmear_awi(inx: &Bits, num_signals: usize) -> Awi { // need extra bit to get all `n + 1` lb_num += 1; } - let mut signals = Awi::zero(NonZeroUsize::new(num_signals).unwrap()); - let lut_s0 = inlawi!(10010000); - let lut_and = inlawi!(1000); - let lut_or = inlawi!(1110); + let nzbw = NonZeroUsize::new(num_signals).unwrap(); + let mut signals = SmallVec::with_capacity(num_signals); for i in 0..num_signals { // if `inx < i` let mut signal = inlawi!(0); @@ -149,30 +147,19 @@ pub fn tsmear_awi(inx: &Bits, num_signals: usize) -> Awi { if (i & (1 << j)) == 0 { // update equality, and if the prefix is true and the `j` bit of `inx` is set // then the signal is set - let mut tmp0 = inlawi!(00); - tmp0.set(0, inx.get(j).unwrap()).unwrap(); - tmp0.set(1, prefix_equal.to_bool()).unwrap(); - let mut tmp1 = inlawi!(00); - tmp1.lut_(&lut_s0, &tmp0).unwrap(); - prefix_equal.set(0, tmp1.get(0).unwrap()).unwrap(); - - // or into `signal` - let mut tmp = inlawi!(00); - tmp.set(0, tmp1.get(1).unwrap()).unwrap(); - tmp.set(1, signal.to_bool()).unwrap(); - signal.lut_(&lut_or, &tmp).unwrap(); + + static_lut!(signal; 11111000; inx.get(j).unwrap(), prefix_equal, signal); + + static_lut!(prefix_equal; 0100; inx.get(j).unwrap(), prefix_equal); } else { // just update equality, the `j`th bit of `i` is 1 and cannot be less than // whatever the `inx` bit is - let mut tmp = inlawi!(00); - tmp.set(0, inx.get(j).unwrap()).unwrap(); - tmp.set(1, prefix_equal.to_bool()).unwrap(); - prefix_equal.lut_(&lut_and, &tmp).unwrap(); + static_lut!(prefix_equal; 1000; inx.get(j).unwrap(), prefix_equal); } } - signals.set(i, signal.to_bool()).unwrap(); + signals.push(signal.state()); } - signals + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(signals))) } pub fn mux_(x0: &Bits, x1: &Bits, inx: &Bits) -> Awi { From 35a84d96b46786af54d7226cab67a4c59e85a32d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:16:40 -0600 Subject: [PATCH 10/62] Update meta.rs --- starlight/src/lower/meta.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 10871722..48c5c83a 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -165,18 +165,14 @@ pub fn tsmear_awi(inx: &Bits, num_signals: usize) -> Awi { pub fn mux_(x0: &Bits, x1: &Bits, inx: &Bits) -> Awi { assert_eq!(x0.bw(), x1.bw()); assert_eq!(inx.bw(), 1); - let mut out = Awi::zero(x0.nzbw()); - let lut = inlawi!(1100_1010); + let nzbw = x0.nzbw(); + let mut signals = SmallVec::with_capacity(nzbw.get()); for i in 0..x0.bw() { - let mut tmp0 = inlawi!(000); - tmp0.set(0, x0.get(i).unwrap()).unwrap(); - tmp0.set(1, x1.get(i).unwrap()).unwrap(); - tmp0.set(2, inx.to_bool()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut, &tmp0).unwrap(); - out.set(i, tmp1.to_bool()).unwrap(); + static_lut!(tmp1; 1100_1010; x0.get(i).unwrap(), x1.get(i).unwrap(), inx); + signals.push(tmp1.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(signals))) } /* From 70c8139b25e6030d95090568103b423da4cbc81f Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:25:05 -0600 Subject: [PATCH 11/62] Update meta.rs --- starlight/src/lower/meta.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 48c5c83a..7b12de3d 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -198,16 +198,10 @@ pub fn dynamic_to_static_lut(out: &mut Bits, table: &Bits, inx: &Bits) { // if this is broken it breaks a lot of stuff assert!(table.bw() == (out.bw().checked_mul(1 << inx.bw()).unwrap())); let signals = selector(inx, None); - let lut = inlawi!(1111_1000); for j in 0..out.bw() { let mut column = inlawi!(0); for (i, signal) in signals.iter().enumerate() { - let mut tmp = inlawi!(000); - tmp.set(0, signal.to_bool()).unwrap(); - tmp.set(1, table.get((i * out.bw()) + j).unwrap()).unwrap(); - tmp.set(2, column.to_bool()).unwrap(); - // if the column is set or both the cell and signal are set - column.lut_(&lut, &tmp).unwrap(); + static_lut!(column; 1111_1000; signal, table.get((i * out.bw()) + j).unwrap(), column); } out.set(j, column.to_bool()).unwrap(); } @@ -218,15 +212,9 @@ pub fn dynamic_to_static_get(bits: &Bits, inx: &Bits) -> inlawi_ty!(1) { return InlAwi::from(bits.to_bool()) } let signals = selector(inx, Some(bits.bw())); - let lut = inlawi!(1111_1000); let mut out = inlawi!(0); for (i, signal) in signals.iter().enumerate() { - let mut tmp = inlawi!(000); - tmp.set(0, signal.to_bool()).unwrap(); - tmp.set(1, bits.get(i).unwrap()).unwrap(); - tmp.set(2, out.to_bool()).unwrap(); - // horizontally OR the product of the signals and `bits` - out.lut_(&lut, &tmp).unwrap(); + static_lut!(out; 1111_1000; signal, bits.get(i).unwrap(), out); } out } From bb76999862bbfed3a60ca692fbcfa792196cf1d1 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:30:19 -0600 Subject: [PATCH 12/62] Update meta.rs --- starlight/src/lower/meta.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 7b12de3d..72ed1dcf 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -168,9 +168,9 @@ pub fn mux_(x0: &Bits, x1: &Bits, inx: &Bits) -> Awi { let nzbw = x0.nzbw(); let mut signals = SmallVec::with_capacity(nzbw.get()); for i in 0..x0.bw() { - let mut tmp1 = inlawi!(0); - static_lut!(tmp1; 1100_1010; x0.get(i).unwrap(), x1.get(i).unwrap(), inx); - signals.push(tmp1.state()); + let mut tmp = inlawi!(0); + static_lut!(tmp; 1100_1010; x0.get(i).unwrap(), x1.get(i).unwrap(), inx); + signals.push(tmp.state()); } Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(signals))) } @@ -224,19 +224,15 @@ pub fn dynamic_to_static_set(bits: &Bits, inx: &Bits, bit: &Bits) -> Awi { return Awi::from(bit) } let signals = selector(inx, Some(bits.bw())); - let mut out = Awi::zero(bits.nzbw()); - let lut = inlawi!(1101_1000); + let nzbw = bits.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); for (i, signal) in signals.iter().enumerate() { - let mut tmp0 = inlawi!(000); - tmp0.set(0, signal.to_bool()).unwrap(); - tmp0.set(1, bit.to_bool()).unwrap(); - tmp0.set(2, bits.get(i).unwrap()).unwrap(); - let mut tmp1 = inlawi!(0); // multiplex between using `bits` or the `bit` depending on the signal - tmp1.lut_(&lut, &tmp0).unwrap(); - out.set(i, tmp1.to_bool()).unwrap(); + let mut tmp = inlawi!(0); + static_lut!(tmp; 1101_1000; signal, bit, bits.get(i).unwrap()); + out.push(tmp.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } pub fn resize(x: &Bits, w: NonZeroUsize, signed: bool) -> Awi { From b0cc2911cbbc942b4ddaae54854483e5efff23d9 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 16:44:52 -0600 Subject: [PATCH 13/62] Update meta.rs --- starlight/src/lower/meta.rs | 41 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 72ed1dcf..824920b7 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -2,7 +2,10 @@ use std::{cmp::min, mem, num::NonZeroUsize}; -use awint::awint_dag::smallvec::SmallVec; +use awint::awint_dag::{ + smallvec::{smallvec, SmallVec}, + ConcatFieldsType, +}; use crate::{ awi, @@ -236,24 +239,32 @@ pub fn dynamic_to_static_set(bits: &Bits, inx: &Bits, bit: &Bits) -> Awi { } pub fn resize(x: &Bits, w: NonZeroUsize, signed: bool) -> Awi { - let mut out = Awi::zero(w); - if out.nzbw() == x.nzbw() { - out.copy_(x).unwrap(); - } else if out.nzbw() < x.nzbw() { - for i in 0..out.bw() { - out.set(i, x.get(i).unwrap()).unwrap(); - } - } else { + if w == x.nzbw() { + return Awi::from_bits(&x); + } else if w < x.nzbw() { + Awi::new( + w, + Op::ConcatFields(ConcatFieldsType::from_iter([(x.state(), 0usize, w)])), + ) + } else if signed { + let mut out = Awi::zero(w); for i in 0..x.bw() { out.set(i, x.get(i).unwrap()).unwrap(); } - if signed { - for i in x.bw()..out.bw() { - out.set(i, x.get(x.bw() - 1).unwrap()).unwrap(); - } - } // else the bits in `out` are automatically zero + for i in x.bw()..out.bw() { + out.set(i, x.get(x.bw() - 1).unwrap()).unwrap(); + } + out + } else { + let zero = Awi::zero(NonZeroUsize::new(w.get() - x.bw()).unwrap()); + Awi::new( + w, + Op::Concat(ConcatType::from_smallvec(smallvec![ + x.state(), + zero.state() + ])), + ) } - out } pub fn resize_cond(x: &Bits, w: NonZeroUsize, signed: &Bits) -> Awi { From 1be8120f5009f8aea8d5155fa78ce5592214e5d5 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 17:11:46 -0600 Subject: [PATCH 14/62] add `Repeat` --- starlight/src/ensemble/state.rs | 16 ++++++++++++++++ starlight/src/lower/lower_op.rs | 2 +- starlight/src/lower/lower_state.rs | 2 +- starlight/src/lower/meta.rs | 21 ++++++++++++--------- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index ba4998ab..754fd816 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -320,6 +320,22 @@ impl Ensemble { } assert_eq!(total_len, to); } + Repeat([x]) => { + self.initialize_state_bits_if_needed(p_state).unwrap(); + let len = self.stator.states[p_state].p_self_bits.len(); + let x_w = self.stator.states[x].p_self_bits.len(); + assert!((len % x_w) == 0); + let mut from = 0; + for to in 0..len { + if from >= x_w { + from = 0; + } + let p_equiv0 = self.stator.states[p_state].p_self_bits[to].unwrap(); + let p_equiv1 = self.stator.states[x].p_self_bits[from].unwrap(); + self.union_equiv(p_equiv0, p_equiv1).unwrap(); + from += 1; + } + } StaticLut(ref concat, ref table) => { let table = table.clone(); let concat_len = concat.len(); diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs index b0b0b435..175708d0 100644 --- a/starlight/src/lower/lower_op.rs +++ b/starlight/src/lower/lower_op.rs @@ -36,7 +36,7 @@ pub fn lower_op( match start_op { Invalid => return Err(EvalError::OtherStr("encountered `Invalid` in lowering")), Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) | Concat(_) - | ConcatFields(_) | StaticLut(..) => return Ok(true), + | ConcatFields(_) | Repeat(_) | StaticLut(..) => return Ok(true), Lut([lut, inx]) => { if m.is_literal(lut) { return Err(EvalError::OtherStr( diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 34969c74..b3434d3c 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -219,7 +219,7 @@ impl Ensemble { } } let needs_lower = match lock.ensemble.stator.states[p_state].op { - Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) + Opaque(..) | Literal(_) | Assert(_) | Copy(_) | StaticGet(..) | Repeat(_) | StaticLut(..) => false, Lut([lut, inx]) => { if let Literal(ref lit) = lock.ensemble.stator.states[lut].op { diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 824920b7..6d3d931b 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -240,21 +240,24 @@ pub fn dynamic_to_static_set(bits: &Bits, inx: &Bits, bit: &Bits) -> Awi { pub fn resize(x: &Bits, w: NonZeroUsize, signed: bool) -> Awi { if w == x.nzbw() { - return Awi::from_bits(&x); + Awi::from_bits(x) } else if w < x.nzbw() { Awi::new( w, Op::ConcatFields(ConcatFieldsType::from_iter([(x.state(), 0usize, w)])), ) } else if signed { - let mut out = Awi::zero(w); - for i in 0..x.bw() { - out.set(i, x.get(i).unwrap()).unwrap(); - } - for i in x.bw()..out.bw() { - out.set(i, x.get(x.bw() - 1).unwrap()).unwrap(); - } - out + let extension = Awi::new( + NonZeroUsize::new(w.get() - x.bw()).unwrap(), + Op::Repeat([x.msb().state()]), + ); + Awi::new( + w, + Op::Concat(ConcatType::from_smallvec(smallvec![ + x.state(), + extension.state() + ])), + ) } else { let zero = Awi::zero(NonZeroUsize::new(w.get() - x.bw()).unwrap()); Awi::new( From 5b164fed286fc7197a55bcb59117a9044a70f01a Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 17:17:45 -0600 Subject: [PATCH 15/62] Update meta.rs --- starlight/src/lower/meta.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 6d3d931b..96dd43b4 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -272,23 +272,27 @@ pub fn resize(x: &Bits, w: NonZeroUsize, signed: bool) -> Awi { pub fn resize_cond(x: &Bits, w: NonZeroUsize, signed: &Bits) -> Awi { assert_eq!(signed.bw(), 1); - let mut out = Awi::zero(w); - if out.nzbw() == x.nzbw() { - out.copy_(x).unwrap(); - } else if out.nzbw() < x.nzbw() { - for i in 0..out.bw() { - out.set(i, x.get(i).unwrap()).unwrap(); - } + if w == x.nzbw() { + Awi::from_bits(x) + } else if w < x.nzbw() { + Awi::new( + w, + Op::ConcatFields(ConcatFieldsType::from_iter([(x.state(), 0usize, w)])), + ) } else { - for i in 0..x.bw() { - out.set(i, x.get(i).unwrap()).unwrap(); - } - let signed = signed.to_bool(); - for i in x.bw()..out.bw() { - out.set(i, signed).unwrap(); - } + let extend = x.msb() & signed.to_bool(); + let extension = Awi::new( + NonZeroUsize::new(w.get() - x.bw()).unwrap(), + Op::Repeat([extend.state()]), + ); + Awi::new( + w, + Op::Concat(ConcatType::from_smallvec(smallvec![ + x.state(), + extension.state() + ])), + ) } - out } /// Returns (`lhs`, true) if there are invalid values From ba74c11ab5864238f0a677dfda08e565d5a23fb1 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 17:39:37 -0600 Subject: [PATCH 16/62] Update meta.rs --- starlight/src/lower/meta.rs | 48 +++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 96dd43b4..d93bcabf 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -297,19 +297,53 @@ pub fn resize_cond(x: &Bits, w: NonZeroUsize, signed: &Bits) -> Awi { /// Returns (`lhs`, true) if there are invalid values pub fn static_field(lhs: &Bits, to: usize, rhs: &Bits, from: usize, width: usize) -> (Awi, bool) { - let mut out = Awi::from_bits(lhs); if (width > lhs.bw()) || (width > rhs.bw()) || (to > (lhs.bw() - width)) || (from > (rhs.bw() - width)) { - (out, true) - } else { - for i in 0..width { - out.set(i + to, rhs.get(i + from).unwrap()).unwrap(); - } - (out, false) + return (Awi::from_bits(lhs), true); } + let res = if let Some(width) = NonZeroUsize::new(width) { + if let Some(lhs_rem_lo) = NonZeroUsize::new(to) { + if let Some(lhs_rem_hi) = NonZeroUsize::new(from) { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([ + (lhs.state(), 0usize, lhs_rem_lo), + (rhs.state(), from, width), + (lhs.state(), to + width.get(), lhs_rem_hi), + ])), + ) + } else { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([ + (lhs.state(), 0usize, lhs_rem_lo), + (rhs.state(), from, width), + ])), + ) + } + } else { + if let Some(lhs_rem_hi) = NonZeroUsize::new(lhs.bw() - width.get()) { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([ + (rhs.state(), from, width), + (lhs.state(), width.get(), lhs_rem_hi), + ])), + ) + } else { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([(rhs.state(), from, width)])), + ) + } + } + } else { + Awi::from_bits(lhs) + }; + (res, false) } /// This does not handle invalid arguments; set `width` to zero to cause no-ops From 9c8d28775605383d292850d912d37f71319d578d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 22:25:58 -0600 Subject: [PATCH 17/62] Update meta.rs --- starlight/src/lower/meta.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index d93bcabf..be0326b3 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -348,21 +348,28 @@ pub fn static_field(lhs: &Bits, to: usize, rhs: &Bits, from: usize, width: usize /// This does not handle invalid arguments; set `width` to zero to cause no-ops pub fn field_width(lhs: &Bits, rhs: &Bits, width: &Bits) -> Awi { - let mut out = Awi::from_bits(lhs); let min_w = min(lhs.bw(), rhs.bw()); let signals = tsmear_inx(width, min_w); - let lut = inlawi!(1100_1010); + let nzbw = NonZeroUsize::new(signals.len()).unwrap(); + let mut mux_part = SmallVec::with_capacity(nzbw.get()); for (i, signal) in signals.into_iter().enumerate() { // mux_ between `lhs` or `rhs` based on the signal - let mut tmp0 = inlawi!(000); - tmp0.set(0, lhs.get(i).unwrap()).unwrap(); - tmp0.set(1, rhs.get(i).unwrap()).unwrap(); - tmp0.set(2, signal.to_bool()).unwrap(); - let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut, &tmp0).unwrap(); - out.set(i, tmp1.to_bool()).unwrap(); + let mut tmp = inlawi!(0); + static_lut!(tmp; 1100_1010; lhs.get(i).unwrap(), rhs.get(i).unwrap(), signal); + mux_part.push(tmp.state()); + } + let mux_part = Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(mux_part))); + if let Some(lhs_rem_hi) = NonZeroUsize::new(lhs.bw() - nzbw.get()) { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([ + (mux_part.state(), 0usize, nzbw), + (lhs.state(), nzbw.get(), lhs_rem_hi), + ])), + ) + } else { + mux_part } - out } /// Given the diagonal control lines and input of a crossbar with output width From f9e3b0b5492a061ec734207864762d471ee2b133 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 22:36:43 -0600 Subject: [PATCH 18/62] Update meta.rs --- starlight/src/lower/meta.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index be0326b3..56be7e15 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -19,6 +19,12 @@ const USIZE_BITS: usize = usize::BITS as usize; // infinite lowering loops. These first few functions need to use manual // concatenation and only literal macros within loop blocks. +// Everything used to be done through `get` and `set`, but going straight to +// `StaticLut` or `Concat` or `ConcatFields` is a massive performance boost. + +// TODO In the future if we want something more, we should have some kind of +// caching for known optimization results. + // note that the $inx arguments are in order from least to most significant macro_rules! static_lut { ($lhs:ident; $lut:expr; $($inx:expr),*) => {{ @@ -385,22 +391,23 @@ pub fn crossbar( ) { assert!(signal_range.0 < signal_range.1); assert_eq!(signal_range.1 - signal_range.0, signals.len()); + + let nzbw = output.nzbw(); + let mut tmp_output = SmallVec::with_capacity(nzbw.get()); for j in 0..output.bw() { // output bar for ORing let mut out_bar = inlawi!(0); for i in 0..input.bw() { let signal_inx = output.bw() - 1 + i - j; if (signal_inx >= signal_range.0) && (signal_inx < signal_range.1) { - let mut inx = inlawi!(000); - inx.set(0, input.get(i).unwrap()).unwrap(); - inx.set(1, signals[signal_inx - signal_range.0].to_bool()) - .unwrap(); - inx.set(2, out_bar.to_bool()).unwrap(); - out_bar.lut_(&inlawi!(1111_1000), &inx).unwrap(); + static_lut!(out_bar; 1111_1000; input.get(i).unwrap(), signals[signal_inx - signal_range.0], out_bar); } } - output.set(j, out_bar.to_bool()).unwrap(); + tmp_output.push(out_bar.state()); } + output + .update_state(nzbw, Op::Concat(ConcatType::from_smallvec(tmp_output))) + .unwrap_at_runtime(); } pub fn funnel_(x: &Bits, s: &Bits) -> Awi { From 656b662971acc5d4aa10a18eb47a7e3ff5f79a27 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 22:39:28 -0600 Subject: [PATCH 19/62] Update meta.rs --- starlight/src/lower/meta.rs | 38 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 56be7e15..59af21a4 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -207,13 +207,17 @@ pub fn dynamic_to_static_lut(out: &mut Bits, table: &Bits, inx: &Bits) { // if this is broken it breaks a lot of stuff assert!(table.bw() == (out.bw().checked_mul(1 << inx.bw()).unwrap())); let signals = selector(inx, None); + let nzbw = out.nzbw(); + let mut tmp_output = SmallVec::with_capacity(nzbw.get()); for j in 0..out.bw() { let mut column = inlawi!(0); for (i, signal) in signals.iter().enumerate() { static_lut!(column; 1111_1000; signal, table.get((i * out.bw()) + j).unwrap(), column); } - out.set(j, column.to_bool()).unwrap(); + tmp_output.push(column.state()); } + out.update_state(nzbw, Op::Concat(ConcatType::from_smallvec(tmp_output))) + .unwrap_at_runtime(); } pub fn dynamic_to_static_get(bits: &Bits, inx: &Bits) -> inlawi_ty!(1) { @@ -330,21 +334,19 @@ pub fn static_field(lhs: &Bits, to: usize, rhs: &Bits, from: usize, width: usize ])), ) } + } else if let Some(lhs_rem_hi) = NonZeroUsize::new(lhs.bw() - width.get()) { + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([ + (rhs.state(), from, width), + (lhs.state(), width.get(), lhs_rem_hi), + ])), + ) } else { - if let Some(lhs_rem_hi) = NonZeroUsize::new(lhs.bw() - width.get()) { - Awi::new( - lhs.nzbw(), - Op::ConcatFields(ConcatFieldsType::from_iter([ - (rhs.state(), from, width), - (lhs.state(), width.get(), lhs_rem_hi), - ])), - ) - } else { - Awi::new( - lhs.nzbw(), - Op::ConcatFields(ConcatFieldsType::from_iter([(rhs.state(), from, width)])), - ) - } + Awi::new( + lhs.nzbw(), + Op::ConcatFields(ConcatFieldsType::from_iter([(rhs.state(), from, width)])), + ) } } else { Awi::from_bits(lhs) @@ -400,7 +402,11 @@ pub fn crossbar( for i in 0..input.bw() { let signal_inx = output.bw() - 1 + i - j; if (signal_inx >= signal_range.0) && (signal_inx < signal_range.1) { - static_lut!(out_bar; 1111_1000; input.get(i).unwrap(), signals[signal_inx - signal_range.0], out_bar); + static_lut!(out_bar; 1111_1000; + input.get(i).unwrap(), + signals[signal_inx - signal_range.0], + out_bar + ); } } tmp_output.push(out_bar.state()); From d9e622c3a5b8948952c6e65b2821eee961c2b8fb Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 22:48:53 -0600 Subject: [PATCH 20/62] Update meta.rs --- starlight/src/lower/meta.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 59af21a4..9cce13d4 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -472,6 +472,7 @@ pub fn ashr(x: &Bits, s: &Bits) -> Awi { // Not sure if there is a better way to do this. If we try to use the crossbar // signals in some way, we are guaranteed some kind of > O(1) time thing. + let msb = x.msb(); // get the `lb_num` that `tsmear_inx` uses, it can be `x.bw() - 1` because of // the `s < x.bw()` requirement, this single bit of difference is important // for powers of two because of the `lb_num += 1` condition it avoids. @@ -484,26 +485,18 @@ pub fn ashr(x: &Bits, s: &Bits) -> Awi { } if let Some(w) = NonZeroUsize::new(lb_num) { let mut gated_s = Awi::zero(w); - let lut_and = inlawi!(1000); // `gated_s` will be zero if `x.msb()` is zero, in which case `tsmear_inx` // produces all zeros to be ORed for i in 0..gated_s.bw() { - let mut tmp0 = inlawi!(00); - tmp0.set(0, s.get(i).unwrap()).unwrap(); - tmp0.set(1, x.msb()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut_and, &tmp0).unwrap(); + static_lut!(tmp1; 1000; s.get(i).unwrap(), msb); gated_s.set(i, tmp1.to_bool()).unwrap(); } let or_mask = tsmear_awi(&gated_s, num); - let lut_or = inlawi!(1110); for i in 0..or_mask.bw() { let out_i = out.bw() - 1 - i; - let mut tmp0 = inlawi!(00); - tmp0.set(0, out.get(out_i).unwrap()).unwrap(); - tmp0.set(1, or_mask.get(i).unwrap()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut_or, &tmp0).unwrap(); + static_lut!(tmp1; 1110; out.get(out_i).unwrap(), or_mask.get(i).unwrap()); out.set(out_i, tmp1.to_bool()).unwrap(); } } @@ -545,14 +538,14 @@ pub fn rotr(x: &Bits, s: &Bits) -> Awi { } pub fn bitwise_not(x: &Bits) -> Awi { - let mut out = Awi::zero(x.nzbw()); + let nzbw = x.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); for i in 0..x.bw() { let mut tmp = inlawi!(0); - let inx = InlAwi::from(x.get(i).unwrap()); - tmp.lut_(&inlawi!(01), &inx).unwrap(); - out.set(i, tmp.to_bool()).unwrap(); + static_lut!(tmp; 01; x.get(i).unwrap()); + out.push(tmp.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } pub fn bitwise(lhs: &Bits, rhs: &Bits, lut: inlawi_ty!(4)) -> Awi { From a60e2a8334922cef5239790544267d016a4573f8 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 22:58:19 -0600 Subject: [PATCH 21/62] improve --- starlight/src/lower/lower_op.rs | 16 +++++++++++++--- starlight/src/lower/meta.rs | 31 ++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs index 175708d0..928b467c 100644 --- a/starlight/src/lower/lower_op.rs +++ b/starlight/src/lower/lower_op.rs @@ -17,6 +17,7 @@ use awint::{ }; use super::meta::*; +use crate::awi; pub trait LowerManagement { fn graft(&mut self, output_and_operands: &[PState]); @@ -311,19 +312,28 @@ pub fn lower_op( Or([lhs, rhs]) => { let lhs = Awi::opaque(m.get_nzbw(lhs)); let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(1110)); + let out = bitwise(&lhs, &rhs, { + use awi::*; + awi!(1110) + }); m.graft(&[out.state(), lhs.state(), rhs.state()]); } And([lhs, rhs]) => { let lhs = Awi::opaque(m.get_nzbw(lhs)); let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(1000)); + let out = bitwise(&lhs, &rhs, { + use awi::*; + awi!(1000) + }); m.graft(&[out.state(), lhs.state(), rhs.state()]); } Xor([lhs, rhs]) => { let lhs = Awi::opaque(m.get_nzbw(lhs)); let rhs = Awi::opaque(m.get_nzbw(rhs)); - let out = bitwise(&lhs, &rhs, inlawi!(0110)); + let out = bitwise(&lhs, &rhs, { + use awi::*; + awi!(0110) + }); m.graft(&[out.state(), lhs.state(), rhs.state()]); } Inc([x, cin]) => { diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 9cce13d4..a01a1cc9 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -2,9 +2,12 @@ use std::{cmp::min, mem, num::NonZeroUsize}; -use awint::awint_dag::{ - smallvec::{smallvec, SmallVec}, - ConcatFieldsType, +use awint::{ + awint_dag::{ + smallvec::{smallvec, SmallVec}, + ConcatFieldsType, + }, + bw, }; use crate::{ @@ -548,18 +551,24 @@ pub fn bitwise_not(x: &Bits) -> Awi { Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } -pub fn bitwise(lhs: &Bits, rhs: &Bits, lut: inlawi_ty!(4)) -> Awi { +pub fn bitwise(lhs: &Bits, rhs: &Bits, lut: awi::Awi) -> Awi { assert_eq!(lhs.bw(), rhs.bw()); - let mut out = Awi::zero(lhs.nzbw()); + assert_eq!(lut.bw(), 4); + let nzbw = lhs.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); for i in 0..lhs.bw() { let mut tmp = inlawi!(0); - let mut inx = inlawi!(00); - inx.set(0, lhs.get(i).unwrap()).unwrap(); - inx.set(1, rhs.get(i).unwrap()).unwrap(); - tmp.lut_(&lut, &inx).unwrap(); - out.set(i, tmp.to_bool()).unwrap(); + tmp.update_state( + bw(1), + Op::StaticLut( + ConcatType::from_iter([lhs.get(i).unwrap().state(), rhs.get(i).unwrap().state()]), + lut.clone(), + ), + ) + .unwrap_at_runtime(); + out.push(tmp.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } pub fn incrementer(x: &Bits, cin: &Bits, dec: bool) -> (Awi, inlawi_ty!(1)) { From eefc04e02d36c7752f2876439294747107602617 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 23:11:20 -0600 Subject: [PATCH 22/62] Update meta.rs --- starlight/src/lower/meta.rs | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index a01a1cc9..eff0c190 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -573,24 +573,32 @@ pub fn bitwise(lhs: &Bits, rhs: &Bits, lut: awi::Awi) -> Awi { pub fn incrementer(x: &Bits, cin: &Bits, dec: bool) -> (Awi, inlawi_ty!(1)) { assert_eq!(cin.bw(), 1); - // half adder or subtractor - let lut = if dec { - inlawi!(1110_1001) - } else { - inlawi!(1001_0100) - }; - let mut out = Awi::zero(x.nzbw()); + let nzbw = x.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); let mut carry = InlAwi::from(cin.to_bool()); - for i in 0..x.bw() { - let mut carry_sum = inlawi!(00); - let mut inx = inlawi!(00); - inx.set(0, carry.to_bool()).unwrap(); - inx.set(1, x.get(i).unwrap()).unwrap(); - carry_sum.lut_(&lut, &inx).unwrap(); - out.set(i, carry_sum.get(0).unwrap()).unwrap(); - carry.bool_(carry_sum.get(1).unwrap()); + if dec { + for i in 0..x.bw() { + let mut tmp = inlawi!(0); + let b = x.get(i).unwrap(); + // half subtractor + static_lut!(tmp; 1001; carry, b); + out.push(tmp.state()); + static_lut!(carry; 1110; carry, b); + } + } else { + for i in 0..x.bw() { + let mut tmp = inlawi!(0); + let b = x.get(i).unwrap(); + // half adder + static_lut!(tmp; 0110; carry, b); + out.push(tmp.state()); + static_lut!(carry; 1000; carry, b); + } } - (out, carry) + ( + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))), + carry, + ) } // TODO select carry adder From cf785441ff98547a3ff6f5aadecb834ec756cbc9 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 23:29:36 -0600 Subject: [PATCH 23/62] Update meta.rs --- starlight/src/lower/meta.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index eff0c190..c826f74f 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -623,26 +623,18 @@ pub fn cin_sum(cin: &Bits, lhs: &Bits, rhs: &Bits) -> (Awi, inlawi_ty!(1), inlaw assert_eq!(cin.bw(), 1); assert_eq!(lhs.bw(), rhs.bw()); let w = lhs.bw(); - // full adder - let lut = inlawi!(1110_1001_1001_0100); let mut out = Awi::zero(lhs.nzbw()); + //let nzbw = lhs.nzbw(); + //let mut out = SmallVec::with_capacity(nzbw.get()); let mut carry = InlAwi::from(cin.to_bool()); for i in 0..w { let mut carry_sum = inlawi!(00); - let mut inx = inlawi!(000); - inx.set(0, carry.to_bool()).unwrap(); - inx.set(1, lhs.get(i).unwrap()).unwrap(); - inx.set(2, rhs.get(i).unwrap()).unwrap(); - carry_sum.lut_(&lut, &inx).unwrap(); + static_lut!(carry_sum; 1110_1001_1001_0100; carry, lhs.get(i).unwrap(), rhs.get(i).unwrap()); out.set(i, carry_sum.get(0).unwrap()).unwrap(); carry.bool_(carry_sum.get(1).unwrap()); } let mut signed_overflow = inlawi!(0); - let mut inx = inlawi!(000); - inx.set(0, lhs.get(w - 1).unwrap()).unwrap(); - inx.set(1, rhs.get(w - 1).unwrap()).unwrap(); - inx.set(2, out.get(w - 1).unwrap()).unwrap(); - signed_overflow.lut_(&inlawi!(0001_1000), &inx).unwrap(); + static_lut!(signed_overflow; 0001_1000; lhs.get(w - 1).unwrap(), rhs.get(w - 1).unwrap(), out.get(w - 1).unwrap()); (out, carry, signed_overflow) } From 50b031b358455c93fcb5cacb2b88ae6afe553557 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Wed, 6 Dec 2023 23:38:17 -0600 Subject: [PATCH 24/62] Update meta.rs --- starlight/src/lower/meta.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index c826f74f..2a8b3787 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -623,19 +623,33 @@ pub fn cin_sum(cin: &Bits, lhs: &Bits, rhs: &Bits) -> (Awi, inlawi_ty!(1), inlaw assert_eq!(cin.bw(), 1); assert_eq!(lhs.bw(), rhs.bw()); let w = lhs.bw(); - let mut out = Awi::zero(lhs.nzbw()); - //let nzbw = lhs.nzbw(); - //let mut out = SmallVec::with_capacity(nzbw.get()); + let nzbw = lhs.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); let mut carry = InlAwi::from(cin.to_bool()); for i in 0..w { let mut carry_sum = inlawi!(00); static_lut!(carry_sum; 1110_1001_1001_0100; carry, lhs.get(i).unwrap(), rhs.get(i).unwrap()); - out.set(i, carry_sum.get(0).unwrap()).unwrap(); + out.push(carry_sum.get(0).unwrap().state()); carry.bool_(carry_sum.get(1).unwrap()); } let mut signed_overflow = inlawi!(0); - static_lut!(signed_overflow; 0001_1000; lhs.get(w - 1).unwrap(), rhs.get(w - 1).unwrap(), out.get(w - 1).unwrap()); - (out, carry, signed_overflow) + let a = lhs.get(w - 1).unwrap().state(); + let b = rhs.get(w - 1).unwrap().state(); + let c = *out.get(w - 1).unwrap(); + signed_overflow + .update_state( + bw(1), + Op::StaticLut(ConcatType::from_iter([a, b, c]), { + use awi::*; + awi!(0001_1000) + }), + ) + .unwrap_at_runtime(); + ( + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))), + carry, + signed_overflow, + ) } pub fn negator(x: &Bits, neg: &Bits) -> Awi { From 310de677030708bc48827db94e92558473ff7f52 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 00:11:41 -0600 Subject: [PATCH 25/62] Update meta.rs --- starlight/src/lower/meta.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 2a8b3787..ef14418e 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -654,21 +654,17 @@ pub fn cin_sum(cin: &Bits, lhs: &Bits, rhs: &Bits) -> (Awi, inlawi_ty!(1), inlaw pub fn negator(x: &Bits, neg: &Bits) -> Awi { assert_eq!(neg.bw(), 1); - // half adder with input inversion control - let lut = inlawi!(0100_1001_1001_0100); - let mut out = Awi::zero(x.nzbw()); + let nzbw = x.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); let mut carry = InlAwi::from(neg.to_bool()); for i in 0..x.bw() { let mut carry_sum = inlawi!(00); - let mut inx = inlawi!(000); - inx.set(0, carry.to_bool()).unwrap(); - inx.set(1, x.get(i).unwrap()).unwrap(); - inx.set(2, neg.to_bool()).unwrap(); - carry_sum.lut_(&lut, &inx).unwrap(); - out.set(i, carry_sum.get(0).unwrap()).unwrap(); + // half adder with input inversion control + static_lut!(carry_sum; 0100_1001_1001_0100; carry, x.get(i).unwrap(), neg); + out.push(carry_sum.get(0).unwrap().state()); carry.bool_(carry_sum.get(1).unwrap()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } /// Setting `width` to 0 guarantees that nothing happens even with other From bb9c473dc1ae4b06520a64c99fb4b00c8da5c6b6 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 00:16:57 -0600 Subject: [PATCH 26/62] Update meta.rs --- starlight/src/lower/meta.rs | 41 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index ef14418e..5ad8ed5b 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -701,19 +701,20 @@ pub fn field_to(lhs: &Bits, to: &Bits, rhs: &Bits, width: &Bits) -> Awi { let mut lmask = tsmear_inx(&tmp, lhs.bw()); lmask.reverse(); - let mut out = Awi::from_bits(lhs); - let lut = inlawi!(1011_1111_1000_0000); + let nzbw = lhs.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); + // when `tmask` and `lmask` are both set, mux_ in `rhs` for i in 0..lhs.bw() { - let mut tmp = inlawi!(0000); - tmp.set(0, rhs_to_lhs.get(i).unwrap()).unwrap(); - tmp.set(1, tmask[i].to_bool()).unwrap(); - tmp.set(2, lmask[i].to_bool()).unwrap(); - tmp.set(3, lhs.get(i).unwrap()).unwrap(); let mut lut_out = inlawi!(0); - lut_out.lut_(&lut, &tmp).unwrap(); - out.set(i, lut_out.to_bool()).unwrap(); + static_lut!(lut_out; 1011_1111_1000_0000; + rhs_to_lhs.get(i).unwrap(), + tmask[i], + lmask[i], + lhs.get(i).unwrap() + ); + out.push(lut_out.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } else { let lut = inlawi!(rhs[0], lhs[0]).unwrap(); let mut out = awi!(0); @@ -768,20 +769,20 @@ pub fn field(lhs: &Bits, to: &Bits, rhs: &Bits, from: &Bits, width: &Bits) -> Aw let mut lmask = tsmear_inx(&tmp, lhs.bw()); lmask.reverse(); - let mut out = Awi::from_bits(lhs); + let nzbw = lhs.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); // when `tmask` and `lmask` are both set, mux_ in `rhs` - let lut = inlawi!(1011_1111_1000_0000); for i in 0..lhs.bw() { - let mut tmp = inlawi!(0000); - tmp.set(0, rhs_to_lhs.get(i).unwrap()).unwrap(); - tmp.set(1, tmask[i].to_bool()).unwrap(); - tmp.set(2, lmask[i].to_bool()).unwrap(); - tmp.set(3, lhs.get(i).unwrap()).unwrap(); let mut lut_out = inlawi!(0); - lut_out.lut_(&lut, &tmp).unwrap(); - out.set(i, lut_out.to_bool()).unwrap(); + static_lut!(lut_out; 1011_1111_1000_0000; + rhs_to_lhs.get(i).unwrap(), + tmask[i], + lmask[i], + lhs.get(i).unwrap() + ); + out.push(lut_out.state()); } - out + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) } else { // `lhs.bw() == 1`, `rhs.bw() == 1`, `width` is the only thing that matters let lut = inlawi!(rhs[0], lhs[0]).unwrap(); From 7ec2f98788156234eca8bc415787e6a39719f307 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 00:23:44 -0600 Subject: [PATCH 27/62] Update meta.rs --- starlight/src/lower/meta.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 5ad8ed5b..3342f8dc 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -794,17 +794,12 @@ pub fn field(lhs: &Bits, to: &Bits, rhs: &Bits, from: &Bits, width: &Bits) -> Aw pub fn equal(lhs: &Bits, rhs: &Bits) -> inlawi_ty!(1) { let mut ranks = vec![vec![]]; - let lut_xnor = inlawi!(1001); for i in 0..lhs.bw() { - let mut tmp0 = inlawi!(00); - tmp0.set(0, lhs.get(i).unwrap()).unwrap(); - tmp0.set(1, rhs.get(i).unwrap()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut_xnor, &tmp0).unwrap(); + static_lut!(tmp1; 1001; lhs.get(i).unwrap(), rhs.get(i).unwrap()); ranks[0].push(tmp1); } // binary tree reduce - let lut_and = inlawi!(1000); loop { let prev_rank = ranks.last().unwrap(); let rank_len = prev_rank.len(); @@ -813,11 +808,8 @@ pub fn equal(lhs: &Bits, rhs: &Bits) -> inlawi_ty!(1) { } let mut next_rank = vec![]; for i in 0..(rank_len / 2) { - let mut tmp0 = inlawi!(00); - tmp0.set(0, prev_rank[2 * i].to_bool()).unwrap(); - tmp0.set(1, prev_rank[2 * i + 1].to_bool()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut_and, &tmp0).unwrap(); + static_lut!(tmp1; 1000; prev_rank[2 * i], prev_rank[2 * i + 1]); next_rank.push(tmp1); } if (rank_len & 1) != 0 { @@ -926,17 +918,12 @@ pub fn lut_set(table: &Bits, entry: &Bits, inx: &Bits) -> Awi { assert_eq!(table.bw(), entry.bw() * num_entries); let signals = selector(inx, Some(num_entries)); let mut out = Awi::from_bits(table); - let lut_mux = inlawi!(1100_1010); for (j, signal) in signals.into_iter().enumerate() { for i in 0..entry.bw() { let lut_inx = i + (j * entry.bw()); // mux_ between `lhs` or `entry` based on the signal - let mut tmp0 = inlawi!(000); - tmp0.set(0, table.get(lut_inx).unwrap()).unwrap(); - tmp0.set(1, entry.get(i).unwrap()).unwrap(); - tmp0.set(2, signal.to_bool()).unwrap(); let mut tmp1 = inlawi!(0); - tmp1.lut_(&lut_mux, &tmp0).unwrap(); + static_lut!(tmp1; 1100_1010; table.get(lut_inx).unwrap(), entry.get(i).unwrap(), signal); out.set(lut_inx, tmp1.to_bool()).unwrap(); } } From bdbbf11966884bef1f9eb16c03348c44002c1340 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 00:32:04 -0600 Subject: [PATCH 28/62] Update meta.rs --- starlight/src/lower/meta.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 3342f8dc..009b1a26 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -126,9 +126,10 @@ pub fn tsmear_inx(inx: &Bits, num_signals: usize) -> Vec { // update equality, and if the prefix is true and the `j` bit of `inx` is set // then the signal is set - static_lut!(signal; 11111000; inx.get(j).unwrap(), prefix_equal, signal); + let inx_j = inx.get(j).unwrap(); + static_lut!(signal; 11111000; inx_j, prefix_equal, signal); - static_lut!(prefix_equal; 0100; inx.get(j).unwrap(), prefix_equal); + static_lut!(prefix_equal; 0100; inx_j, prefix_equal); } else { // just update equality, the `j`th bit of `i` is 1 and cannot be less than // whatever the `inx` bit is @@ -160,9 +161,10 @@ pub fn tsmear_awi(inx: &Bits, num_signals: usize) -> Awi { // update equality, and if the prefix is true and the `j` bit of `inx` is set // then the signal is set - static_lut!(signal; 11111000; inx.get(j).unwrap(), prefix_equal, signal); + let inx_j = inx.get(j).unwrap(); + static_lut!(signal; 11111000; inx_j, prefix_equal, signal); - static_lut!(prefix_equal; 0100; inx.get(j).unwrap(), prefix_equal); + static_lut!(prefix_equal; 0100; inx_j, prefix_equal); } else { // just update equality, the `j`th bit of `i` is 1 and cannot be less than // whatever the `inx` bit is @@ -628,7 +630,11 @@ pub fn cin_sum(cin: &Bits, lhs: &Bits, rhs: &Bits) -> (Awi, inlawi_ty!(1), inlaw let mut carry = InlAwi::from(cin.to_bool()); for i in 0..w { let mut carry_sum = inlawi!(00); - static_lut!(carry_sum; 1110_1001_1001_0100; carry, lhs.get(i).unwrap(), rhs.get(i).unwrap()); + static_lut!(carry_sum; 1110_1001_1001_0100; + carry, + lhs.get(i).unwrap(), + rhs.get(i).unwrap() + ); out.push(carry_sum.get(0).unwrap().state()); carry.bool_(carry_sum.get(1).unwrap()); } @@ -923,7 +929,11 @@ pub fn lut_set(table: &Bits, entry: &Bits, inx: &Bits) -> Awi { let lut_inx = i + (j * entry.bw()); // mux_ between `lhs` or `entry` based on the signal let mut tmp1 = inlawi!(0); - static_lut!(tmp1; 1100_1010; table.get(lut_inx).unwrap(), entry.get(i).unwrap(), signal); + static_lut!(tmp1; 1100_1010; + table.get(lut_inx).unwrap(), + entry.get(i).unwrap(), + signal + ); out.set(lut_inx, tmp1.to_bool()).unwrap(); } } @@ -938,7 +948,6 @@ pub fn mul_add(out_w: NonZeroUsize, add: Option<&Bits>, lhs: &Bits, rhs: &Bits) (lhs, rhs) }; - let and = inlawi!(1000); let place_map0: &mut Vec> = &mut vec![]; let place_map1: &mut Vec> = &mut vec![]; for _ in 0..out_w.get() { @@ -946,13 +955,11 @@ pub fn mul_add(out_w: NonZeroUsize, add: Option<&Bits>, lhs: &Bits, rhs: &Bits) place_map1.push(vec![]); } for j in 0..rhs.bw() { + let rhs_j = rhs.get(j).unwrap(); for i in 0..lhs.bw() { if let Some(place) = place_map0.get_mut(i + j) { - let mut tmp = inlawi!(00); - tmp.set(0, rhs.get(j).unwrap()).unwrap(); - tmp.set(1, lhs.get(i).unwrap()).unwrap(); let mut ji = inlawi!(0); - ji.lut_(&and, &tmp).unwrap(); + static_lut!(ji; 1000; rhs_j, lhs.get(i).unwrap()); place.push(ji); } } From 478c7c19f854fa91e8e7a083b85d488f2485113e Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 00:40:34 -0600 Subject: [PATCH 29/62] finish pass over meta --- starlight/src/lower/lower_op.rs | 5 +---- starlight/src/lower/meta.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/starlight/src/lower/lower_op.rs b/starlight/src/lower/lower_op.rs index 928b467c..bcc9e095 100644 --- a/starlight/src/lower/lower_op.rs +++ b/starlight/src/lower/lower_op.rs @@ -494,10 +494,7 @@ pub fn lower_op( } Rev([x]) => { let x = Awi::opaque(m.get_nzbw(x)); - let mut out = Awi::zero(x.nzbw()); - for i in 0..x.bw() { - out.set(i, x.get(x.bw() - 1 - i).unwrap()).unwrap() - } + let out = reverse(&x); m.graft(&[out.state(), x.state()]); } Eq([lhs, rhs]) => { diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index 009b1a26..cebec188 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -45,6 +45,15 @@ macro_rules! static_lut { }}; } +pub fn reverse(x: &Bits) -> Awi { + let nzbw = x.nzbw(); + let mut out = SmallVec::with_capacity(nzbw.get()); + for i in 0..x.bw() { + out.push(x.get(x.bw() - 1 - i).unwrap().state()) + } + Awi::new(nzbw, Op::Concat(ConcatType::from_smallvec(out))) +} + /// Given `inx.bw()` bits, this returns `2^inx.bw()` signals for every possible /// state of `inx`. The `i`th signal is true only if `inx.to_usize() == i`. /// `cap` optionally restricts the number of signals. If `cap` is 0, there is From 79ccd6d0d048a63c2df757266d6a92d5cd1d071f Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 17:38:23 -0600 Subject: [PATCH 30/62] fix `LazyAwi` --- starlight/src/awi_structs/lazy_awi.rs | 38 +++++---------------------- starlight/src/ensemble/note.rs | 20 ++++++++++++-- starlight/src/ensemble/value.rs | 31 +++++++++++----------- 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/starlight/src/awi_structs/lazy_awi.rs b/starlight/src/awi_structs/lazy_awi.rs index 83506b42..de5493f4 100644 --- a/starlight/src/awi_structs/lazy_awi.rs +++ b/starlight/src/awi_structs/lazy_awi.rs @@ -12,7 +12,7 @@ use awint::{ use crate::{ awi, - ensemble::{Evaluator, PNote}, + ensemble::{Ensemble, Evaluator, PNote}, epoch::get_current_epoch, }; @@ -26,11 +26,7 @@ use crate::{ /// When other mimicking types are created from a reference of this, `retro_` /// can later be called to retroactively change the input values of the DAG. pub struct LazyAwi { - // this must remain the same opaque and noted in order for `retro_` to work opaque: dag::Awi, - // needs to be kept in case the `LazyAwi` is optimized away, but we still need bitwidth - // comparisons - nzbw: NonZeroUsize, p_note: PNote, } @@ -46,15 +42,11 @@ impl LazyAwi { } pub fn nzbw(&self) -> NonZeroUsize { - self.nzbw + Ensemble::get_thread_local_note_nzbw(self.p_note).unwrap() } pub fn bw(&self) -> usize { - self.nzbw.get() - } - - pub fn p_note(&self) -> PNote { - self.p_note + self.nzbw().get() } pub fn opaque(w: NonZeroUsize) -> Self { @@ -66,11 +58,7 @@ impl LazyAwi { .ensemble .note_pstate(opaque.state()) .unwrap(); - Self { - opaque, - nzbw: w, - p_note, - } + Self { opaque, p_note } } // TODO it probably does need to be an extra `Awi` in the `Opaque` variant, @@ -107,14 +95,7 @@ impl LazyAwi { /// if this is being called after the corresponding Epoch is dropped and /// states have been pruned. pub fn retro_(&self, rhs: &awi::Bits) -> Result<(), EvalError> { - if self.nzbw != rhs.nzbw() { - // `change_thread_local_state_value` will return without error if it does not - // find the state, but we need to return an error if there is a bitwidth - // mismatch - return Err(EvalError::WrongBitwidth) - } - let p_lhs = self.state(); - Evaluator::change_thread_local_state_value(p_lhs, rhs) + Evaluator::change_thread_local_note_value(self.p_note, rhs) } } @@ -215,14 +196,7 @@ impl LazyInlAwi { /// if this is being called after the corresponding Epoch is dropped and /// states have been pruned. pub fn retro_(&self, rhs: &awi::Bits) -> Result<(), EvalError> { - if BW != rhs.bw() { - // `change_thread_local_state_value` will return without error if it does not - // find the state, but we need to return an error if there is a bitwidth - // mismatch - return Err(EvalError::WrongBitwidth) - } - let p_lhs = self.state(); - Evaluator::change_thread_local_state_value(p_lhs, rhs) + Evaluator::change_thread_local_note_value(self.p_note, rhs) } } diff --git a/starlight/src/ensemble/note.rs b/starlight/src/ensemble/note.rs index 9e7a6bbd..ae7503ba 100644 --- a/starlight/src/ensemble/note.rs +++ b/starlight/src/ensemble/note.rs @@ -1,6 +1,11 @@ -use awint::awint_dag::{triple_arena::ptr_struct, PState}; +use std::num::NonZeroUsize; -use crate::ensemble::{Ensemble, PBack, Referent}; +use awint::awint_dag::{triple_arena::ptr_struct, EvalError, PState}; + +use crate::{ + ensemble::{Ensemble, PBack, Referent}, + epoch::get_current_epoch, +}; ptr_struct!(PNote); @@ -43,6 +48,17 @@ impl Ensemble { } Some(p_note) } + + pub fn get_thread_local_note_nzbw(p_note: PNote) -> Result { + let epoch_shared = get_current_epoch().unwrap(); + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let ensemble = &mut lock.ensemble; + if let Some(note) = ensemble.notes.get(p_note) { + Ok(NonZeroUsize::new(note.bits.len()).unwrap()) + } else { + Err(EvalError::OtherStr("could not find thread local `Note`")) + } + } } impl Default for Note { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index ef3a32fc..8bc31010 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -8,6 +8,7 @@ use awint::{ Awi, }; +use super::PNote; use crate::{ awi, ensemble::{Ensemble, PBack, PTNode, Referent, TNode}, @@ -151,29 +152,29 @@ impl Evaluator { let _ = self.evaluations.insert(eval_step, ()); } - /// This will return no error if `p_state` is not contained - pub fn change_thread_local_state_value( - p_state: PState, + pub fn change_thread_local_note_value( + p_note: PNote, bits: &awi::Bits, ) -> Result<(), EvalError> { let epoch_shared = get_current_epoch().unwrap(); let mut lock = epoch_shared.epoch_data.borrow_mut(); let ensemble = &mut lock.ensemble; - if let Some(state) = ensemble.stator.states.get(p_state) { - if state.nzbw != bits.nzbw() { + if let Some(note) = ensemble.notes.get(p_note) { + if note.bits.len() != bits.bw() { return Err(EvalError::WrongBitwidth); } - // switch to change phase - if ensemble.evaluator.phase != EvalPhase::Change { - ensemble.evaluator.phase = EvalPhase::Change; - ensemble.evaluator.next_change_visit_gen(); - } - ensemble.initialize_state_bits_if_needed(p_state).unwrap(); - for bit_i in 0..bits.bw() { - let p_bit = ensemble.stator.states.get(p_state).unwrap().p_self_bits[bit_i]; - if let Some(p_bit) = p_bit { - let _ = ensemble.change_value(p_bit, Value::Dynam(bits.get(bit_i).unwrap())); + } else { + return Err(EvalError::OtherStr("could not find thread local `Note`")) + } + for bit_i in 0..bits.bw() { + let p_back = ensemble.notes[p_note].bits[bit_i]; + if let Some(p_back) = p_back { + // switch to change phase + if ensemble.evaluator.phase != EvalPhase::Change { + ensemble.evaluator.phase = EvalPhase::Change; + ensemble.evaluator.next_change_visit_gen(); } + let _ = ensemble.change_value(p_back, Value::Dynam(bits.get(bit_i).unwrap())); } } Ok(()) From 3bf92484221f9a2a7e67905699340a056794a686 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 17:42:17 -0600 Subject: [PATCH 31/62] move around --- starlight/src/awi_structs/lazy_awi.rs | 6 +++--- starlight/src/ensemble/note.rs | 28 ++++++++++++++++++++++++- starlight/src/ensemble/value.rs | 30 --------------------------- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/starlight/src/awi_structs/lazy_awi.rs b/starlight/src/awi_structs/lazy_awi.rs index de5493f4..bc78b315 100644 --- a/starlight/src/awi_structs/lazy_awi.rs +++ b/starlight/src/awi_structs/lazy_awi.rs @@ -12,7 +12,7 @@ use awint::{ use crate::{ awi, - ensemble::{Ensemble, Evaluator, PNote}, + ensemble::{Ensemble, PNote}, epoch::get_current_epoch, }; @@ -95,7 +95,7 @@ impl LazyAwi { /// if this is being called after the corresponding Epoch is dropped and /// states have been pruned. pub fn retro_(&self, rhs: &awi::Bits) -> Result<(), EvalError> { - Evaluator::change_thread_local_note_value(self.p_note, rhs) + Ensemble::change_thread_local_note_value(self.p_note, rhs) } } @@ -196,7 +196,7 @@ impl LazyInlAwi { /// if this is being called after the corresponding Epoch is dropped and /// states have been pruned. pub fn retro_(&self, rhs: &awi::Bits) -> Result<(), EvalError> { - Evaluator::change_thread_local_note_value(self.p_note, rhs) + Ensemble::change_thread_local_note_value(self.p_note, rhs) } } diff --git a/starlight/src/ensemble/note.rs b/starlight/src/ensemble/note.rs index ae7503ba..89aa83f6 100644 --- a/starlight/src/ensemble/note.rs +++ b/starlight/src/ensemble/note.rs @@ -3,7 +3,8 @@ use std::num::NonZeroUsize; use awint::awint_dag::{triple_arena::ptr_struct, EvalError, PState}; use crate::{ - ensemble::{Ensemble, PBack, Referent}, + awi, + ensemble::{Ensemble, PBack, Referent, Value}, epoch::get_current_epoch, }; @@ -59,6 +60,31 @@ impl Ensemble { Err(EvalError::OtherStr("could not find thread local `Note`")) } } + + pub fn change_thread_local_note_value( + p_note: PNote, + bits: &awi::Bits, + ) -> Result<(), EvalError> { + let epoch_shared = get_current_epoch().unwrap(); + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let ensemble = &mut lock.ensemble; + if let Some(note) = ensemble.notes.get(p_note) { + if note.bits.len() != bits.bw() { + return Err(EvalError::WrongBitwidth); + } + } else { + return Err(EvalError::OtherStr("could not find thread local `Note`")) + } + for bit_i in 0..bits.bw() { + let p_back = ensemble.notes[p_note].bits[bit_i]; + if let Some(p_back) = p_back { + ensemble + .change_value(p_back, Value::Dynam(bits.get(bit_i).unwrap())) + .unwrap(); + } + } + Ok(()) + } } impl Default for Note { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 8bc31010..9b774964 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -8,9 +8,7 @@ use awint::{ Awi, }; -use super::PNote; use crate::{ - awi, ensemble::{Ensemble, PBack, PTNode, Referent, TNode}, epoch::{get_current_epoch, EpochShared}, }; @@ -152,34 +150,6 @@ impl Evaluator { let _ = self.evaluations.insert(eval_step, ()); } - pub fn change_thread_local_note_value( - p_note: PNote, - bits: &awi::Bits, - ) -> Result<(), EvalError> { - let epoch_shared = get_current_epoch().unwrap(); - let mut lock = epoch_shared.epoch_data.borrow_mut(); - let ensemble = &mut lock.ensemble; - if let Some(note) = ensemble.notes.get(p_note) { - if note.bits.len() != bits.bw() { - return Err(EvalError::WrongBitwidth); - } - } else { - return Err(EvalError::OtherStr("could not find thread local `Note`")) - } - for bit_i in 0..bits.bw() { - let p_back = ensemble.notes[p_note].bits[bit_i]; - if let Some(p_back) = p_back { - // switch to change phase - if ensemble.evaluator.phase != EvalPhase::Change { - ensemble.evaluator.phase = EvalPhase::Change; - ensemble.evaluator.next_change_visit_gen(); - } - let _ = ensemble.change_value(p_back, Value::Dynam(bits.get(bit_i).unwrap())); - } - } - Ok(()) - } - // stepping loops should request their drivers, evaluating everything requests // everything pub fn calculate_thread_local_state_value( From a914786e075fb62e7453499679b848cdc9175f1d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 18:00:09 -0600 Subject: [PATCH 32/62] fix `EvalAwi` --- starlight/src/awi_structs/eval_awi.rs | 11 ++- starlight/src/ensemble/note.rs | 25 +++++++ starlight/src/ensemble/value.rs | 99 ++++++++++++--------------- 3 files changed, 72 insertions(+), 63 deletions(-) diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index c8015f7e..4a44a1cb 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -7,7 +7,7 @@ use awint::{ use crate::{ awi, - ensemble::{Evaluator, PNote, Value}, + ensemble::{Ensemble, PNote, Value}, epoch::get_current_epoch, }; @@ -79,10 +79,9 @@ impl EvalAwi { pub fn eval(&self) -> Result { let nzbw = self.nzbw(); - let p_self = self.state(); let mut res = awi::Awi::zero(nzbw); for bit_i in 0..res.bw() { - let val = Evaluator::calculate_thread_local_state_value(p_self, bit_i)?; + let val = Ensemble::calculate_thread_local_note_value(self.p_note, bit_i)?; if let Some(val) = val.known_value() { res.set(bit_i, val).unwrap(); } else { @@ -93,7 +92,7 @@ impl EvalAwi { .epoch_data .borrow() .ensemble - .get_state_debug(p_self) + .get_state_debug(self.p_state) .unwrap() ))) } @@ -103,9 +102,7 @@ impl EvalAwi { /// Assumes `self` is a single bit pub(crate) fn eval_bit(&self) -> Result { - let p_self = self.state(); - assert_eq!(self.bw(), 1); - Evaluator::calculate_thread_local_state_value(p_self, 0) + Ensemble::calculate_thread_local_note_value(self.p_note, 0) } pub fn zero(w: NonZeroUsize) -> Self { diff --git a/starlight/src/ensemble/note.rs b/starlight/src/ensemble/note.rs index 89aa83f6..fe790c43 100644 --- a/starlight/src/ensemble/note.rs +++ b/starlight/src/ensemble/note.rs @@ -85,6 +85,31 @@ impl Ensemble { } Ok(()) } + + pub fn calculate_thread_local_note_value( + p_note: PNote, + bit_i: usize, + ) -> Result { + let epoch_shared = get_current_epoch().unwrap(); + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let ensemble = &mut lock.ensemble; + let p_back = if let Some(note) = ensemble.notes.get(p_note) { + if bit_i >= note.bits.len() { + return Err(EvalError::WrongBitwidth); + } + if let Some(p_back) = note.bits[bit_i] { + p_back + } else { + return Err(EvalError::OtherStr( + "something went wrong, found `Note` for evaluator but a bit was denoted", + )) + } + } else { + return Err(EvalError::OtherStr("could not find thread local `Note`")) + }; + drop(lock); + Ensemble::calculate_value(&epoch_shared, p_back) + } } impl Default for Note { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 9b774964..590bf0a4 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -3,14 +3,14 @@ use std::num::{NonZeroU64, NonZeroUsize}; use awint::{ awint_dag::{ triple_arena::{ptr_struct, Advancer, OrdArena}, - EvalError, PState, + EvalError, }, Awi, }; use crate::{ ensemble::{Ensemble, PBack, PTNode, Referent, TNode}, - epoch::{get_current_epoch, EpochShared}, + epoch::EpochShared, }; #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -149,55 +149,6 @@ impl Evaluator { pub fn insert(&mut self, eval_step: Eval) { let _ = self.evaluations.insert(eval_step, ()); } - - // stepping loops should request their drivers, evaluating everything requests - // everything - pub fn calculate_thread_local_state_value( - p_state: PState, - bit_i: usize, - ) -> Result { - let epoch_shared = get_current_epoch().unwrap(); - let mut lock = epoch_shared.epoch_data.borrow_mut(); - let ensemble = &mut lock.ensemble; - ensemble.initialize_state_bits_if_needed(p_state).unwrap(); - let state = ensemble.stator.states.get(p_state).unwrap(); - let p_back = *state.p_self_bits.get(bit_i).unwrap(); - let p_back = if let Some(p) = p_back { - p - } else { - return Err(EvalError::OtherString(format!( - "state {p_state} bit {bit_i} has been removed, something was not noted correctly" - ))); - }; - if let Some(equiv) = ensemble.backrefs.get_val_mut(p_back) { - // switch to request phase - if ensemble.evaluator.phase != EvalPhase::Request { - ensemble.evaluator.phase = EvalPhase::Request; - ensemble.evaluator.next_request_visit_gen(); - } - let visit = ensemble.evaluator.request_visit_gen(); - if equiv.request_visit != visit { - equiv.request_visit = visit; - ensemble - .evaluator - .insert(Eval::Investigate0(0, equiv.p_self_equiv)); - drop(lock); - Ensemble::handle_requests(&epoch_shared)?; - } else { - drop(lock); - } - Ok(epoch_shared - .epoch_data - .borrow() - .ensemble - .backrefs - .get_val(p_back) - .unwrap() - .val) - } else { - Err(EvalError::InvalidPtr) - } - } } impl Ensemble { @@ -322,17 +273,17 @@ impl Ensemble { res } - /// Returns `None` only if `p_back` does not exist or was removed pub fn change_value(&mut self, p_back: PBack, value: Value) -> Option<()> { if let Some(equiv) = self.backrefs.get_val_mut(p_back) { - if self.evaluator.phase != EvalPhase::Change { - self.evaluator.phase = EvalPhase::Change; - self.evaluator.next_change_visit_gen(); - } if equiv.val.is_const() { // not allowed panic!(); } + // switch to change phase if not already + if self.evaluator.phase != EvalPhase::Change { + self.evaluator.phase = EvalPhase::Change; + self.evaluator.next_change_visit_gen(); + } equiv.val = value; Some(()) } else { @@ -340,6 +291,42 @@ impl Ensemble { } } + pub fn calculate_value(epoch_shared: &EpochShared, p_back: PBack) -> Result { + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let ensemble = &mut lock.ensemble; + if let Some(equiv) = ensemble.backrefs.get_val_mut(p_back) { + if equiv.val.is_const() { + return Ok(equiv.val) + } + // switch to request phase if not already + if ensemble.evaluator.phase != EvalPhase::Request { + ensemble.evaluator.phase = EvalPhase::Request; + ensemble.evaluator.next_request_visit_gen(); + } + let visit = ensemble.evaluator.request_visit_gen(); + if equiv.request_visit != visit { + equiv.request_visit = visit; + ensemble + .evaluator + .insert(Eval::Investigate0(0, equiv.p_self_equiv)); + drop(lock); + Ensemble::handle_requests(epoch_shared)?; + } else { + drop(lock); + } + Ok(epoch_shared + .epoch_data + .borrow() + .ensemble + .backrefs + .get_val(p_back) + .unwrap() + .val) + } else { + Err(EvalError::InvalidPtr) + } + } + fn handle_requests(epoch_shared: &EpochShared) -> Result<(), EvalError> { // TODO currently, the only way of avoiding N^2 worst case scenarios where // different change cascades lead to large groups of nodes being evaluated From 875d8cc03b287176e7039f706901bf176643dd30 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 18:11:21 -0600 Subject: [PATCH 33/62] fix bug introduced in optimizations --- starlight/src/lower/meta.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/starlight/src/lower/meta.rs b/starlight/src/lower/meta.rs index cebec188..211e90b2 100644 --- a/starlight/src/lower/meta.rs +++ b/starlight/src/lower/meta.rs @@ -304,10 +304,9 @@ pub fn resize_cond(x: &Bits, w: NonZeroUsize, signed: &Bits) -> Awi { Op::ConcatFields(ConcatFieldsType::from_iter([(x.state(), 0usize, w)])), ) } else { - let extend = x.msb() & signed.to_bool(); let extension = Awi::new( NonZeroUsize::new(w.get() - x.bw()).unwrap(), - Op::Repeat([extend.state()]), + Op::Repeat([signed.state()]), ); Awi::new( w, From acd6a38d25af015010d2306e80b9c950aedcf78f Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 20:05:29 -0600 Subject: [PATCH 34/62] Update fuzz_lower.rs --- testcrate/tests/fuzz_lower.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testcrate/tests/fuzz_lower.rs b/testcrate/tests/fuzz_lower.rs index 0bd49f82..c5f62237 100644 --- a/testcrate/tests/fuzz_lower.rs +++ b/testcrate/tests/fuzz_lower.rs @@ -148,10 +148,11 @@ impl Mem { &mut self.a[inx].dag } - pub fn finish(&mut self, _epoch: &Epoch) { + pub fn finish(&mut self, epoch: &Epoch) { for pair in self.a.vals_mut() { pair.eval = Some(EvalAwi::from(&pair.dag)) } + epoch.prune().unwrap(); } pub fn eval_and_verify_equal(&mut self, epoch: &Epoch) -> Result<(), EvalError> { From c1f23cdfe44b7034b5b9db72b04ec6ebd2812366 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 20:18:13 -0600 Subject: [PATCH 35/62] renames to `allow_pruning` --- starlight/src/awi_structs/epoch.rs | 13 +++++++------ starlight/src/ensemble/debug.rs | 2 +- starlight/src/ensemble/optimize.rs | 4 ++-- starlight/src/ensemble/state.rs | 28 +++++++--------------------- starlight/src/ensemble/together.rs | 14 ++++++++------ starlight/src/lower/lower_state.rs | 8 ++++---- 6 files changed, 29 insertions(+), 40 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 3739ccb5..354abbbd 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -58,7 +58,8 @@ pub struct EpochData { pub epoch_key: EpochKey, pub ensemble: Ensemble, pub responsible_for: Arena, - pub keep_flag: bool, + /// If set, states made will have `allow_pruning` set + pub allow_pruning: bool, } impl Drop for EpochData { @@ -85,7 +86,7 @@ impl EpochShared { epoch_key: _callback().push_on_epoch_stack(), ensemble: Ensemble::new(), responsible_for: Arena::new(), - keep_flag: true, + allow_pruning: false, }; let p_self = epoch_data.responsible_for.insert(PerEpochShared::new()); Self { @@ -257,10 +258,10 @@ pub fn _callback() -> EpochCallback { fn new_pstate(nzbw: NonZeroUsize, op: Op, location: Option) -> PState { no_recursive_current_epoch_mut(|current| { let mut epoch_data = current.epoch_data.borrow_mut(); - let keep = epoch_data.keep_flag; + let allow_pruning = epoch_data.allow_pruning; let p_state = epoch_data .ensemble - .make_state(nzbw, op.clone(), location, keep); + .make_state(nzbw, op.clone(), location, allow_pruning); epoch_data .responsible_for .get_mut(current.p_self) @@ -460,14 +461,14 @@ impl Epoch { self.shared.ensemble() } - /// Removes all non-noted states + /// For users, this removes all states that do not lead to a live `EvalAwi` pub fn prune(&self) -> Result<(), EvalError> { let epoch_shared = get_current_epoch().unwrap(); if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { return Err(EvalError::OtherStr("epoch is not the current epoch")) } let mut lock = epoch_shared.epoch_data.borrow_mut(); - lock.ensemble.prune_unnoted_states() + lock.ensemble.prune_states() } /// Lowers all states. This is not needed in most circumstances, `EvalAwi` diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index 46780d4f..d1f116f3 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -48,7 +48,7 @@ impl DebugNodeTrait for State { v.push(format!( "{} {} {} {}", this.rc, - short(this.keep), + short(this.allow_pruning), short(this.lowered_to_elementary), short(this.lowered_to_tnodes) )); diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index 90b37bb2..c69da914 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -215,7 +215,7 @@ impl Ensemble { } Referent::ThisStateBit(p_state, _) => { let state = &self.stator.states[p_state]; - if state.keep { + if !state.allow_pruning { non_self_rc += 1; } } @@ -465,7 +465,7 @@ impl Ensemble { Referent::ThisTNode(_) => (), Referent::ThisStateBit(p_state, _) => { let state = &self.stator.states[p_state]; - if state.keep { + if !state.allow_pruning { found_use = true; } } diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 754fd816..b4ee40b4 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -12,7 +12,7 @@ use crate::{ awi, ensemble::{ value::{Change, Eval}, - Ensemble, PBack, Referent, Value, + Ensemble, PBack, Value, }, epoch::EpochShared, }; @@ -35,7 +35,8 @@ pub struct State { /// The number of other `State`s, and only other `State`s, that reference /// this one through the `Op`s pub rc: usize, - pub keep: bool, + /// Allows this state to be pruned + pub allow_pruning: bool, /// If the `State` has been lowered to elementary `State`s (`Static-` /// operations and roots). Note that a DFS might set this before actually /// being lowered. @@ -75,7 +76,7 @@ impl Ensemble { } else { return Err(EvalError::OtherStr("tried to subtract a 0 reference count")) }; - if (state.rc == 0) && (!state.keep) { + if (state.rc == 0) && state.allow_pruning { self.remove_state(p_state)?; } Ok(()) @@ -84,27 +85,12 @@ impl Ensemble { } } - // TODO need to slightly rethink the PState/PNode system. - // For now, we just prune states if any of their bits shares a surject with a - // note. - pub fn prune_unnoted_states(&mut self) -> Result<(), EvalError> { + /// Prunes all states that `allow_pruning` + pub fn prune_states(&mut self) -> Result<(), EvalError> { let mut adv = self.stator.states.advancer(); while let Some(p_state) = adv.advance(&self.stator.states) { let state = &self.stator.states[p_state]; - let mut remove = true; - 'outer: for p_bit in &state.p_self_bits { - if let Some(p_bit) = p_bit { - let mut equiv_adv = self.backrefs.advancer_surject(*p_bit); - while let Some(p_back) = equiv_adv.advance(&self.backrefs) { - if let Referent::Note(_) = self.backrefs.get_key(p_back).unwrap() { - remove = false; - break 'outer - } - } - } - } - if remove { - self.stator.states.get_mut(p_state).unwrap().keep = false; + if state.allow_pruning { self.remove_state(p_state).unwrap(); } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index f8293908..de168708 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -329,7 +329,7 @@ impl Ensemble { nzbw: NonZeroUsize, op: Op, location: Option, - keep: bool, + allow_pruning: bool, ) -> PState { for operand in op.operands() { let state = self.stator.states.get_mut(*operand).unwrap(); @@ -342,7 +342,7 @@ impl Ensemble { location, err: None, rc: 0, - keep, + allow_pruning, lowered_to_elementary: false, lowered_to_tnodes: false, }) @@ -510,15 +510,17 @@ impl Ensemble { Ok(()) } - /// Removes the state (it does not necessarily need to still be contained) - /// and removes its source tree of states with resulting zero reference - /// count and `!state.keep` + /// Triggers a cascade of state removals if the states `allow_pruning` and + /// their reference counts are zero pub fn remove_state(&mut self, p_state: PState) -> Result<(), EvalError> { + if !self.stator.states.contains(p_state) { + return Err(EvalError::InvalidPtr); + } let mut pstate_stack = vec![p_state]; while let Some(p) = pstate_stack.pop() { let mut delete = false; if let Some(state) = self.stator.states.get(p) { - if (state.rc == 0) && !state.keep { + if (state.rc == 0) && state.allow_pruning { delete = true; } } diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index b3434d3c..efe659e9 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -184,7 +184,7 @@ impl Ensemble { lock.ensemble.stator.states[p_state].lowered_to_elementary = true; // NOTE be sure to reset this before returning from the function - lock.keep_flag = false; + lock.allow_pruning = true; drop(lock); let mut path: Vec<(usize, PState)> = vec![(0, p_state)]; loop { @@ -332,7 +332,7 @@ impl Ensemble { temporary.remove_as_current(); let mut lock = epoch_shared.epoch_data.borrow_mut(); lock.ensemble.stator.states[p_state].err = Some(e.clone()); - lock.keep_flag = true; + lock.allow_pruning = false; return Err(e) } }; @@ -345,7 +345,7 @@ impl Ensemble { let mut lock = epoch_shared.epoch_data.borrow_mut(); for p_state in states { let state = &lock.ensemble.stator.states[p_state]; - if (!state.keep) && (state.rc == 0) { + if state.allow_pruning && (state.rc == 0) { lock.ensemble.remove_state(p_state).unwrap(); } } @@ -384,7 +384,7 @@ impl Ensemble { } let mut lock = epoch_shared.epoch_data.borrow_mut(); - lock.keep_flag = true; + lock.allow_pruning = false; if unimplemented { Err(EvalError::Unimplemented) From d36432e28a3af2fe27060edc4e9b013733c13979 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 20:36:45 -0600 Subject: [PATCH 36/62] more cleanup --- starlight/src/awi_structs/epoch.rs | 40 ++++++++++++++------------- starlight/src/awi_structs/eval_awi.rs | 4 +-- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 354abbbd..b3437b41 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -154,10 +154,10 @@ impl EpochShared { pub fn remove_associated(&self) { let mut epoch_data = self.epoch_data.borrow_mut(); let mut ours = epoch_data.responsible_for.remove(self.p_self).unwrap(); + ours.assertions.bits.clear(); for p_state in ours.states_inserted { let _ = epoch_data.ensemble.remove_state(p_state); } - ours.assertions.bits.clear(); } pub fn set_as_current(&self) { @@ -274,21 +274,22 @@ pub fn _callback() -> EpochCallback { fn register_assertion_bit(bit: dag::bool, location: Location) { // need a new bit to attach new location data to let new_bit = new_pstate(bw(1), Op::Assert([bit.state()]), Some(location)); - no_recursive_current_epoch_mut(|current| { - let mut epoch_data = current.epoch_data.borrow_mut(); - // need to manually construct to get around closure issues - let p_note = epoch_data.ensemble.note_pstate(new_bit).unwrap(); - let eval_awi = EvalAwi { - p_state: new_bit, - p_note, - }; - epoch_data - .responsible_for - .get_mut(current.p_self) - .unwrap() - .assertions - .bits - .push(eval_awi); + let eval_awi = EvalAwi::from_state(new_bit).unwrap(); + // manual to get around closure issue + CURRENT_EPOCH.with(|top| { + let mut top = top.borrow_mut(); + if let Some(current) = top.as_mut() { + let mut epoch_data = current.epoch_data.borrow_mut(); + epoch_data + .responsible_for + .get_mut(current.p_self) + .unwrap() + .assertions + .bits + .push(eval_awi); + } else { + panic!("There needs to be an `Epoch` in scope for this to work"); + } }) } fn get_nzbw(p_state: PState) -> NonZeroUsize { @@ -381,8 +382,9 @@ impl Epoch { } /// Intended primarily for developer use - pub fn internal_epoch_shared(&self) -> &EpochShared { - &self.shared + #[doc(hidden)] + pub fn internal_epoch_shared(this: &Epoch) -> &EpochShared { + &this.shared } /// Gets the assertions associated with this Epoch (not including assertions @@ -439,7 +441,7 @@ impl Epoch { } else if unknown.is_none() { // get the earliest failure to evaluate wait for all bits to be checked for // falsity - unknown = Some(eval_awi.p_state); + unknown = Some(eval_awi.state()); } } if let Some(p_state) = unknown { diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 4a44a1cb..7f4c2744 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -20,8 +20,8 @@ use crate::{ /// /// TODO pub struct EvalAwi { - pub(crate) p_state: PState, - pub(crate) p_note: PNote, + p_state: PState, + p_note: PNote, } // TODO impl drop to remove note From 9a79cadfca3ad83f3ef2203ae0826eee124c93b0 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 20:56:45 -0600 Subject: [PATCH 37/62] move asserting assertions downwards --- starlight/src/awi_structs/epoch.rs | 69 ++++++++++++++++++++------- starlight/src/awi_structs/eval_awi.rs | 10 ++++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index b3437b41..ef32d230 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -133,6 +133,56 @@ impl EpochShared { Assertions { bits: cloned } } + /// Using `EpochShared::assertions` creates all new `Assertions`, this can + /// eliminate them entirely + pub fn assert_assertions(&self) -> Result<(), EvalError> { + let p_self = self.p_self; + let epoch_data = self.epoch_data.borrow(); + let len = epoch_data + .responsible_for + .get(p_self) + .unwrap() + .assertions + .bits + .len(); + drop(epoch_data); + let mut i = 0; + loop { + if i >= len { + break + } + let epoch_data = self.epoch_data.borrow(); + let eval_awi = &epoch_data + .responsible_for + .get(p_self) + .unwrap() + .assertions + .bits[i]; + let p_state = eval_awi.state(); + let p_note = eval_awi.p_note(); + drop(epoch_data); + let val = Ensemble::calculate_thread_local_note_value(p_note, 0)?; + if let Some(val) = val.known_value() { + if !val { + let epoch_data = self.epoch_data.borrow(); + let s = epoch_data.ensemble.get_state_debug(p_state); + if let Some(s) = s { + return Err(EvalError::OtherString(format!( + "an assertion bit evaluated to false, failed on {p_note} {:?}", + s + ))) + } else { + return Err(EvalError::OtherString(format!( + "an assertion bit evaluated to false, failed on {p_note} {p_state}" + ))) + } + } + } + i += 1; + } + Ok(()) + } + /// Returns a clone of the ensemble pub fn ensemble(&self) -> Ensemble { self.epoch_data.borrow().ensemble.clone() @@ -398,24 +448,7 @@ impl Epoch { /// If any assertion bit evaluates to false, this returns an error. pub fn assert_assertions(&self) -> Result<(), EvalError> { - let bits = self.shared.assertions().bits; - for eval_awi in bits { - let val = eval_awi.eval_bit()?; - if let Some(val) = val.known_value() { - if !val { - return Err(EvalError::OtherString(format!( - "an assertion bit evaluated to false, failed on {}", - self.shared - .epoch_data - .borrow() - .ensemble - .get_state_debug(eval_awi.state()) - .unwrap() - ))) - } - } - } - Ok(()) + self.shared.assert_assertions() } /// If any assertion bit evaluates to false, this returns an error. If there diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 7f4c2744..dcd85379 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -128,6 +128,16 @@ impl EvalAwi { impl fmt::Debug for EvalAwi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(epoch) = get_current_epoch() { + if let Some(s) = epoch + .epoch_data + .borrow() + .ensemble + .get_state_debug(self.state()) + { + return write!(f, "EvalAwi({s})"); + } + } write!(f, "EvalAwi({:?})", self.state()) } } From ee3e463e8902fb4d5027fb9387e057bebfad2bc4 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Thu, 7 Dec 2023 21:08:06 -0600 Subject: [PATCH 38/62] fix the assertion situation more --- starlight/src/awi_structs/epoch.rs | 84 ++++++++++++++------------- starlight/src/awi_structs/eval_awi.rs | 7 +-- testcrate/tests/fuzz_elementary.rs | 3 +- testcrate/tests/fuzz_lower.rs | 2 + 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index ef32d230..6e603865 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -133,12 +133,12 @@ impl EpochShared { Assertions { bits: cloned } } - /// Using `EpochShared::assertions` creates all new `Assertions`, this can - /// eliminate them entirely - pub fn assert_assertions(&self) -> Result<(), EvalError> { + /// Using `EpochShared::assertions` creates all new `Assertions`. This + /// eliminates assertions that evaluate to a constant true. + pub fn assert_assertions(&self, strict: bool) -> Result<(), EvalError> { let p_self = self.p_self; let epoch_data = self.epoch_data.borrow(); - let len = epoch_data + let mut len = epoch_data .responsible_for .get(p_self) .unwrap() @@ -146,6 +146,7 @@ impl EpochShared { .bits .len(); drop(epoch_data); + let mut unknown = None; let mut i = 0; loop { if i >= len { @@ -177,8 +178,43 @@ impl EpochShared { ))) } } + } else if unknown.is_none() { + // get the earliest failure to evaluate, should be closest to the root cause. + // Wait for all bits to be checked for falsity + unknown = Some((p_note, p_state)); + } + if val.is_const() { + // remove the assertion + let mut epoch_data = self.epoch_data.borrow_mut(); + epoch_data + .responsible_for + .get_mut(p_self) + .unwrap() + .assertions + .bits + .swap_remove(i); + len -= 1; + } else { + i += 1; + } + } + if strict { + if let Some((p_note, p_state)) = unknown { + let epoch_data = self.epoch_data.borrow(); + let s = epoch_data.ensemble.get_state_debug(p_state); + if let Some(s) = s { + return Err(EvalError::OtherString(format!( + "an assertion bit could not be evaluated to a known value, failed on \ + {p_note} {:?}", + s + ))) + } else { + return Err(EvalError::OtherString(format!( + "an assertion bit could not be evaluated to a known value, failed on \ + {p_note} {p_state}" + ))) + } } - i += 1; } Ok(()) } @@ -448,48 +484,14 @@ impl Epoch { /// If any assertion bit evaluates to false, this returns an error. pub fn assert_assertions(&self) -> Result<(), EvalError> { - self.shared.assert_assertions() + self.shared.assert_assertions(false) } /// If any assertion bit evaluates to false, this returns an error. If there /// were no known false assertions but some are `Value::Unknown`, this /// returns a specific error for it. pub fn assert_assertions_strict(&self) -> Result<(), EvalError> { - let bits = self.shared.assertions().bits; - let mut unknown = None; - for eval_awi in bits { - let val = eval_awi.eval_bit()?; - if let Some(val) = val.known_value() { - if !val { - return Err(EvalError::OtherString(format!( - "assertion bits are not all true, failed on {}", - self.shared - .epoch_data - .borrow() - .ensemble - .get_state_debug(eval_awi.state()) - .unwrap() - ))) - } - } else if unknown.is_none() { - // get the earliest failure to evaluate wait for all bits to be checked for - // falsity - unknown = Some(eval_awi.state()); - } - } - if let Some(p_state) = unknown { - Err(EvalError::OtherString(format!( - "an assertion bit could not be evaluated to a known value, failed on {}", - self.shared - .epoch_data - .borrow() - .ensemble - .get_state_debug(p_state) - .unwrap() - ))) - } else { - Ok(()) - } + self.shared.assert_assertions(true) } pub fn ensemble(&self) -> Ensemble { diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index dcd85379..1a883f6d 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -7,7 +7,7 @@ use awint::{ use crate::{ awi, - ensemble::{Ensemble, PNote, Value}, + ensemble::{Ensemble, PNote}, epoch::get_current_epoch, }; @@ -100,11 +100,6 @@ impl EvalAwi { Ok(res) } - /// Assumes `self` is a single bit - pub(crate) fn eval_bit(&self) -> Result { - Ensemble::calculate_thread_local_note_value(self.p_note, 0) - } - pub fn zero(w: NonZeroUsize) -> Self { Self::from_bits(&dag::Awi::zero(w)).unwrap() } diff --git a/testcrate/tests/fuzz_elementary.rs b/testcrate/tests/fuzz_elementary.rs index 8b53e519..2d7571e1 100644 --- a/testcrate/tests/fuzz_elementary.rs +++ b/testcrate/tests/fuzz_elementary.rs @@ -103,13 +103,14 @@ impl Mem { epoch.prune().unwrap(); } - pub fn verify_equivalence(&mut self, _epoch: &Epoch) -> Result<(), EvalError> { + pub fn verify_equivalence(&mut self, epoch: &Epoch) -> Result<(), EvalError> { // set all lazy roots for (lazy, lit) in &mut self.roots { lazy.retro_(lit).unwrap(); } // evaluate all + epoch.assert_assertions().unwrap(); for pair in self.a.vals() { assert_eq!(pair.eval.as_ref().unwrap().eval().unwrap(), pair.awi); } diff --git a/testcrate/tests/fuzz_lower.rs b/testcrate/tests/fuzz_lower.rs index c5f62237..27b1ff8f 100644 --- a/testcrate/tests/fuzz_lower.rs +++ b/testcrate/tests/fuzz_lower.rs @@ -166,6 +166,7 @@ impl Mem { // lower epoch.lower().unwrap(); + epoch.assert_assertions().unwrap(); // set remaining lazy roots for (lazy, lit) in self.roots.drain(..) { @@ -173,6 +174,7 @@ impl Mem { } // evaluate all + epoch.assert_assertions_strict().unwrap(); for pair in self.a.vals() { assert_eq!(pair.eval.as_ref().unwrap().eval().unwrap(), pair.awi); } From d5dd23151d4715ee727688d9edadc015708791ce Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 00:52:49 -0600 Subject: [PATCH 39/62] impl the drop for `EvalAwi` --- starlight/src/awi_structs/epoch.rs | 20 +++++++++++++------- starlight/src/awi_structs/eval_awi.rs | 22 +++++++++++++++++++--- starlight/src/ensemble/note.rs | 14 ++++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 6e603865..c3d4cdc1 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -108,6 +108,7 @@ impl EpochShared { } } + /* /// Returns a clone of the assertions currently associated with `self` pub fn assertions(&self) -> Assertions { let p_self = self.p_self; @@ -131,7 +132,7 @@ impl EpochShared { } } Assertions { bits: cloned } - } + }*/ /// Using `EpochShared::assertions` creates all new `Assertions`. This /// eliminates assertions that evaluate to a constant true. @@ -186,13 +187,15 @@ impl EpochShared { if val.is_const() { // remove the assertion let mut epoch_data = self.epoch_data.borrow_mut(); - epoch_data + let eval_awi = epoch_data .responsible_for .get_mut(p_self) .unwrap() .assertions .bits .swap_remove(i); + drop(epoch_data); + drop(eval_awi); len -= 1; } else { i += 1; @@ -239,11 +242,13 @@ impl EpochShared { /// Removes associated states and assertions pub fn remove_associated(&self) { let mut epoch_data = self.epoch_data.borrow_mut(); - let mut ours = epoch_data.responsible_for.remove(self.p_self).unwrap(); - ours.assertions.bits.clear(); - for p_state in ours.states_inserted { - let _ = epoch_data.ensemble.remove_state(p_state); + let ours = epoch_data.responsible_for.remove(self.p_self).unwrap(); + for p_state in &ours.states_inserted { + let _ = epoch_data.ensemble.remove_state(*p_state); } + drop(epoch_data); + // drop the `EvalAwi`s of the assertions after unlocking + drop(ours); } pub fn set_as_current(&self) { @@ -473,12 +478,13 @@ impl Epoch { &this.shared } + /* /// Gets the assertions associated with this Epoch (not including assertions /// from when sub-epochs are alive or from before the this Epoch was /// created) pub fn assertions(&self) -> Assertions { self.shared.assertions() - } + }*/ // TODO fix the EvalError enum situation diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 1a883f6d..38b5150d 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -1,4 +1,4 @@ -use std::{fmt, num::NonZeroUsize}; +use std::{fmt, num::NonZeroUsize, thread::panicking}; use awint::{ awint_dag::{dag, epoch, EvalError, Lineage, PState}, @@ -18,13 +18,29 @@ use crate::{ /// /// # Custom Drop /// -/// TODO +/// Upon being dropped, this will remove special references being kept by the +/// current `Epoch` pub struct EvalAwi { p_state: PState, p_note: PNote, } -// TODO impl drop to remove note +impl Drop for EvalAwi { + fn drop(&mut self) { + // prevent invoking recursive panics and a buffer overrun + if !panicking() { + if let Some(epoch) = get_current_epoch() { + let res = epoch + .epoch_data + .borrow_mut() + .ensemble + .remove_note(self.p_note); + // TODO + //res.unwrap(); + } + } + } +} impl Lineage for EvalAwi { fn state(&self) -> PState { diff --git a/starlight/src/ensemble/note.rs b/starlight/src/ensemble/note.rs index fe790c43..519b0978 100644 --- a/starlight/src/ensemble/note.rs +++ b/starlight/src/ensemble/note.rs @@ -50,6 +50,20 @@ impl Ensemble { Some(p_note) } + pub fn remove_note(&mut self, p_note: PNote) -> Result<(), EvalError> { + if let Some(note) = self.notes.remove(p_note) { + for p_back in note.bits { + if let Some(p_back) = p_back { + let referent = self.backrefs.remove_key(p_back).unwrap().0; + assert!(matches!(referent, Referent::Note(_))); + } + } + Ok(()) + } else { + Err(EvalError::InvalidPtr) + } + } + pub fn get_thread_local_note_nzbw(p_note: PNote) -> Result { let epoch_shared = get_current_epoch().unwrap(); let mut lock = epoch_shared.epoch_data.borrow_mut(); From 87b60512cb9ac0f83139ddb70402778f3a2c5458 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 01:09:39 -0600 Subject: [PATCH 40/62] impl the dropping --- starlight/src/awi_structs/eval_awi.rs | 8 ++++++-- starlight/src/awi_structs/lazy_awi.rs | 29 +++++++++++++++++++++++---- testcrate/benches/bench.rs | 17 ++++++++-------- testcrate/tests/fuzz_lower.rs | 2 +- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 38b5150d..042ef65c 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -35,9 +35,13 @@ impl Drop for EvalAwi { .borrow_mut() .ensemble .remove_note(self.p_note); - // TODO - //res.unwrap(); + if res.is_err() { + panic!( + "most likely, an `EvalAwi` created in one `Epoch` was dropped in another" + ) + } } + // else the epoch has been dropped } } } diff --git a/starlight/src/awi_structs/lazy_awi.rs b/starlight/src/awi_structs/lazy_awi.rs index bc78b315..045740af 100644 --- a/starlight/src/awi_structs/lazy_awi.rs +++ b/starlight/src/awi_structs/lazy_awi.rs @@ -3,6 +3,7 @@ use std::{ fmt, num::NonZeroUsize, ops::{Deref, Index, RangeFull}, + thread::panicking, }; use awint::{ @@ -19,17 +20,37 @@ use crate::{ // do not implement `Clone` for this, we would need a separate `LazyCellAwi` // type -// TODO I have attached a note to `LazyAwi` because without debug assertions, -// states could get clobbered. I suspect that it naturally requires `Note`s to -// get involved because of the `nzbw` problem. - /// When other mimicking types are created from a reference of this, `retro_` /// can later be called to retroactively change the input values of the DAG. +/// +/// # Custom Drop +/// +/// Upon being dropped, this will remove special references being kept by the +/// current `Epoch` pub struct LazyAwi { opaque: dag::Awi, p_note: PNote, } +impl Drop for LazyAwi { + fn drop(&mut self) { + // prevent invoking recursive panics and a buffer overrun + if !panicking() { + if let Some(epoch) = get_current_epoch() { + let res = epoch + .epoch_data + .borrow_mut() + .ensemble + .remove_note(self.p_note); + if res.is_err() { + panic!("most likely, a `LazyAwi` created in one `Epoch` was dropped in another") + } + } + // else the epoch has been dropped + } + } +} + impl Lineage for LazyAwi { fn state(&self) -> PState { self.opaque.state() diff --git a/testcrate/benches/bench.rs b/testcrate/benches/bench.rs index 0d8f90ee..5f8c8dd1 100644 --- a/testcrate/benches/bench.rs +++ b/testcrate/benches/bench.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use starlight::{dag::*, Epoch, EvalAwi, LazyAwi}; +use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi}; use test::Bencher; #[bench] @@ -14,12 +14,12 @@ fn lower_funnel(bencher: &mut Bencher) { let mut out = inlawi!(0u32); out.funnel_(&rhs, &s).unwrap(); let _eval = EvalAwi::from(&out); + epoch0.prune().unwrap(); epoch0.lower().unwrap(); epoch0.assert_assertions().unwrap(); - // FIXME - //awi::assert_eq!(epoch0.ensemble().stator.states.len(), 7045); - //awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 26250); - //awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 4773); + awi::assert_eq!(epoch0.ensemble().stator.states.len(), 2437); + awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 8623); + awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 1349); }) } @@ -36,9 +36,8 @@ fn optimize_funnel(bencher: &mut Bencher) { epoch0.prune().unwrap(); epoch0.optimize().unwrap(); epoch0.assert_assertions().unwrap(); - // FIXME - //awi::assert_eq!(epoch0.ensemble().stator.states.len(), 7044); - //awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 15304); - //awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 1236); + awi::assert_eq!(epoch0.ensemble().stator.states.len(), 2437); + awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 8383); + awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 1269); }) } diff --git a/testcrate/tests/fuzz_lower.rs b/testcrate/tests/fuzz_lower.rs index 27b1ff8f..60e7862e 100644 --- a/testcrate/tests/fuzz_lower.rs +++ b/testcrate/tests/fuzz_lower.rs @@ -769,12 +769,12 @@ fn fuzz_lower() { for _ in 0..N.1 { let epoch = Epoch::new(); - m.clear(); for _ in 0..N.0 { num_dag_duo(&mut rng, &mut m) } m.finish(&epoch); m.eval_and_verify_equal(&epoch).unwrap(); + m.clear(); drop(epoch); } } From dcdd0b4c49d8f85f8a7d26c838e88bb8b20d8676 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 01:21:04 -0600 Subject: [PATCH 41/62] fix the benches --- testcrate/benches/bench.rs | 8 +------- testcrate/tests/stats.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 testcrate/tests/stats.rs diff --git a/testcrate/benches/bench.rs b/testcrate/benches/bench.rs index 5f8c8dd1..1504cfbd 100644 --- a/testcrate/benches/bench.rs +++ b/testcrate/benches/bench.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi}; +use starlight::{dag::*, Epoch, EvalAwi, LazyAwi}; use test::Bencher; #[bench] @@ -17,9 +17,6 @@ fn lower_funnel(bencher: &mut Bencher) { epoch0.prune().unwrap(); epoch0.lower().unwrap(); epoch0.assert_assertions().unwrap(); - awi::assert_eq!(epoch0.ensemble().stator.states.len(), 2437); - awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 8623); - awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 1349); }) } @@ -36,8 +33,5 @@ fn optimize_funnel(bencher: &mut Bencher) { epoch0.prune().unwrap(); epoch0.optimize().unwrap(); epoch0.assert_assertions().unwrap(); - awi::assert_eq!(epoch0.ensemble().stator.states.len(), 2437); - awi::assert_eq!(epoch0.ensemble().backrefs.len_keys(), 8383); - awi::assert_eq!(epoch0.ensemble().backrefs.len_vals(), 1269); }) } diff --git a/testcrate/tests/stats.rs b/testcrate/tests/stats.rs new file mode 100644 index 00000000..377e335e --- /dev/null +++ b/testcrate/tests/stats.rs @@ -0,0 +1,26 @@ +use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi}; + +// this is done separately from the benchmarks because getting the `ensemble` is expensive +#[test] +fn stats_optimize_funnel() { + let epoch0 = Epoch::new(); + + let rhs = LazyAwi::opaque(bw(64)); + let s = LazyAwi::opaque(bw(5)); + let mut out = inlawi!(0u32); + out.funnel_(&rhs, &s).unwrap(); + let _eval = EvalAwi::from(&out); + epoch0.prune().unwrap(); + epoch0.lower().unwrap(); + epoch0.assert_assertions().unwrap(); + let ensemble = epoch0.ensemble(); + awi::assert_eq!(ensemble.stator.states.len(), 2437); + awi::assert_eq!(ensemble.backrefs.len_keys(), 8623); + awi::assert_eq!(ensemble.backrefs.len_vals(), 1349); + epoch0.optimize().unwrap(); + epoch0.assert_assertions().unwrap(); + let ensemble = epoch0.ensemble(); + awi::assert_eq!(ensemble.stator.states.len(), 2437); + awi::assert_eq!(ensemble.backrefs.len_keys(), 8383); + awi::assert_eq!(ensemble.backrefs.len_vals(), 1269); +} From e3a4fa6115e5231adffe37aed2b580dd1a839f22 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 01:35:29 -0600 Subject: [PATCH 42/62] fix old docs --- starlight/Cargo.toml | 4 ++-- starlight/src/awi_structs/epoch.rs | 3 --- starlight/src/lower/lower_state.rs | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/starlight/Cargo.toml b/starlight/Cargo.toml index 26d2094f..24980229 100644 --- a/starlight/Cargo.toml +++ b/starlight/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/AaronKutch/starlight" documentation = "https://docs.rs/starlight" -description = "reservation" +description = "experimental HDL and optimizer for DAGs of lookup tables" keywords = ["dag", "rtl", "hdl"] -categories = [] +categories = ["algorithms"] [dependencies] awint = { path = "../../awint/awint", default-features = false, features = ["rand_support", "dag"] } diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index c3d4cdc1..11686201 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -534,7 +534,4 @@ impl Epoch { lock.ensemble.optimize_all(); Ok(()) } - - // TODO - //pub fn prune_nonnoted } diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index efe659e9..21aae9e5 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -39,7 +39,6 @@ impl Ensemble { } } - // TODO what do we do when we make multi-output things // graft input for i in 1..operands.len() { let grafted = operands[i]; @@ -64,8 +63,6 @@ impl Ensemble { } pub fn lower_op(epoch_shared: &EpochShared, p_state: PState) -> Result { - // TODO optimization to remove unused nodes early - //let epoch = StateEpoch::new(); struct Tmp<'a> { ptr: PState, epoch_shared: &'a EpochShared, From e90332f8e31d15ba8fcc1f1995a2822e535f2a85 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 13:34:44 -0600 Subject: [PATCH 43/62] Version 0.2.0 --- CHANGELOG.md | 7 +++++++ starlight/Cargo.toml | 6 +++--- testcrate/tests/stats.rs | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc69969..6b8571e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.2.0] - 2023-12-08 +### Crate +- `awint` 0.15 + +### Changes +- Dramatically improved performance by a variety of changes + ## [0.1.0] - 2023-12-05 ### Crate - `awint` 0.14 diff --git a/starlight/Cargo.toml b/starlight/Cargo.toml index 24980229..c865db9f 100644 --- a/starlight/Cargo.toml +++ b/starlight/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "starlight" -version = "0.1.1" +version = "0.2.0" edition = "2021" authors = ["Aaron Kutch "] license = "MIT OR Apache-2.0" @@ -12,8 +12,8 @@ keywords = ["dag", "rtl", "hdl"] categories = ["algorithms"] [dependencies] -awint = { path = "../../awint/awint", default-features = false, features = ["rand_support", "dag"] } -#awint = { version = "0.14", default-features = false, features = ["rand_support", "dag"] } +#awint = { path = "../../awint/awint", default-features = false, features = ["rand_support", "dag"] } +awint = { version = "0.15", default-features = false, features = ["rand_support", "dag"] } rand_xoshiro = { version = "0.6", default-features = false } [features] diff --git a/testcrate/tests/stats.rs b/testcrate/tests/stats.rs index 377e335e..17caa7d3 100644 --- a/testcrate/tests/stats.rs +++ b/testcrate/tests/stats.rs @@ -1,6 +1,7 @@ use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi}; -// this is done separately from the benchmarks because getting the `ensemble` is expensive +// this is done separately from the benchmarks because getting the `ensemble` is +// expensive #[test] fn stats_optimize_funnel() { let epoch0 = Epoch::new(); From d1f52501f4ffe1694aaad782827559824343ccea Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 14:08:52 -0600 Subject: [PATCH 44/62] many improvements --- starlight/src/awi_structs/epoch.rs | 8 +++---- starlight/src/awi_structs/eval_awi.rs | 30 ++++++++++++--------------- starlight/src/ensemble/debug.rs | 6 +++++- starlight/src/lower/lower_state.rs | 6 ------ testcrate/tests/loop.rs | 25 ++++++++++++---------- testcrate/tests/stats.rs | 12 +++++------ 6 files changed, 41 insertions(+), 46 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 11686201..afa9e659 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -86,7 +86,7 @@ impl EpochShared { epoch_key: _callback().push_on_epoch_stack(), ensemble: Ensemble::new(), responsible_for: Arena::new(), - allow_pruning: false, + allow_pruning: true, }; let p_self = epoch_data.responsible_for.insert(PerEpochShared::new()); Self { @@ -108,7 +108,6 @@ impl EpochShared { } } - /* /// Returns a clone of the assertions currently associated with `self` pub fn assertions(&self) -> Assertions { let p_self = self.p_self; @@ -132,7 +131,7 @@ impl EpochShared { } } Assertions { bits: cloned } - }*/ + } /// Using `EpochShared::assertions` creates all new `Assertions`. This /// eliminates assertions that evaluate to a constant true. @@ -478,13 +477,12 @@ impl Epoch { &this.shared } - /* /// Gets the assertions associated with this Epoch (not including assertions /// from when sub-epochs are alive or from before the this Epoch was /// created) pub fn assertions(&self) -> Assertions { self.shared.assertions() - }*/ + } // TODO fix the EvalError enum situation diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 042ef65c..a6ee1468 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -30,11 +30,8 @@ impl Drop for EvalAwi { // prevent invoking recursive panics and a buffer overrun if !panicking() { if let Some(epoch) = get_current_epoch() { - let res = epoch - .epoch_data - .borrow_mut() - .ensemble - .remove_note(self.p_note); + let mut lock = epoch.epoch_data.borrow_mut(); + let res = lock.ensemble.remove_note(self.p_note); if res.is_err() { panic!( "most likely, an `EvalAwi` created in one `Epoch` was dropped in another" @@ -55,13 +52,9 @@ impl Lineage for EvalAwi { impl Clone for EvalAwi { /// This makes another note to the same state that `self` pointed to. fn clone(&self) -> Self { - let p_note = get_current_epoch() - .unwrap() - .epoch_data - .borrow_mut() - .ensemble - .note_pstate(self.p_state) - .unwrap(); + let epoch_data = get_current_epoch().unwrap().epoch_data; + let mut lock = epoch_data.borrow_mut(); + let p_note = lock.ensemble.note_pstate(self.p_state).unwrap(); Self { p_state: self.p_state, p_note, @@ -83,12 +76,15 @@ impl EvalAwi { } pub(crate) fn from_state(p_state: PState) -> Option { - let p_note = get_current_epoch() + let epoch_data = get_current_epoch().unwrap().epoch_data; + let mut lock = epoch_data.borrow_mut(); + let p_note = lock.ensemble.note_pstate(p_state)?; + lock.ensemble + .stator + .states + .get_mut(p_state) .unwrap() - .epoch_data - .borrow_mut() - .ensemble - .note_pstate(p_state)?; + .allow_pruning = false; Some(Self { p_state, p_note }) } diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index d1f116f3..9e77c5d2 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -18,7 +18,11 @@ impl DebugNodeTrait for State { sources: { let mut v = vec![]; for i in 0..this.op.operands_len() { - v.push((this.op.operands()[i], this.op.operand_names()[i].to_owned())) + if let Some(name) = this.op.operand_names().get(i) { + v.push((this.op.operands()[i], (*name).to_owned())); + } else { + v.push((this.op.operands()[i], "".to_owned())); + } } v }, diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 21aae9e5..348bba1c 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -180,8 +180,6 @@ impl Ensemble { } lock.ensemble.stator.states[p_state].lowered_to_elementary = true; - // NOTE be sure to reset this before returning from the function - lock.allow_pruning = true; drop(lock); let mut path: Vec<(usize, PState)> = vec![(0, p_state)]; loop { @@ -329,7 +327,6 @@ impl Ensemble { temporary.remove_as_current(); let mut lock = epoch_shared.epoch_data.borrow_mut(); lock.ensemble.stator.states[p_state].err = Some(e.clone()); - lock.allow_pruning = false; return Err(e) } }; @@ -380,9 +377,6 @@ impl Ensemble { } } - let mut lock = epoch_shared.epoch_data.borrow_mut(); - lock.allow_pruning = false; - if unimplemented { Err(EvalError::Unimplemented) } else { diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index aea5f03a..1838eae8 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -1,6 +1,6 @@ -// TODO should loop be a capability of LazyAwi or something? Have an enum on the -// inside? -/* +/*use starlight::{Epoch, Loop, dag::*, EvalAwi, awi}; +use testcrate::_render; + #[test] fn invert_in_loop() { let epoch0 = Epoch::new(); @@ -16,17 +16,20 @@ fn invert_in_loop() { { use awi::{assert_eq, *}; - t_dag.eval_all().unwrap(); - assert_eq!(t_dag.get_noted_as_extawi(p_x).unwrap(), awi!(1)); - t_dag.drive_loops(); - t_dag.eval_all().unwrap(); - assert_eq!(t_dag.get_noted_as_extawi(p_x).unwrap(), awi!(0)); - t_dag.drive_loops(); - t_dag.eval_all().unwrap(); - assert_eq!(t_dag.get_noted_as_extawi(p_x).unwrap(), awi!(1)); + let eval_x = EvalAwi::from(&x); + _render(&epoch0).unwrap(); + //epoch0.drive_loops(); + assert_eq!(eval_x.eval().unwrap(), awi!(1)); + //epoch0.drive_loops(); + assert_eq!(eval_x.eval().unwrap(), awi!(0)); + //epoch0.drive_loops(); + assert_eq!(eval_x.eval().unwrap(), awi!(1)); } + drop(epoch0); } +*/ +/* // tests an incrementing counter #[test] fn incrementer() { diff --git a/testcrate/tests/stats.rs b/testcrate/tests/stats.rs index 17caa7d3..4d9cba6d 100644 --- a/testcrate/tests/stats.rs +++ b/testcrate/tests/stats.rs @@ -15,13 +15,13 @@ fn stats_optimize_funnel() { epoch0.lower().unwrap(); epoch0.assert_assertions().unwrap(); let ensemble = epoch0.ensemble(); - awi::assert_eq!(ensemble.stator.states.len(), 2437); - awi::assert_eq!(ensemble.backrefs.len_keys(), 8623); - awi::assert_eq!(ensemble.backrefs.len_vals(), 1349); + awi::assert_eq!(ensemble.stator.states.len(), 2436); + awi::assert_eq!(ensemble.backrefs.len_keys(), 8559); + awi::assert_eq!(ensemble.backrefs.len_vals(), 1317); epoch0.optimize().unwrap(); epoch0.assert_assertions().unwrap(); let ensemble = epoch0.ensemble(); - awi::assert_eq!(ensemble.stator.states.len(), 2437); - awi::assert_eq!(ensemble.backrefs.len_keys(), 8383); - awi::assert_eq!(ensemble.backrefs.len_vals(), 1269); + awi::assert_eq!(ensemble.stator.states.len(), 2436); + awi::assert_eq!(ensemble.backrefs.len_keys(), 8319); + awi::assert_eq!(ensemble.backrefs.len_vals(), 1237); } From d7942dc31795eb667b5c0369e28761fc15f29581 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 15:04:50 -0600 Subject: [PATCH 45/62] tmp --- starlight/src/awi_structs/eval_awi.rs | 16 ++++++++-------- starlight/src/ensemble/debug.rs | 2 +- starlight/src/ensemble/state.rs | 22 +++++++++++++++++++--- starlight/src/ensemble/together.rs | 5 +++-- starlight/src/lower/lower_state.rs | 2 +- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index a6ee1468..274ed8b9 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -37,6 +37,12 @@ impl Drop for EvalAwi { "most likely, an `EvalAwi` created in one `Epoch` was dropped in another" ) } + lock.ensemble + .stator + .states + .get_mut(self.p_state) + .unwrap() + .dec_other_rc(); } // else the epoch has been dropped } @@ -52,13 +58,7 @@ impl Lineage for EvalAwi { impl Clone for EvalAwi { /// This makes another note to the same state that `self` pointed to. fn clone(&self) -> Self { - let epoch_data = get_current_epoch().unwrap().epoch_data; - let mut lock = epoch_data.borrow_mut(); - let p_note = lock.ensemble.note_pstate(self.p_state).unwrap(); - Self { - p_state: self.p_state, - p_note, - } + Self::from_state(self.p_state).unwrap() } } @@ -84,7 +84,7 @@ impl EvalAwi { .states .get_mut(p_state) .unwrap() - .allow_pruning = false; + .inc_other_rc(); Some(Self { p_state, p_note }) } diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index 9e77c5d2..efa09f3b 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -52,7 +52,7 @@ impl DebugNodeTrait for State { v.push(format!( "{} {} {} {}", this.rc, - short(this.allow_pruning), + short(this.pruning_allowed()), short(this.lowered_to_elementary), short(this.lowered_to_tnodes) )); diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index b4ee40b4..3e0d9b3d 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -35,6 +35,7 @@ pub struct State { /// The number of other `State`s, and only other `State`s, that reference /// this one through the `Op`s pub rc: usize, + pub other_rc: usize, /// Allows this state to be pruned pub allow_pruning: bool, /// If the `State` has been lowered to elementary `State`s (`Static-` @@ -46,6 +47,21 @@ pub struct State { pub lowered_to_tnodes: bool, } +impl State { + /// Returns if pruning this state is allowed + pub fn pruning_allowed(&self) -> bool { + (self.rc == 0) && (self.other_rc == 0) + } + + pub fn inc_other_rc(&mut self) { + self.other_rc = self.other_rc.checked_add(1).unwrap() + } + + pub fn dec_other_rc(&mut self) { + self.other_rc = self.other_rc.checked_sub(1).unwrap() + } +} + #[derive(Debug, Clone)] pub struct Stator { pub states: Arena, @@ -76,7 +92,7 @@ impl Ensemble { } else { return Err(EvalError::OtherStr("tried to subtract a 0 reference count")) }; - if (state.rc == 0) && state.allow_pruning { + if state.pruning_allowed() { self.remove_state(p_state)?; } Ok(()) @@ -85,12 +101,12 @@ impl Ensemble { } } - /// Prunes all states that `allow_pruning` + /// Prunes all states with `pruning_allowed()` pub fn prune_states(&mut self) -> Result<(), EvalError> { let mut adv = self.stator.states.advancer(); while let Some(p_state) = adv.advance(&self.stator.states) { let state = &self.stator.states[p_state]; - if state.allow_pruning { + if state.pruning_allowed() { self.remove_state(p_state).unwrap(); } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index de168708..75ab9350 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -342,6 +342,7 @@ impl Ensemble { location, err: None, rc: 0, + other_rc: 0, allow_pruning, lowered_to_elementary: false, lowered_to_tnodes: false, @@ -510,7 +511,7 @@ impl Ensemble { Ok(()) } - /// Triggers a cascade of state removals if the states `allow_pruning` and + /// Triggers a cascade of state removals if `pruning_allowed()` and /// their reference counts are zero pub fn remove_state(&mut self, p_state: PState) -> Result<(), EvalError> { if !self.stator.states.contains(p_state) { @@ -520,7 +521,7 @@ impl Ensemble { while let Some(p) = pstate_stack.pop() { let mut delete = false; if let Some(state) = self.stator.states.get(p) { - if (state.rc == 0) && state.allow_pruning { + if state.pruning_allowed() { delete = true; } } diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 348bba1c..0de1f532 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -339,7 +339,7 @@ impl Ensemble { let mut lock = epoch_shared.epoch_data.borrow_mut(); for p_state in states { let state = &lock.ensemble.stator.states[p_state]; - if state.allow_pruning && (state.rc == 0) { + if state.pruning_allowed() { lock.ensemble.remove_state(p_state).unwrap(); } } From 37385f7398c3cfcd9a69af1ce5b657338be973a6 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 15:13:50 -0600 Subject: [PATCH 46/62] overhaul pruning again --- starlight/src/awi_structs/epoch.rs | 8 +------- starlight/src/awi_structs/eval_awi.rs | 4 ++-- starlight/src/ensemble/optimize.rs | 8 ++++++-- starlight/src/ensemble/state.rs | 14 ++++++-------- starlight/src/ensemble/together.rs | 4 +--- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index afa9e659..4bc185c0 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -58,8 +58,6 @@ pub struct EpochData { pub epoch_key: EpochKey, pub ensemble: Ensemble, pub responsible_for: Arena, - /// If set, states made will have `allow_pruning` set - pub allow_pruning: bool, } impl Drop for EpochData { @@ -86,7 +84,6 @@ impl EpochShared { epoch_key: _callback().push_on_epoch_stack(), ensemble: Ensemble::new(), responsible_for: Arena::new(), - allow_pruning: true, }; let p_self = epoch_data.responsible_for.insert(PerEpochShared::new()); Self { @@ -348,10 +345,7 @@ pub fn _callback() -> EpochCallback { fn new_pstate(nzbw: NonZeroUsize, op: Op, location: Option) -> PState { no_recursive_current_epoch_mut(|current| { let mut epoch_data = current.epoch_data.borrow_mut(); - let allow_pruning = epoch_data.allow_pruning; - let p_state = epoch_data - .ensemble - .make_state(nzbw, op.clone(), location, allow_pruning); + let p_state = epoch_data.ensemble.make_state(nzbw, op.clone(), location); epoch_data .responsible_for .get_mut(current.p_self) diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 274ed8b9..36f7715c 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -42,7 +42,7 @@ impl Drop for EvalAwi { .states .get_mut(self.p_state) .unwrap() - .dec_other_rc(); + .dec_extern_rc(); } // else the epoch has been dropped } @@ -84,7 +84,7 @@ impl EvalAwi { .states .get_mut(p_state) .unwrap() - .inc_other_rc(); + .inc_extern_rc(); Some(Self { p_state, p_note }) } diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index c69da914..93878431 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -215,7 +215,9 @@ impl Ensemble { } Referent::ThisStateBit(p_state, _) => { let state = &self.stator.states[p_state]; - if !state.allow_pruning { + // the state bits can always be disregarded on a per-tnode basis unless they are + // being used externally + if state.extern_rc != 0 { non_self_rc += 1; } } @@ -465,7 +467,9 @@ impl Ensemble { Referent::ThisTNode(_) => (), Referent::ThisStateBit(p_state, _) => { let state = &self.stator.states[p_state]; - if !state.allow_pruning { + // the state bits can always be disregarded on a per-tnode basis unless + // they are being used externally + if state.extern_rc != 0 { found_use = true; } } diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 3e0d9b3d..59230750 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -35,9 +35,7 @@ pub struct State { /// The number of other `State`s, and only other `State`s, that reference /// this one through the `Op`s pub rc: usize, - pub other_rc: usize, - /// Allows this state to be pruned - pub allow_pruning: bool, + pub extern_rc: usize, /// If the `State` has been lowered to elementary `State`s (`Static-` /// operations and roots). Note that a DFS might set this before actually /// being lowered. @@ -50,15 +48,15 @@ pub struct State { impl State { /// Returns if pruning this state is allowed pub fn pruning_allowed(&self) -> bool { - (self.rc == 0) && (self.other_rc == 0) + (self.rc == 0) && (self.extern_rc == 0) } - pub fn inc_other_rc(&mut self) { - self.other_rc = self.other_rc.checked_add(1).unwrap() + pub fn inc_extern_rc(&mut self) { + self.extern_rc = self.extern_rc.checked_add(1).unwrap() } - pub fn dec_other_rc(&mut self) { - self.other_rc = self.other_rc.checked_sub(1).unwrap() + pub fn dec_extern_rc(&mut self) { + self.extern_rc = self.extern_rc.checked_sub(1).unwrap() } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index 75ab9350..c4256cba 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -329,7 +329,6 @@ impl Ensemble { nzbw: NonZeroUsize, op: Op, location: Option, - allow_pruning: bool, ) -> PState { for operand in op.operands() { let state = self.stator.states.get_mut(*operand).unwrap(); @@ -342,8 +341,7 @@ impl Ensemble { location, err: None, rc: 0, - other_rc: 0, - allow_pruning, + extern_rc: 0, lowered_to_elementary: false, lowered_to_tnodes: false, }) From d9eb103fa5795aed1c3d0884e4d31852e0ab9d4b Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 15:27:16 -0600 Subject: [PATCH 47/62] Update state.rs --- starlight/src/ensemble/state.rs | 352 ++++++++++++++++---------------- 1 file changed, 177 insertions(+), 175 deletions(-) diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 59230750..3facf2c2 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -240,181 +240,7 @@ impl Ensemble { path.last_mut().unwrap().0 += 1; } else if i >= ops.len() { // checked all sources - match self.stator.states[p_state].op { - Assert([x]) => { - // this is the only foolproof way of doing this, at least without more - // branches - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[p_state].p_self_bits.len(); - assert_eq!(len, self.stator.states[x].p_self_bits.len()); - for i in 0..len { - let p_equiv0 = self.stator.states[p_state].p_self_bits[i].unwrap(); - let p_equiv1 = self.stator.states[x].p_self_bits[i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - } - Copy([x]) => { - // this is the only foolproof way of doing this, at least without more - // branches - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[p_state].p_self_bits.len(); - assert_eq!(len, self.stator.states[x].p_self_bits.len()); - for i in 0..len { - let p_equiv0 = self.stator.states[p_state].p_self_bits[i].unwrap(); - let p_equiv1 = self.stator.states[x].p_self_bits[i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - } - StaticGet([bits], inx) => { - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[bits].p_self_bits.len(); - assert!(inx < len); - let p_self_bits = &self.stator.states[p_state].p_self_bits; - assert_eq!(p_self_bits.len(), 1); - let p_equiv0 = p_self_bits[0].unwrap(); - let p_equiv1 = self.stator.states[bits].p_self_bits[inx].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - Concat(ref concat) => { - let concat_len = concat.len(); - self.initialize_state_bits_if_needed(p_state).unwrap(); - let total_len = self.stator.states[p_state].p_self_bits.len(); - let mut to = 0; - for c_i in 0..concat_len { - let c = if let Concat(ref concat) = self.stator.states[p_state].op { - concat.as_slice()[c_i] - } else { - unreachable!() - }; - let len = self.stator.states[c].p_self_bits.len(); - for i in 0..len { - let p_equiv0 = - self.stator.states[p_state].p_self_bits[to + i].unwrap(); - let p_equiv1 = self.stator.states[c].p_self_bits[i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - to += len; - } - assert_eq!(total_len, to); - } - ConcatFields(ref concat) => { - let concat_len = concat.len(); - self.initialize_state_bits_if_needed(p_state).unwrap(); - let total_len = self.stator.states[p_state].p_self_bits.len(); - let mut to = 0; - for c_i in 0..concat_len { - let (c, (from, width)) = - if let ConcatFields(ref concat) = self.stator.states[p_state].op { - (concat.t_as_slice()[c_i], concat.field_as_slice()[c_i]) - } else { - unreachable!() - }; - let len = width.get(); - for i in 0..len { - let p_equiv0 = - self.stator.states[p_state].p_self_bits[to + i].unwrap(); - let p_equiv1 = self.stator.states[c].p_self_bits[from + i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - to += len; - } - assert_eq!(total_len, to); - } - Repeat([x]) => { - self.initialize_state_bits_if_needed(p_state).unwrap(); - let len = self.stator.states[p_state].p_self_bits.len(); - let x_w = self.stator.states[x].p_self_bits.len(); - assert!((len % x_w) == 0); - let mut from = 0; - for to in 0..len { - if from >= x_w { - from = 0; - } - let p_equiv0 = self.stator.states[p_state].p_self_bits[to].unwrap(); - let p_equiv1 = self.stator.states[x].p_self_bits[from].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - from += 1; - } - } - StaticLut(ref concat, ref table) => { - let table = table.clone(); - let concat_len = concat.len(); - self.initialize_state_bits_if_needed(p_state).unwrap(); - let mut inx_bits: SmallVec<[Option; 8]> = smallvec![]; - for c_i in 0..concat_len { - let c = if let StaticLut(ref concat, _) = self.stator.states[p_state].op - { - concat.as_slice()[c_i] - } else { - unreachable!() - }; - let bits = &self.stator.states[c].p_self_bits; - inx_bits.extend(bits.iter().cloned()); - } - - self.initialize_state_bits_if_needed(p_state).unwrap(); - let inx_len = inx_bits.len(); - let out_bw = self.stator.states[p_state].p_self_bits.len(); - let num_entries = - 1usize.checked_shl(u32::try_from(inx_len).unwrap()).unwrap(); - // this must be handled upstream - assert_eq!(out_bw * num_entries, table.bw()); - // convert from multiple out to single out bit lut - for bit_i in 0..out_bw { - let single_bit_table = if out_bw == 1 { - table.clone() - } else { - let mut val = - awi::Awi::zero(NonZeroUsize::new(num_entries).unwrap()); - for i in 0..num_entries { - val.set(i, table.get((i * out_bw) + bit_i).unwrap()) - .unwrap(); - } - val - }; - let p_equiv0 = self - .make_lut(&inx_bits, &single_bit_table, Some(p_state)) - .unwrap(); - let p_equiv1 = self.stator.states[p_state].p_self_bits[bit_i].unwrap(); - self.union_equiv(p_equiv0, p_equiv1).unwrap(); - } - } - Opaque(ref v, name) => { - if name == Some("LoopHandle") { - if v.len() != 2 { - return Err(EvalError::OtherStr( - "LoopHandle `Opaque` does not have 2 arguments", - )) - } - let v0 = v[0]; - let v1 = v[1]; - let w = self.stator.states[v0].p_self_bits.len(); - if w != self.stator.states[v1].p_self_bits.len() { - return Err(EvalError::OtherStr( - "LoopHandle `Opaque` has a bitwidth mismatch of looper and \ - driver", - )) - } - // Loops work by an initial `Opaque` that gets registered earlier - // and is used by things that use the loop value. A second - // LoopHandle Opaque references the first with `p_looper` and - // supplies a driver. - for i in 0..w { - let p_looper = self.stator.states[v0].p_self_bits[i].unwrap(); - let p_driver = self.stator.states[v1].p_self_bits[i].unwrap(); - self.make_loop(p_looper, p_driver, Value::Dynam(false)) - .unwrap(); - } - } else if let Some(name) = name { - return Err(EvalError::OtherString(format!( - "cannot lower opaque with name {name}" - ))) - } else { - return Err(EvalError::OtherStr("cannot lower opaque with no name")) - } - } - ref op => return Err(EvalError::OtherString(format!("cannot lower {op:?}"))), - } + lower_elementary_to_tnodes_intermediate(self, p_state)?; path.pop().unwrap(); if path.is_empty() { break @@ -469,6 +295,182 @@ impl Ensemble { } } +fn lower_elementary_to_tnodes_intermediate( + this: &mut Ensemble, + p_state: PState, +) -> Result<(), EvalError> { + match this.stator.states[p_state].op { + Assert([x]) => { + // this is the only foolproof way of doing this, at least without more + // branches + this.initialize_state_bits_if_needed(p_state).unwrap(); + let len = this.stator.states[p_state].p_self_bits.len(); + assert_eq!(len, this.stator.states[x].p_self_bits.len()); + for i in 0..len { + let p_equiv0 = this.stator.states[p_state].p_self_bits[i].unwrap(); + let p_equiv1 = this.stator.states[x].p_self_bits[i].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + } + Copy([x]) => { + // this is the only foolproof way of doing this, at least without more + // branches + this.initialize_state_bits_if_needed(p_state).unwrap(); + let len = this.stator.states[p_state].p_self_bits.len(); + assert_eq!(len, this.stator.states[x].p_self_bits.len()); + for i in 0..len { + let p_equiv0 = this.stator.states[p_state].p_self_bits[i].unwrap(); + let p_equiv1 = this.stator.states[x].p_self_bits[i].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + } + StaticGet([bits], inx) => { + this.initialize_state_bits_if_needed(p_state).unwrap(); + let len = this.stator.states[bits].p_self_bits.len(); + assert!(inx < len); + let p_self_bits = &this.stator.states[p_state].p_self_bits; + assert_eq!(p_self_bits.len(), 1); + let p_equiv0 = p_self_bits[0].unwrap(); + let p_equiv1 = this.stator.states[bits].p_self_bits[inx].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + Concat(ref concat) => { + let concat_len = concat.len(); + this.initialize_state_bits_if_needed(p_state).unwrap(); + let total_len = this.stator.states[p_state].p_self_bits.len(); + let mut to = 0; + for c_i in 0..concat_len { + let c = if let Concat(ref concat) = this.stator.states[p_state].op { + concat.as_slice()[c_i] + } else { + unreachable!() + }; + let len = this.stator.states[c].p_self_bits.len(); + for i in 0..len { + let p_equiv0 = this.stator.states[p_state].p_self_bits[to + i].unwrap(); + let p_equiv1 = this.stator.states[c].p_self_bits[i].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + to += len; + } + assert_eq!(total_len, to); + } + ConcatFields(ref concat) => { + let concat_len = concat.len(); + this.initialize_state_bits_if_needed(p_state).unwrap(); + let total_len = this.stator.states[p_state].p_self_bits.len(); + let mut to = 0; + for c_i in 0..concat_len { + let (c, (from, width)) = + if let ConcatFields(ref concat) = this.stator.states[p_state].op { + (concat.t_as_slice()[c_i], concat.field_as_slice()[c_i]) + } else { + unreachable!() + }; + let len = width.get(); + for i in 0..len { + let p_equiv0 = this.stator.states[p_state].p_self_bits[to + i].unwrap(); + let p_equiv1 = this.stator.states[c].p_self_bits[from + i].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + to += len; + } + assert_eq!(total_len, to); + } + Repeat([x]) => { + this.initialize_state_bits_if_needed(p_state).unwrap(); + let len = this.stator.states[p_state].p_self_bits.len(); + let x_w = this.stator.states[x].p_self_bits.len(); + assert!((len % x_w) == 0); + let mut from = 0; + for to in 0..len { + if from >= x_w { + from = 0; + } + let p_equiv0 = this.stator.states[p_state].p_self_bits[to].unwrap(); + let p_equiv1 = this.stator.states[x].p_self_bits[from].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + from += 1; + } + } + StaticLut(ref concat, ref table) => { + let table = table.clone(); + let concat_len = concat.len(); + this.initialize_state_bits_if_needed(p_state).unwrap(); + let mut inx_bits: SmallVec<[Option; 8]> = smallvec![]; + for c_i in 0..concat_len { + let c = if let StaticLut(ref concat, _) = this.stator.states[p_state].op { + concat.as_slice()[c_i] + } else { + unreachable!() + }; + let bits = &this.stator.states[c].p_self_bits; + inx_bits.extend(bits.iter().cloned()); + } + + this.initialize_state_bits_if_needed(p_state).unwrap(); + let inx_len = inx_bits.len(); + let out_bw = this.stator.states[p_state].p_self_bits.len(); + let num_entries = 1usize.checked_shl(u32::try_from(inx_len).unwrap()).unwrap(); + // this must be handled upstream + assert_eq!(out_bw * num_entries, table.bw()); + // convert from multiple out to single out bit lut + for bit_i in 0..out_bw { + let single_bit_table = if out_bw == 1 { + table.clone() + } else { + let mut val = awi::Awi::zero(NonZeroUsize::new(num_entries).unwrap()); + for i in 0..num_entries { + val.set(i, table.get((i * out_bw) + bit_i).unwrap()) + .unwrap(); + } + val + }; + let p_equiv0 = this + .make_lut(&inx_bits, &single_bit_table, Some(p_state)) + .unwrap(); + let p_equiv1 = this.stator.states[p_state].p_self_bits[bit_i].unwrap(); + this.union_equiv(p_equiv0, p_equiv1).unwrap(); + } + } + Opaque(ref v, name) => { + if name == Some("LoopHandle") { + if v.len() != 2 { + return Err(EvalError::OtherStr( + "LoopHandle `Opaque` does not have 2 arguments", + )) + } + let v0 = v[0]; + let v1 = v[1]; + let w = this.stator.states[v0].p_self_bits.len(); + if w != this.stator.states[v1].p_self_bits.len() { + return Err(EvalError::OtherStr( + "LoopHandle `Opaque` has a bitwidth mismatch of looper and driver", + )) + } + // Loops work by an initial `Opaque` that gets registered earlier + // and is used by things that use the loop value. A second + // LoopHandle Opaque references the first with `p_looper` and + // supplies a driver. + for i in 0..w { + let p_looper = this.stator.states[v0].p_self_bits[i].unwrap(); + let p_driver = this.stator.states[v1].p_self_bits[i].unwrap(); + this.make_loop(p_looper, p_driver, Value::Dynam(false)) + .unwrap(); + } + } else if let Some(name) = name { + return Err(EvalError::OtherString(format!( + "cannot lower opaque with name {name}" + ))) + } else { + return Err(EvalError::OtherStr("cannot lower opaque with no name")) + } + } + ref op => return Err(EvalError::OtherString(format!("cannot lower {op:?}"))), + } + Ok(()) +} + impl Default for Stator { fn default() -> Self { Self::new() From 2a462c7960b91beb91da3fcc229b8054e9dfbd7d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 23:06:48 -0600 Subject: [PATCH 48/62] create `LNode`s --- starlight/src/ensemble.rs | 2 + starlight/src/ensemble/debug.rs | 23 +++--- starlight/src/ensemble/lnode.rs | 19 +++++ starlight/src/ensemble/optimize.rs | 77 +++++++++++++++----- starlight/src/ensemble/tnode.rs | 8 +-- starlight/src/ensemble/together.rs | 111 ++++++++++++++++------------- starlight/src/ensemble/value.rs | 88 ++++++++++++++++++++--- 7 files changed, 235 insertions(+), 93 deletions(-) create mode 100644 starlight/src/ensemble/lnode.rs diff --git a/starlight/src/ensemble.rs b/starlight/src/ensemble.rs index 999afb37..e806a012 100644 --- a/starlight/src/ensemble.rs +++ b/starlight/src/ensemble.rs @@ -1,5 +1,6 @@ #[cfg(feature = "debug")] mod debug; +mod lnode; mod note; mod optimize; mod state; @@ -7,6 +8,7 @@ mod tnode; mod together; mod value; +pub use lnode::{LNode, PLNode}; pub use note::{Note, PNote}; pub use optimize::Optimizer; pub use state::{State, Stator}; diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index efa09f3b..e9f5bb92 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -5,6 +5,7 @@ use awint::{ awint_macro_internals::triple_arena::Arena, }; +use super::LNode; use crate::{ ensemble::{Ensemble, Equiv, PBack, PNote, Referent, State, TNode}, triple_arena::{Advancer, ChainArena}, @@ -79,6 +80,7 @@ pub struct StateBit { pub enum NodeKind { StateBit(StateBit), TNode(TNode), + LNode(LNode), Equiv(Equiv, Vec), Note(PBack, PNote, u64), Remove, @@ -108,9 +110,6 @@ impl DebugNodeTrait for NodeKind { if let Some(ref lut) = tnode.lut { v.push(format!("{:?} ", lut)); } - if let Some(driver) = tnode.loop_driver { - v.push(format!("driver: {:?}", driver)); - } if let Some(lowered_from) = tnode.lowered_from { v.push(format!("{:?}", lowered_from)); } @@ -118,6 +117,15 @@ impl DebugNodeTrait for NodeKind { }, sinks: vec![], }, + NodeKind::LNode(lnode) => DebugNode { + sources: vec![], + center: { + let mut v = vec![format!("{:?}", p_this)]; + v.push(format!("driver: {:?}", lnode.p_driver)); + v + }, + sinks: vec![], + }, NodeKind::Equiv(equiv, p_tnodes) => DebugNode { sources: p_tnodes.iter().map(|p| (*p, String::new())).collect(), center: { @@ -174,15 +182,6 @@ impl Ensemble { *inp = p_input; } } - if let Some(loop_driver) = tnode.loop_driver.as_mut() { - if let Referent::LoopDriver(_) = - self.backrefs.get_key(*loop_driver).unwrap() - { - let p_driver = - self.backrefs.get_val(*loop_driver).unwrap().p_self_equiv; - *loop_driver = p_driver; - } - } NodeKind::TNode(tnode) } Referent::Note(p_note) => { diff --git a/starlight/src/ensemble/lnode.rs b/starlight/src/ensemble/lnode.rs new file mode 100644 index 00000000..0b4cfe12 --- /dev/null +++ b/starlight/src/ensemble/lnode.rs @@ -0,0 +1,19 @@ +use awint::awint_dag::triple_arena::ptr_struct; + +use super::PBack; + +// We use this because our algorithms depend on generation counters +ptr_struct!(PLNode); + +/// A temporal loopback node +#[derive(Debug, Clone)] +pub struct LNode { + pub p_self: PBack, + pub p_driver: PBack, +} + +impl LNode { + pub fn new(p_self: PBack, p_driver: PBack) -> Self { + Self { p_self, p_driver } + } +} diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index 93878431..81c9ea41 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -9,6 +9,7 @@ use awint::{ Awi, InlAwi, }; +use super::PLNode; use crate::{ ensemble::{Ensemble, PBack, PTNode, Referent, TNode, Value}, triple_arena::{ptr_struct, OrdArena}, @@ -48,6 +49,8 @@ pub enum Optimization { InvestigateUsed(PBack), /// If an input was constified InvestigateConst(PTNode), + /// If a driver was constified + InvestigateLoopDriverConst(PLNode), /// The optimization state that equivalences are set to after the /// preinvestigation finds nothing InvestigateEquiv0(PBack), @@ -80,7 +83,8 @@ impl Optimizer { impl Ensemble { /// Removes all `Const` inputs and assigns `Const` result if possible. - /// Returns if a `Const` result was assigned. + /// Returns if a `Const` result was assigned (`Optimization::ConstifyEquiv` + /// needs to be run by the caller). pub fn const_eval_tnode(&mut self, p_tnode: PTNode) -> bool { let tnode = self.tnodes.get_mut(p_tnode).unwrap(); if let Some(original_lut) = &tnode.lut { @@ -157,8 +161,6 @@ impl Ensemble { if lut.bw() == 1 { let equiv = self.backrefs.get_val_mut(tnode.p_self).unwrap(); equiv.val = Value::Const(lut.to_bool()); - self.optimizer - .insert(Optimization::ConstifyEquiv(equiv.p_self_equiv)); // fix the `lut` to its new state, do this even if we are doing the constant // optimization tnode.lut = Some(lut); @@ -189,7 +191,21 @@ impl Ensemble { false } } else { - // TODO loopbacks + false + } + } + + /// Assigns `Const` result if possible. + /// Returns if a `Const` result was assigned. + pub fn const_eval_lnode(&mut self, p_lnode: PLNode) -> bool { + let lnode = self.lnodes.get(p_lnode).unwrap(); + let p_self = lnode.p_self; + let p_driver = lnode.p_driver; + let equiv = self.backrefs.get_val(p_driver).unwrap(); + if let Value::Const(val) = equiv.val { + self.backrefs.get_val_mut(p_self).unwrap().val = Value::Const(val); + true + } else { false } } @@ -207,6 +223,12 @@ impl Ensemble { let referent = *self.backrefs.get_key(p_back).unwrap(); match referent { Referent::ThisEquiv => (), + Referent::ThisLNode(p_lnode) => { + // avoid checking more if it was already determined to be constant + if !is_const && self.const_eval_lnode(p_lnode) { + is_const = true; + } + } Referent::ThisTNode(p_tnode) => { // avoid checking more if it was already determined to be constant if !is_const && self.const_eval_tnode(p_tnode) { @@ -226,7 +248,7 @@ impl Ensemble { // the way `LoopDriver` networks with no real dependencies will work, is // that const propogation and other simplifications will eventually result // in a single node equivalence that drives itself, which we can remove - let p_back_driver = self.tnodes.get(p_driver).unwrap().p_self; + let p_back_driver = self.lnodes.get(p_driver).unwrap().p_self; if !self.backrefs.in_same_set(p_back, p_back_driver).unwrap() { non_self_rc += 1; } @@ -271,12 +293,6 @@ impl Ensemble { /// `Advancer`s. pub fn remove_tnode_not_p_self(&mut self, p_tnode: PTNode) { let tnode = self.tnodes.remove(p_tnode).unwrap(); - if let Some(p_driver) = tnode.loop_driver { - let p_equiv = self.backrefs.get_val(p_driver).unwrap().p_self_equiv; - self.optimizer - .insert(Optimization::InvestigateUsed(p_equiv)); - self.backrefs.remove_key(p_driver).unwrap(); - } for inp in tnode.inp { let p_equiv = self.backrefs.get_val(inp).unwrap().p_self_equiv; self.optimizer @@ -285,6 +301,17 @@ impl Ensemble { } } + /// Does not perform the final step + /// `ensemble.backrefs.remove(lnode.p_self).unwrap()` which is important for + /// `Advancer`s. + pub fn remove_lnode_not_p_self(&mut self, p_lnode: PLNode) { + let lnode = self.lnodes.remove(p_lnode).unwrap(); + let p_equiv = self.backrefs.get_val(lnode.p_driver).unwrap().p_self_equiv; + self.optimizer + .insert(Optimization::InvestigateUsed(p_equiv)); + self.backrefs.remove_key(lnode.p_driver).unwrap(); + } + pub fn optimize_all(&mut self) { // need to preinvestigate everything before starting a priority loop let mut adv = self.backrefs.advancer(); @@ -357,6 +384,9 @@ impl Ensemble { Referent::ThisTNode(p_tnode) => { self.remove_tnode_not_p_self(p_tnode); } + Referent::ThisLNode(p_lnode) => { + self.remove_lnode_not_p_self(p_lnode); + } Referent::ThisStateBit(p_state, i_bit) => { let p_bit = self.stator.states[p_state].p_self_bits[i_bit] .as_mut() @@ -384,13 +414,13 @@ impl Ensemble { assert!(found); } Referent::LoopDriver(p_driver) => { - let tnode = self.tnodes.get_mut(p_driver).unwrap(); - assert_eq!(tnode.loop_driver, Some(p_back)); + let lnode = self.lnodes.get_mut(p_driver).unwrap(); + assert_eq!(lnode.p_driver, p_back); let p_back_new = self .backrefs .insert_key(p_source, Referent::LoopDriver(p_driver)) .unwrap(); - tnode.loop_driver = Some(p_back_new); + lnode.p_driver = p_back_new; } Referent::Note(p_note) => { // here we see a major advantage of the backref system @@ -432,6 +462,10 @@ impl Ensemble { self.remove_tnode_not_p_self(*p_tnode); remove.push(p_back); } + Referent::ThisLNode(p_lnode) => { + self.remove_lnode_not_p_self(*p_lnode); + remove.push(p_back); + } Referent::ThisStateBit(..) => (), Referent::Input(p_inp) => { self.optimizer @@ -439,7 +473,7 @@ impl Ensemble { } Referent::LoopDriver(p_driver) => { self.optimizer - .insert(Optimization::InvestigateConst(*p_driver)); + .insert(Optimization::InvestigateLoopDriverConst(*p_driver)); } Referent::Note(_) => (), } @@ -465,6 +499,7 @@ impl Ensemble { match referent { Referent::ThisEquiv => (), Referent::ThisTNode(_) => (), + Referent::ThisLNode(_) => (), Referent::ThisStateBit(p_state, _) => { let state = &self.stator.states[p_state]; // the state bits can always be disregarded on a per-tnode basis unless @@ -478,7 +513,7 @@ impl Ensemble { break } Referent::LoopDriver(p_driver) => { - let p_back_driver = self.tnodes.get(p_driver).unwrap().p_self; + let p_back_driver = self.lnodes.get(p_driver).unwrap().p_self; if !self.backrefs.in_same_set(p_back, p_back_driver).unwrap() { found_use = true; break @@ -504,6 +539,16 @@ impl Ensemble { )); } } + Optimization::InvestigateLoopDriverConst(p_lnode) => { + if !self.lnodes.contains(p_lnode) { + return + }; + if self.const_eval_lnode(p_lnode) { + self.optimizer.insert(Optimization::ConstifyEquiv( + self.lnodes.get(p_lnode).unwrap().p_self, + )); + } + } Optimization::InvestigateEquiv0(_p_back) => { /*if !self.backrefs.contains(p_back) { return diff --git a/starlight/src/ensemble/tnode.rs b/starlight/src/ensemble/tnode.rs index b614b248..e679ab18 100644 --- a/starlight/src/ensemble/tnode.rs +++ b/starlight/src/ensemble/tnode.rs @@ -11,7 +11,7 @@ use crate::{ensemble::PBack, triple_arena::ptr_struct}; // We use this because our algorithms depend on generation counters ptr_struct!(PTNode); -/// A "table" node meant to evoke some kind of one-way table in a DAG. +/// A lookup table node #[derive(Debug, Clone)] pub struct TNode { pub p_self: PBack, @@ -19,11 +19,6 @@ pub struct TNode { pub inp: SmallVec<[PBack; 4]>, /// Lookup Table that outputs one bit pub lut: Option, - // If the value cannot be temporally changed with respect to what the - // simplification algorithms can assume. - //pub is_permanent: bool, - /// If the value is temporally driven by a `Loop` - pub loop_driver: Option, pub lowered_from: Option, } @@ -33,7 +28,6 @@ impl TNode { p_self, inp: SmallVec::new(), lut: None, - loop_driver: None, lowered_from, } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index c4256cba..a71b1806 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -9,6 +9,7 @@ use awint::{ Awi, Bits, }; +use super::{LNode, PLNode}; use crate::{ ensemble::{value::Evaluator, Note, Optimizer, PNote, PTNode, State, Stator, TNode, Value}, triple_arena::{ptr_struct, Arena, SurjectArena}, @@ -44,11 +45,14 @@ pub enum Referent { ThisEquiv, /// Self referent, used by all the `Tnode`s of an equivalence class ThisTNode(PTNode), + /// Self referent for an `LNode` + ThisLNode(PLNode), /// Self referent to a particular bit of a `State` ThisStateBit(PState, usize), /// Referent is using this for registering an input dependency Input(PTNode), - LoopDriver(PTNode), + /// Referent is using this for a loop driver + LoopDriver(PLNode), /// Referent is a note Note(PNote), } @@ -59,6 +63,7 @@ pub struct Ensemble { pub notes: Arena, pub stator: Stator, pub tnodes: Arena, + pub lnodes: Arena, pub evaluator: Evaluator, pub optimizer: Optimizer, } @@ -70,6 +75,7 @@ impl Ensemble { notes: Arena::new(), stator: Stator::new(), tnodes: Arena::new(), + lnodes: Arena::new(), evaluator: Evaluator::new(), optimizer: Optimizer::new(), } @@ -151,15 +157,29 @@ impl Ensemble { ))) } } + for (p_lnode, lnode) in &self.lnodes { + if let Some(Referent::ThisLNode(p_self)) = self.backrefs.get_key(lnode.p_self) { + if p_lnode != *p_self { + return Err(EvalError::OtherString(format!( + "{lnode:?}.p_self roundtrip fail" + ))) + } + } else { + return Err(EvalError::OtherString(format!( + "{lnode:?}.p_self is invalid" + ))) + } + } // check other referent validities for referent in self.backrefs.keys() { let invalid = match referent { // already checked Referent::ThisEquiv => false, Referent::ThisTNode(_) => false, + Referent::ThisLNode(_) => false, Referent::ThisStateBit(..) => false, Referent::Input(p_input) => !self.tnodes.contains(*p_input), - Referent::LoopDriver(p_driver) => !self.tnodes.contains(*p_driver), + Referent::LoopDriver(p_driver) => !self.lnodes.contains(*p_driver), Referent::Note(p_note) => !self.notes.contains(*p_note), }; if invalid { @@ -189,24 +209,26 @@ impl Ensemble { ))) } } - if let Some(loop_driver) = tnode.loop_driver { - if let Some(referent) = self.backrefs.get_key(loop_driver) { - if let Referent::LoopDriver(p_driver) = referent { - if !self.tnodes.contains(*p_driver) { - return Err(EvalError::OtherString(format!( - "{p_tnode}: {tnode:?} loop driver referrent {p_driver} is invalid" - ))) - } - } else { + } + for p_lnode in self.lnodes.ptrs() { + let lnode = self.lnodes.get(p_lnode).unwrap(); + if let Some(referent) = self.backrefs.get_key(lnode.p_driver) { + if let Referent::LoopDriver(p_driver) = referent { + if !self.lnodes.contains(*p_driver) { return Err(EvalError::OtherString(format!( - "{p_tnode}: {tnode:?} loop driver has incorrect referrent" + "{p_lnode}: {lnode:?} loop driver referrent {p_driver} is invalid" ))) } } else { return Err(EvalError::OtherString(format!( - "{p_tnode}: {tnode:?} loop driver {loop_driver} is invalid" + "{p_lnode}: {lnode:?} loop driver has incorrect referrent" ))) } + } else { + return Err(EvalError::OtherString(format!( + "{p_lnode}: {lnode:?} loop driver {} is invalid", + lnode.p_driver + ))) } } for note in self.notes.vals() { @@ -240,6 +262,10 @@ impl Ensemble { let tnode = self.tnodes.get(*p_tnode).unwrap(); p_back != tnode.p_self } + Referent::ThisLNode(p_lnode) => { + let lnode = self.lnodes.get(*p_lnode).unwrap(); + p_back != lnode.p_self + } Referent::ThisStateBit(p_state, inx) => { let state = self.stator.states.get(*p_state).unwrap(); let p_bit = state.p_self_bits.get(*inx).unwrap(); @@ -250,9 +276,9 @@ impl Ensemble { } } Referent::Input(p_input) => { - let tnode1 = self.tnodes.get(*p_input).unwrap(); + let tnode = self.tnodes.get(*p_input).unwrap(); let mut found = false; - for p_back1 in &tnode1.inp { + for p_back1 in &tnode.inp { if *p_back1 == p_back { found = true; break @@ -260,9 +286,9 @@ impl Ensemble { } !found } - Referent::LoopDriver(p_loop) => { - let tnode1 = self.tnodes.get(*p_loop).unwrap(); - tnode1.loop_driver != Some(p_back) + Referent::LoopDriver(p_lnode) => { + let lnode = self.lnodes.get(*p_lnode).unwrap(); + lnode.p_driver != p_back } Referent::Note(p_note) => { let note = self.notes.get(*p_note).unwrap(); @@ -445,27 +471,17 @@ impl Ensemble { } looper_equiv.val = init_val; - let referent = self.backrefs.get_key(p_looper)?; - let p_looper_tnode = match referent { - Referent::ThisEquiv => { - // need to create the TNode - self.tnodes.insert_with(|p_tnode| { - let p_back_self = self - .backrefs - .insert_key(p_looper, Referent::ThisTNode(p_tnode)) - .unwrap(); - TNode::new(p_back_self, None) - }) - } - // we might want to support more cases in the future - _ => panic!("bad referent {referent:?}"), - }; - let p_back_driver = self - .backrefs - .insert_key(p_driver, Referent::LoopDriver(p_looper_tnode)) - .unwrap(); - let tnode = self.tnodes.get_mut(p_looper_tnode).unwrap(); - tnode.loop_driver = Some(p_back_driver); + let p_lnode = self.lnodes.insert_with(|p_lnode| { + let p_driver = self + .backrefs + .insert_key(p_driver, Referent::LoopDriver(p_lnode)) + .unwrap(); + let p_self = self + .backrefs + .insert_key(p_looper, Referent::ThisLNode(p_lnode)) + .unwrap(); + LNode::new(p_self, p_driver) + }); Some(()) } @@ -545,17 +561,14 @@ impl Ensemble { Ok(()) } - pub fn drive_loops(&mut self) { - let mut adv = self.tnodes.advancer(); - while let Some(p_tnode) = adv.advance(&self.tnodes) { - let tnode = self.tnodes.get(p_tnode).unwrap(); - if let Some(p_driver) = tnode.loop_driver { - let driver_equiv = self.backrefs.get_val(p_driver).unwrap(); - let val = driver_equiv.val; - let looper_equiv = self.backrefs.get_val_mut(tnode.p_self).unwrap(); - looper_equiv.val = val; - } + pub fn drive_loops(&mut self) -> Result<(), EvalError> { + let mut adv = self.lnodes.advancer(); + while let Some(p_lnode) = adv.advance(&self.lnodes) { + let lnode = self.lnodes.get(p_lnode).unwrap(); + let driver_equiv = self.backrefs.get_val(lnode.p_driver).unwrap(); + self.change_value(lnode.p_self, driver_equiv.val).unwrap(); } + Ok(()) } } diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 590bf0a4..f2545e50 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -8,6 +8,7 @@ use awint::{ Awi, }; +use super::PLNode; use crate::{ ensemble::{Ensemble, PBack, PTNode, Referent, TNode}, epoch::EpochShared, @@ -91,6 +92,13 @@ pub struct RequestTNode { pub p_back_tnode: PBack, } +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct RequestLNode { + pub depth: i64, + pub number_a: u8, + pub p_back_lnode: PBack, +} + #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Change { pub depth: i64, @@ -102,8 +110,10 @@ pub struct Change { pub enum Eval { Investigate0(i64, PBack), ChangeTNode(PTNode), + ChangeLNode(PLNode), Change(Change), RequestTNode(RequestTNode), + RequestLNode(RequestLNode), /// When we have run out of normal things this will activate lowering Investigate1(PBack), } @@ -273,6 +283,38 @@ impl Ensemble { res } + /// If the returned vector is empty, evaluation was successful, otherwise + /// what is needed for evaluation is returned + pub fn try_eval_lnode(&mut self, p_lnode: PLNode, depth: i64) -> Option { + // read current inputs + let lnode = self.lnodes.get(p_lnode).unwrap(); + let p_equiv = self.backrefs.get_val(lnode.p_self).unwrap().p_self_equiv; + let p_driver = lnode.p_driver; + let equiv = self.backrefs.get_val(p_driver).unwrap(); + if let Value::Const(val) = equiv.val { + self.evaluator.insert(Eval::Change(Change { + depth, + p_equiv, + value: Value::Const(val), + })); + None + } else if equiv.change_visit == self.evaluator.change_visit_gen() { + // fixed + self.evaluator.insert(Eval::Change(Change { + depth, + p_equiv, + value: equiv.val, + })); + None + } else { + Some(RequestLNode { + depth: depth - 1, + number_a: 0, + p_back_lnode: p_driver, + }) + } + } + pub fn change_value(&mut self, p_back: PBack, value: Value) -> Option<()> { if let Some(equiv) = self.backrefs.get_val_mut(p_back) { if equiv.val.is_const() { @@ -384,6 +426,9 @@ impl Ensemble { // TODO get priorities right let _ = self.try_eval_tnode(p_tnode, 0); } + Eval::ChangeLNode(p_lnode) => { + let _ = self.try_eval_lnode(p_lnode, 0); + } Eval::Change(change) => { let equiv = self.backrefs.get_val_mut(change.p_equiv).unwrap(); equiv.change_visit = self.evaluator.change_visit_gen(); @@ -398,9 +443,10 @@ impl Ensemble { while let Some(p_back) = adv.advance(&self.backrefs) { let referent = *self.backrefs.get_key(p_back).unwrap(); match referent { - Referent::ThisEquiv => (), - Referent::ThisTNode(_) => (), - Referent::ThisStateBit(..) => (), + Referent::ThisEquiv + | Referent::ThisTNode(_) + | Referent::ThisLNode(_) + | Referent::ThisStateBit(..) => (), Referent::Input(p_tnode) => { let tnode = self.tnodes.get(p_tnode).unwrap(); let p_self = tnode.p_self; @@ -431,6 +477,21 @@ impl Ensemble { unreachable!() } } + Eval::RequestLNode(request) => { + if let Referent::LoopDriver(_) = + self.backrefs.get_key(request.p_back_lnode).unwrap() + { + let equiv = self.backrefs.get_val(request.p_back_lnode).unwrap(); + if (equiv.change_visit != self.evaluator.change_visit_gen()) + || (equiv.request_visit != self.evaluator.request_visit_gen()) + { + self.evaluator + .insert(Eval::Investigate0(request.depth, equiv.p_self_equiv)); + } + } else { + unreachable!() + } + } Eval::Investigate1(_) => todo!(), } } @@ -447,7 +508,7 @@ impl Ensemble { // eval but is only inserted if nothing like the TNode evaluation is able to // prove early value setting let mut insert_if_no_early_exit = vec![]; - let mut saw_tnode = false; + let mut saw_node = false; let mut saw_state = None; let mut adv = self.backrefs.advancer_surject(p_equiv); while let Some(p_back) = adv.advance(&self.backrefs) { @@ -460,20 +521,29 @@ impl Ensemble { // early exit because evaluation was successful return } - for eval in v { - insert_if_no_early_exit.push(Eval::RequestTNode(eval)); + for request in v { + insert_if_no_early_exit.push(Eval::RequestTNode(request)); + } + saw_node = true; + } + Referent::ThisLNode(p_lnode) => { + if let Some(request) = self.try_eval_lnode(p_lnode, depth) { + insert_if_no_early_exit.push(Eval::RequestLNode(request)); + } else { + // early exit because evaluation was successful + return } - saw_tnode = true; + saw_node = true; } Referent::ThisStateBit(p_state, _) => { saw_state = Some(p_state); } Referent::Input(_) => (), - Referent::LoopDriver(_) => {} + Referent::LoopDriver(_) => (), Referent::Note(_) => (), } } - if !saw_tnode { + if !saw_node { let mut will_lower = false; if let Some(p_state) = saw_state { if !self.stator.states[p_state].lowered_to_tnodes { From d2a26c9b58e08243d74d0856575d9fefa986896c Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Fri, 8 Dec 2023 23:26:17 -0600 Subject: [PATCH 49/62] small changes --- starlight/src/ensemble/state.rs | 11 ++++++++++- starlight/src/ensemble/together.rs | 18 ++++++++++-------- starlight/src/lower/lower_state.rs | 5 ++--- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 3facf2c2..512070b5 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -48,7 +48,16 @@ pub struct State { impl State { /// Returns if pruning this state is allowed pub fn pruning_allowed(&self) -> bool { - (self.rc == 0) && (self.extern_rc == 0) + (self.rc == 0) && (self.extern_rc == 0) && !matches!(self.op, Opaque(_, Some(_))) + } + + pub fn inc_rc(&mut self) { + self.rc = self.rc.checked_add(1).unwrap() + } + + pub fn dec_rc(&mut self) -> Option<()> { + self.rc = self.rc.checked_sub(1)?; + Some(()) } pub fn inc_extern_rc(&mut self) { diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index a71b1806..ae7d0c14 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -462,7 +462,12 @@ impl Ensemble { /// Sets up a loop from the loop source `p_looper` and driver `p_driver` #[must_use] - pub fn make_loop(&mut self, p_looper: PBack, p_driver: PBack, init_val: Value) -> Option<()> { + pub fn make_loop( + &mut self, + p_looper: PBack, + p_driver: PBack, + init_val: Value, + ) -> Option { let looper_equiv = self.backrefs.get_val_mut(p_looper)?; match looper_equiv.val { Value::Unknown => (), @@ -482,7 +487,7 @@ impl Ensemble { .unwrap(); LNode::new(p_self, p_driver) }); - Some(()) + Some(p_lnode) } pub fn union_equiv(&mut self, p_equiv0: PBack, p_equiv1: PBack) -> Result<(), EvalError> { @@ -542,12 +547,9 @@ impl Ensemble { if delete { for i in 0..self.stator.states[p].op.operands_len() { let op = self.stator.states[p].op.operands()[i]; - self.stator.states[op].rc = - if let Some(x) = self.stator.states[op].rc.checked_sub(1) { - x - } else { - return Err(EvalError::OtherStr("tried to subtract a 0 reference count")) - }; + if self.stator.states[op].dec_rc().is_none() { + return Err(EvalError::OtherStr("tried to subtract a 0 reference count")) + }; pstate_stack.push(op); } let mut state = self.stator.states.remove(p).unwrap(); diff --git a/starlight/src/lower/lower_state.rs b/starlight/src/lower/lower_state.rs index 0de1f532..8036febf 100644 --- a/starlight/src/lower/lower_state.rs +++ b/starlight/src/lower/lower_state.rs @@ -57,7 +57,7 @@ impl Ensemble { // graft output let grafted = operands[0]; self.stator.states.get_mut(p_state).unwrap().op = Copy([grafted]); - self.stator.states[grafted].rc = self.stator.states[grafted].rc.checked_add(1).unwrap(); + self.stator.states[grafted].inc_rc(); Ok(()) } @@ -365,8 +365,7 @@ impl Ensemble { while let Copy([a]) = lock.ensemble.stator.states[p_next].op { // special optimization case: forward Copies lock.ensemble.stator.states[p_state].op.operands_mut()[i] = a; - let rc = &mut lock.ensemble.stator.states[a].rc; - *rc = (*rc).checked_add(1).unwrap(); + lock.ensemble.stator.states[a].inc_rc(); lock.ensemble.dec_rc(p_next).unwrap(); p_next = a; } From 054da519540599c3594a9145db74223b2c630102 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 00:00:38 -0600 Subject: [PATCH 50/62] debug improvements --- starlight/src/ensemble/debug.rs | 87 +++++++++++++++++++++++++----- starlight/src/ensemble/lnode.rs | 2 +- starlight/src/ensemble/optimize.rs | 3 +- starlight/src/ensemble/together.rs | 6 ++- starlight/src/ensemble/value.rs | 3 +- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index e9f5bb92..84f1e24e 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -5,9 +5,8 @@ use awint::{ awint_macro_internals::triple_arena::Arena, }; -use super::LNode; use crate::{ - ensemble::{Ensemble, Equiv, PBack, PNote, Referent, State, TNode}, + ensemble::{Ensemble, Equiv, PBack, PLNode, PNote, Referent, State, TNode}, triple_arena::{Advancer, ChainArena}, triple_arena_render::{render_to_svg_file, DebugNode, DebugNodeTrait}, Epoch, @@ -72,17 +71,33 @@ impl DebugNodeTrait for State { #[derive(Debug, Clone)] pub struct StateBit { + p_equiv: Option, p_state: PState, i: usize, } +#[derive(Debug, Clone)] +pub struct LNodeTmp { + p_self: PBack, + p_driver: PBack, + p_lnode: PLNode, +} + +#[derive(Debug, Clone)] +pub struct NoteTmp { + p_self: PBack, + p_equiv: PBack, + p_note: PNote, + i: u64, +} + #[derive(Debug, Clone)] pub enum NodeKind { StateBit(StateBit), TNode(TNode), - LNode(LNode), + LNode(LNodeTmp), Equiv(Equiv, Vec), - Note(PBack, PNote, u64), + Note(NoteTmp), Remove, } @@ -90,10 +105,16 @@ impl DebugNodeTrait for NodeKind { fn debug_node(p_this: PBack, this: &Self) -> DebugNode { match this { NodeKind::StateBit(state_bit) => DebugNode { - sources: vec![], + sources: { + if let Some(p_equiv) = state_bit.p_equiv { + vec![(p_equiv, "".to_string())] + } else { + vec![] + } + }, center: { let mut v = vec![format!("{:?}", p_this)]; - v.push(format!("{} {}", state_bit.p_state, state_bit.i)); + v.push(format!("{} [{}]", state_bit.p_state, state_bit.i)); v }, sinks: vec![], @@ -118,10 +139,13 @@ impl DebugNodeTrait for NodeKind { sinks: vec![], }, NodeKind::LNode(lnode) => DebugNode { - sources: vec![], + sources: vec![ + (lnode.p_self, "self".to_owned()), + (lnode.p_driver, "driver".to_owned()), + ], center: { let mut v = vec![format!("{:?}", p_this)]; - v.push(format!("driver: {:?}", lnode.p_driver)); + v.push(format!("{:?}", lnode.p_lnode)); v }, sinks: vec![], @@ -136,9 +160,14 @@ impl DebugNodeTrait for NodeKind { }, sinks: vec![], }, - NodeKind::Note(p_back, p_note, inx) => DebugNode { - sources: vec![(*p_back, String::new())], - center: { vec![format!("{p_note} [{inx}]")] }, + NodeKind::Note(note) => DebugNode { + sources: vec![(note.p_equiv, String::new())], + center: { + vec![ + format!("{}", note.p_self), + format!("{} [{}]", note.p_note, note.i), + ] + }, sinks: vec![], }, NodeKind::Remove => panic!("should have been removed"), @@ -170,8 +199,22 @@ impl Ensemble { } NodeKind::Equiv(self.backrefs.get_val(p_self).unwrap().clone(), v) } - Referent::ThisStateBit(p, i) => { - NodeKind::StateBit(StateBit { p_state: *p, i: *i }) + Referent::ThisStateBit(p_state, i) => { + let state = self.stator.states.get(*p_state).unwrap().clone(); + if let Some(p_bit) = state.p_self_bits[*i] { + let p_equiv = self.backrefs.get_val(p_bit).unwrap().p_self_equiv; + NodeKind::StateBit(StateBit { + p_equiv: Some(p_equiv), + p_state: *p_state, + i: *i, + }) + } else { + NodeKind::StateBit(StateBit { + p_equiv: None, + p_state: *p_state, + i: *i, + }) + } } Referent::ThisTNode(p_tnode) => { let mut tnode = self.tnodes.get(*p_tnode).unwrap().clone(); @@ -184,6 +227,17 @@ impl Ensemble { } NodeKind::TNode(tnode) } + Referent::ThisLNode(p_lnode) => { + let lnode = self.lnodes.get(*p_lnode).unwrap(); + // forward to the `PBack`s + let p_self = self.backrefs.get_val(lnode.p_self).unwrap().p_self_equiv; + let p_driver = self.backrefs.get_val(lnode.p_driver).unwrap().p_self_equiv; + NodeKind::LNode(LNodeTmp { + p_self, + p_driver, + p_lnode: *p_lnode, + }) + } Referent::Note(p_note) => { let note = self.notes.get(*p_note).unwrap(); let mut inx = u64::MAX; @@ -193,7 +247,12 @@ impl Ensemble { } } let equiv = self.backrefs.get_val(p_self).unwrap(); - NodeKind::Note(equiv.p_self_equiv, *p_note, inx) + NodeKind::Note(NoteTmp { + p_self, + p_equiv: equiv.p_self_equiv, + p_note: *p_note, + i: inx, + }) } _ => NodeKind::Remove, } diff --git a/starlight/src/ensemble/lnode.rs b/starlight/src/ensemble/lnode.rs index 0b4cfe12..5a6555f1 100644 --- a/starlight/src/ensemble/lnode.rs +++ b/starlight/src/ensemble/lnode.rs @@ -1,6 +1,6 @@ use awint::awint_dag::triple_arena::ptr_struct; -use super::PBack; +use crate::ensemble::PBack; // We use this because our algorithms depend on generation counters ptr_struct!(PLNode); diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index 81c9ea41..3c55a6f7 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -9,9 +9,8 @@ use awint::{ Awi, InlAwi, }; -use super::PLNode; use crate::{ - ensemble::{Ensemble, PBack, PTNode, Referent, TNode, Value}, + ensemble::{Ensemble, PBack, PLNode, PTNode, Referent, TNode, Value}, triple_arena::{ptr_struct, OrdArena}, SmallMap, }; diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index ae7d0c14..4ae8d4da 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -9,9 +9,11 @@ use awint::{ Awi, Bits, }; -use super::{LNode, PLNode}; use crate::{ - ensemble::{value::Evaluator, Note, Optimizer, PNote, PTNode, State, Stator, TNode, Value}, + ensemble::{ + value::Evaluator, LNode, Note, Optimizer, PLNode, PNote, PTNode, State, Stator, TNode, + Value, + }, triple_arena::{ptr_struct, Arena, SurjectArena}, }; diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index f2545e50..049402eb 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -8,9 +8,8 @@ use awint::{ Awi, }; -use super::PLNode; use crate::{ - ensemble::{Ensemble, PBack, PTNode, Referent, TNode}, + ensemble::{Ensemble, PBack, PLNode, PTNode, Referent, TNode}, epoch::EpochShared, }; From d5ebdcfd172e4fae9a487e175a1a5e701dd0348e Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 15:46:29 -0600 Subject: [PATCH 51/62] actually fix the assertion situation --- starlight/src/awi_structs/epoch.rs | 19 ++++++++++++++++++- starlight/src/ensemble/debug.rs | 2 +- starlight/src/ensemble/state.rs | 5 +---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 4bc185c0..ab5dd60d 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -496,12 +496,25 @@ impl Epoch { self.shared.ensemble() } + /// Used for testing + pub fn prune_ignore_assertions(&self) -> Result<(), EvalError> { + let epoch_shared = get_current_epoch().unwrap(); + if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { + return Err(EvalError::OtherStr("epoch is not the current epoch")) + } + // do not assert assertions because that can trigger lowering + let mut lock = epoch_shared.epoch_data.borrow_mut(); + lock.ensemble.prune_states() + } + /// For users, this removes all states that do not lead to a live `EvalAwi` pub fn prune(&self) -> Result<(), EvalError> { let epoch_shared = get_current_epoch().unwrap(); if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { return Err(EvalError::OtherStr("epoch is not the current epoch")) } + // get rid of constant assertions + let _ = epoch_shared.assert_assertions(false); let mut lock = epoch_shared.epoch_data.borrow_mut(); lock.ensemble.prune_states() } @@ -513,7 +526,9 @@ impl Epoch { if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { return Err(EvalError::OtherStr("epoch is not the current epoch")) } - Ensemble::lower_all(&epoch_shared) + Ensemble::lower_all(&epoch_shared)?; + let _ = epoch_shared.assert_assertions(false); + Ok(()) } pub fn optimize(&self) -> Result<(), EvalError> { @@ -524,6 +539,8 @@ impl Epoch { Ensemble::lower_all(&epoch_shared)?; let mut lock = epoch_shared.epoch_data.borrow_mut(); lock.ensemble.optimize_all(); + drop(lock); + let _ = epoch_shared.assert_assertions(false); Ok(()) } } diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index 84f1e24e..f1370dae 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -52,7 +52,7 @@ impl DebugNodeTrait for State { v.push(format!( "{} {} {} {}", this.rc, - short(this.pruning_allowed()), + this.extern_rc, short(this.lowered_to_elementary), short(this.lowered_to_tnodes) )); diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 512070b5..a21cba5d 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -180,10 +180,7 @@ impl Ensemble { // anything let state = self.stator.states.get_mut(p_state).unwrap(); assert_eq!(state.rc, 0); - // FIXME we definitely need to go through Notes for assertions, - // doc example fails otherwise on release - //state.keep = false; - //self.remove_state(p_state).unwrap(); + self.remove_state(p_state).unwrap(); Ok(()) } else { unreachable!() From bea0ac39c00a1a0683279cdcd132aa348cc4ccd1 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 16:05:19 -0600 Subject: [PATCH 52/62] fix more LNode cases --- starlight/src/ensemble/optimize.rs | 5 +++-- starlight/src/ensemble/value.rs | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index 3c55a6f7..eb69fe9b 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -352,8 +352,9 @@ impl Ensemble { Referent::ThisTNode(p_tnode) => { self.remove_tnode_not_p_self(*p_tnode); } - // TODO check self reference case - Referent::LoopDriver(_) => todo!(), + Referent::LoopDriver(p_lnode) => { + self.remove_lnode_not_p_self(*p_lnode); + } _ => unreachable!(), } } diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 049402eb..a98dc474 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -176,7 +176,7 @@ impl Ensemble { // corresponding bits are set if the input is either a const value or is // already evaluated let mut fixed = inp.clone(); - // corresponding bits ar set if the input is `Value::Unknown` + // corresponding bits are set if the input is `Value::Unknown` let mut unknown = inp.clone(); for i in 0..len { let p_inp = tnode.inp[i]; @@ -458,7 +458,18 @@ impl Ensemble { self.evaluator.insert(Eval::ChangeTNode(p_tnode)); } } - Referent::LoopDriver(_) => (), + Referent::LoopDriver(p_lnode) => { + let lnode = self.lnodes.get(p_lnode).unwrap(); + let p_self = lnode.p_self; + let equiv = self.backrefs.get_val(p_self).unwrap(); + if (equiv.request_visit == self.evaluator.request_visit_gen()) + && (equiv.change_visit != self.evaluator.change_visit_gen()) + { + // only go leafward to the given input if it was in the request + // front and it hasn't been updated by some other route + self.evaluator.insert(Eval::ChangeLNode(p_lnode)); + } + } Referent::Note(_) => (), } } From 4d8162e6ecb0327e37936005d760362959b62258 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 16:18:05 -0600 Subject: [PATCH 53/62] Update value.rs --- starlight/src/ensemble/value.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index a98dc474..a2b04a4f 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -477,9 +477,7 @@ impl Ensemble { Eval::RequestTNode(request) => { if let Referent::Input(_) = self.backrefs.get_key(request.p_back_tnode).unwrap() { let equiv = self.backrefs.get_val(request.p_back_tnode).unwrap(); - if (equiv.change_visit != self.evaluator.change_visit_gen()) - || (equiv.request_visit != self.evaluator.request_visit_gen()) - { + if equiv.request_visit != self.evaluator.request_visit_gen() { self.evaluator .insert(Eval::Investigate0(request.depth, equiv.p_self_equiv)); } @@ -492,9 +490,7 @@ impl Ensemble { self.backrefs.get_key(request.p_back_lnode).unwrap() { let equiv = self.backrefs.get_val(request.p_back_lnode).unwrap(); - if (equiv.change_visit != self.evaluator.change_visit_gen()) - || (equiv.request_visit != self.evaluator.request_visit_gen()) - { + if equiv.request_visit != self.evaluator.request_visit_gen() { self.evaluator .insert(Eval::Investigate0(request.depth, equiv.p_self_equiv)); } From 816551c180a368923818173044a672daa376c9c7 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 17:21:58 -0600 Subject: [PATCH 54/62] fixes for `Loops` --- starlight/src/awi_structs/epoch.rs | 39 +++++++++++++++++++++++++++++- starlight/src/ensemble/together.rs | 20 ++------------- starlight/src/ensemble/value.rs | 1 + testcrate/tests/loop.rs | 10 +++----- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index ab5dd60d..396e756a 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -8,7 +8,7 @@ use std::{cell::RefCell, mem, num::NonZeroUsize, rc::Rc, thread::panicking}; use awint::{ awint_dag::{ epoch::{EpochCallback, EpochKey}, - triple_arena::{ptr_struct, Arena}, + triple_arena::{ptr_struct, Advancer, Arena}, EvalError, Lineage, Location, Op, PState, }, bw, dag, @@ -543,4 +543,41 @@ impl Epoch { let _ = epoch_shared.assert_assertions(false); Ok(()) } + + /// This evaluates all loop drivers, and then registers loopback changes + pub fn drive_loops(&self) -> Result<(), EvalError> { + let epoch_shared = get_current_epoch().unwrap(); + if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { + return Err(EvalError::OtherStr("epoch is not the current epoch")) + } + // first evaluate all loop drivers + let lock = epoch_shared.epoch_data.borrow(); + let mut adv = lock.ensemble.lnodes.advancer(); + drop(lock); + loop { + let lock = epoch_shared.epoch_data.borrow(); + if let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { + let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); + let p_driver = lnode.p_driver; + drop(lock); + Ensemble::calculate_value(&epoch_shared, p_driver)?; + } else { + break + } + } + // second do all loopback changes + let mut lock = epoch_shared.epoch_data.borrow_mut(); + let mut adv = lock.ensemble.lnodes.advancer(); + loop { + if let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { + let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); + let val = lock.ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; + let p_self = lnode.p_self; + lock.ensemble.change_value(p_self, val).unwrap(); + } else { + break + } + } + Ok(()) + } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index 4ae8d4da..1c3e441f 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -470,14 +470,6 @@ impl Ensemble { p_driver: PBack, init_val: Value, ) -> Option { - let looper_equiv = self.backrefs.get_val_mut(p_looper)?; - match looper_equiv.val { - Value::Unknown => (), - // shouldn't fail unless the special Opaque loopback structure is broken - _ => panic!("looper is already set to a known value"), - } - looper_equiv.val = init_val; - let p_lnode = self.lnodes.insert_with(|p_lnode| { let p_driver = self .backrefs @@ -489,6 +481,8 @@ impl Ensemble { .unwrap(); LNode::new(p_self, p_driver) }); + // in order for the value to register correctly + self.change_value(p_looper, init_val).unwrap(); Some(p_lnode) } @@ -564,16 +558,6 @@ impl Ensemble { } Ok(()) } - - pub fn drive_loops(&mut self) -> Result<(), EvalError> { - let mut adv = self.lnodes.advancer(); - while let Some(p_lnode) = adv.advance(&self.lnodes) { - let lnode = self.lnodes.get(p_lnode).unwrap(); - let driver_equiv = self.backrefs.get_val(lnode.p_driver).unwrap(); - self.change_value(lnode.p_self, driver_equiv.val).unwrap(); - } - Ok(()) - } } impl Default for Ensemble { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index a2b04a4f..fead50f4 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -326,6 +326,7 @@ impl Ensemble { self.evaluator.next_change_visit_gen(); } equiv.val = value; + equiv.change_visit = self.evaluator.change_visit_gen(); Some(()) } else { None diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index 1838eae8..71c463dd 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -1,5 +1,4 @@ -/*use starlight::{Epoch, Loop, dag::*, EvalAwi, awi}; -use testcrate::_render; +use starlight::{awi, dag::*, Epoch, EvalAwi, Loop}; #[test] fn invert_in_loop() { @@ -17,17 +16,14 @@ fn invert_in_loop() { use awi::{assert_eq, *}; let eval_x = EvalAwi::from(&x); - _render(&epoch0).unwrap(); - //epoch0.drive_loops(); assert_eq!(eval_x.eval().unwrap(), awi!(1)); - //epoch0.drive_loops(); + epoch0.drive_loops().unwrap(); assert_eq!(eval_x.eval().unwrap(), awi!(0)); - //epoch0.drive_loops(); + epoch0.drive_loops().unwrap(); assert_eq!(eval_x.eval().unwrap(), awi!(1)); } drop(epoch0); } -*/ /* // tests an incrementing counter From 9beed514d99b20fe0c70ee0dac9f7ea45ea8d37d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sat, 9 Dec 2023 22:38:55 -0600 Subject: [PATCH 55/62] overhaul the way `Loop`s work --- starlight/src/awi_structs.rs | 2 +- starlight/src/awi_structs/epoch.rs | 2 - starlight/src/awi_structs/temporal.rs | 199 +++++++++++++------------- starlight/src/ensemble/optimize.rs | 2 + starlight/src/ensemble/state.rs | 28 ++-- starlight/src/ensemble/together.rs | 2 - starlight/src/lib.rs | 8 +- starlight/src/lower.rs | 2 +- testcrate/tests/loop.rs | 64 +++++---- 9 files changed, 158 insertions(+), 151 deletions(-) diff --git a/starlight/src/awi_structs.rs b/starlight/src/awi_structs.rs index 24d30d47..ce1de563 100644 --- a/starlight/src/awi_structs.rs +++ b/starlight/src/awi_structs.rs @@ -6,4 +6,4 @@ mod temporal; pub use epoch::{Assertions, Epoch}; pub use eval_awi::EvalAwi; pub use lazy_awi::{LazyAwi, LazyInlAwi}; -pub use temporal::{Loop, LoopHandle, Net}; +pub use temporal::{Loop, Net}; diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 396e756a..1930f179 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -478,8 +478,6 @@ impl Epoch { self.shared.assertions() } - // TODO fix the EvalError enum situation - /// If any assertion bit evaluates to false, this returns an error. pub fn assert_assertions(&self) -> Result<(), EvalError> { self.shared.assert_assertions(false) diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index 8da3d6a7..b357c2a1 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -1,130 +1,129 @@ use std::{borrow::Borrow, num::NonZeroUsize, ops::Deref}; use awint::{ - awint_dag::{Lineage, PState}, - dag::{self, Awi, Bits, InlAwi}, + awint_dag::{smallvec::smallvec, Lineage, Op}, + dag::{self, awi, Awi, Bits, InlAwi}, }; -/// Returned from `Loop::drive` and other structures like `Net::drive` that use -/// `Loop`s internally, implements [awint::awint_dag::Lineage] so that the whole -/// DAG can be captured. -#[derive(Debug, Clone)] // TODO make Copy -pub struct LoopHandle { - // just use this for now to have the non-sendability - awi: Awi, -} - -impl Lineage for LoopHandle { - fn state(&self) -> PState { - self.awi.state() - } -} +use crate::{epoch::get_current_epoch, lower::meta::selector}; /// Provides a way to temporally wrap around a combinatorial circuit. /// -/// Get a `&Bits` reference from a `Loop` via the `Deref`, `Borrow`, or -/// `AsRef` impls, then consume the `Loop` with [Loop::drive]. +/// Get a `&Bits` temporal value from a `Loop` via one of the traits like +/// `Deref` or `AsRef`, then drive the `Loop` with +/// [Loop::drive]. When [crate::Epoch::drive_loops] is run, it will evaluate the +/// value of the driver and use that to retroactively change the temporal value +/// of the loop. /// /// The fundamental reason for temporal asymmetry is that there needs to be a /// well defined root evaluation state and value. #[derive(Debug)] // do not implement `Clone`, but maybe implement a `duplicate` function that // explicitly duplicates drivers and loopbacks? pub struct Loop { - awi: Awi, + source: Awi, } impl Loop { /// Creates a `Loop` with an intial temporal value of zero and bitwidth `w` pub fn zero(w: NonZeroUsize) -> Self { - // TODO add flag on opaque for initial value, and a way to notify if the - // `LoopHandle` is not included in the graph - Self { - awi: Awi::opaque(w), - } + let source = Awi::new(w, Op::Opaque(smallvec![], Some("LoopSource"))); + Self { source } } - // TODO pub fn opaque() umax(), etc + // TODO pub fn opaque(), umax(), From<&Bits>, etc. What we could do is have an + // extra input to "LoopSource" that designates the initial value, but there are + // many questions to be resolved /// Returns the bitwidth of `self` as a `NonZeroUsize` #[must_use] pub fn nzbw(&self) -> NonZeroUsize { - self.awi.nzbw() + self.source.nzbw() } /// Returns the bitwidth of `self` as a `usize` #[must_use] pub fn bw(&self) -> usize { - self.awi.bw() - } - - /// Get the loop value. This can conveniently be obtained by the `Deref`, - /// `Borrow`, and `AsRef` impls on `Loop`. - #[must_use] - pub fn get(&self) -> &Bits { - &self.awi + self.source.bw() } /// Consumes `self`, looping back with the value of `driver` to change the - /// `Loop`s temporal value in a iterative temporal evaluation. Returns a - /// `LoopHandle`. Returns `None` if `self.bw() != driver.bw()`. + /// `Loop`s temporal value in a iterative temporal evaluation. Returns + /// `None` if `self.bw() != driver.bw()`. #[must_use] - pub fn drive(mut self, driver: &Bits) -> Option { - // TODO use id from `awi`, for now since there are only `Loops` we denote a loop - // with a double input `Opaque` - if self.awi.bw() != driver.bw() { + pub fn drive(self, driver: &Bits) -> Option<()> { + if self.source.bw() != driver.bw() { None } else { - self.awi.opaque_with_(&[driver], Some("LoopHandle")); - Some(LoopHandle { awi: self.awi }) + let epoch = get_current_epoch().unwrap(); + let mut lock = epoch.epoch_data.borrow_mut(); + lock.ensemble + .stator + .states + .get_mut(self.source.state()) + .unwrap() + .op = Op::Opaque(smallvec![driver.state()], Some("LoopSource")); + lock.ensemble + .stator + .states + .get_mut(driver.state()) + .unwrap() + .inc_rc(); + Some(()) } } } -// TODO From<&Bits> and other constructions - impl Deref for Loop { type Target = Bits; fn deref(&self) -> &Self::Target { - self.get() + &self.source } } impl Borrow for Loop { fn borrow(&self) -> &Bits { - self.get() + &self.source } } impl AsRef for Loop { fn as_ref(&self) -> &Bits { - self.get() + &self.source } } /// A reconfigurable `Net` that is a `Vec`-like vector of "ports" that are -/// multiplexed to drive an internal `Loop`. First, [Net::get] or the trait -/// impls can be used to get the temporal value. Second, `Net::push_*` and -/// [Net::get_mut] can be write values to each of the ports. Third, [Net::drive] -/// takes a possibly dynamic index that multiplexes one of the values of the -/// ports to drive the temporal value. +/// multiplexed to drive an internal `Loop`. First, use a trait like +/// `Deref` or `AsRef` to get the temporal value. Second, +/// [Net::push] and [Net::get_mut] can be used to write values to each of the +/// ports. Third, [Net::drive] takes a possibly dynamic index that multiplexes +/// one of the values of the ports to drive the temporal value across +/// [crate::Epoch::drive_loops] calls. #[derive(Debug)] pub struct Net { - driver: Loop, - initial: Awi, + source: Loop, ports: Vec, } impl Net { - /// Create a `Net` with an initial value of zero and bitwidth `w` + /// Create a `Net` with an initial temporal value of zero and bitwidth `w` pub fn zero(w: NonZeroUsize) -> Self { Self { - driver: Loop::zero(w), - initial: Awi::zero(w), + source: Loop::zero(w), ports: vec![], } } + /// Creates a `Net` with [Net::zero] and pushes on `num_ports` ports + /// initialized to zero. + pub fn zero_with_ports(w: NonZeroUsize, num_ports: usize) -> Self { + Self { + source: Loop::zero(w), + ports: vec![Awi::zero(w); num_ports], + } + } + /// Returns the current number of ports #[must_use] pub fn len(&self) -> usize { @@ -140,34 +139,29 @@ impl Net { /// Returns the bitwidth of `self` as a `NonZeroUsize` #[must_use] pub fn nzbw(&self) -> NonZeroUsize { - self.driver.nzbw() + self.source.nzbw() } /// Returns the bitwidth of `self` as a `usize` #[must_use] pub fn bw(&self) -> usize { - self.driver.bw() + self.source.bw() } - /// Pushes on a new port that is initially set to the initial value this - /// `Net` was constructed with (and not the temporal value). If nothing is - /// done to the port, and this port is selected as the driver, then the - /// loop value will be the initial value this `Net` was originally - /// constructed with. Returns a mutable reference to the port for - /// immediate use (or the port can be accessed later by `get_mut`). - pub fn push(&mut self) -> &mut Bits { - self.ports.push(self.initial.clone()); - self.ports.last_mut().unwrap() - } - - /// Get the temporal value. This can conveniently be obtained by the - /// `Deref`, `Borrow`, and `AsRef` impls on `Net`. + /// Pushes on a new port. Returns `None` if the bitwidth mismatches the + /// width that this `Net` was created with #[must_use] - pub fn get(&self) -> &Bits { - &self.driver + pub fn push(&mut self, port: &Bits) -> Option<()> { + if port.bw() != self.bw() { + None + } else { + self.ports.push(Awi::from(port)); + Some(()) + } } - /// Gets the port at index `i`. Returns `None` if `i >= self.len()`. + /// Gets a mutable reference to the port at index `i`. Returns `None` if `i + /// >= self.len()`. #[must_use] pub fn get_mut(&mut self, i: usize) -> Option<&mut Bits> { self.ports.get_mut(i).map(|x| x.as_mut()) @@ -180,8 +174,8 @@ impl Net { if self.bw() != rhs.bw() { None } else { - self.ports.push(Awi::from(rhs.get())); - rhs.ports.push(Awi::from(self.get())); + self.ports.push(Awi::from(rhs.as_ref())); + rhs.ports.push(Awi::from(self.as_ref())); Some(()) } } @@ -189,35 +183,40 @@ impl Net { /// Drives with the value of the `inx`th port. Note that `inx` can be from /// a dynamic `dag::usize`. /// - /// If `inx` is out of range, the initial value is driven (and _not_ the - /// current temporal value). If `self.is_empty()`, the `LoopHandle` points - /// to a loop being driven with the initial value. + /// If `inx` is out of range, the return value is a runtime or dynamic + /// `None`. #[must_use] - pub fn drive(mut self, inx: impl Into) -> LoopHandle { - let last = InlAwi::from_usize(self.len()); - // this elegantly handles the `self.is_empty()` case in addition to the out of - // range case - self.push(); - - // set the index to `last` if it is out of range - let mut inx = InlAwi::from_usize(inx); - let gt = inx.ugt(&last).unwrap(); - inx.mux_(&last, gt).unwrap(); - - // TODO need an optimized onehot selector from `awint_dag` - let mut selector = Awi::uone(NonZeroUsize::new(self.len()).unwrap()); - selector.shl_(inx.to_usize()).unwrap(); + pub fn drive(mut self, inx: impl Into) -> dag::Option<()> { + if self.is_empty() { + return dag::Option::None; + } + let max_inx = self.len() - 1; + let max_inx_bits = max_inx.next_power_of_two().trailing_zeros() as usize; + let inx = InlAwi::from_usize(inx.into()); + // we detect overflow by seeing if any of these bits are nonzero or if the rest + // of the index is greater than the expected max bits (only needed if the + // self.len() is not a power of two) + let should_stay_zero = awi!(inx[max_inx_bits..]).unwrap(); + let mut in_range = should_stay_zero.is_zero(); + let inx = awi!(inx[..max_inx_bits]).unwrap(); + let signals = selector(&inx, None); + if !self.len().is_power_of_two() { + let le = inx.ule(&InlAwi::from_usize(max_inx)).unwrap(); + in_range &= le; + } + let mut tmp = Awi::zero(self.nzbw()); for i in 0..self.len() { - tmp.mux_(self.get_mut(i).unwrap(), selector.get(i).unwrap()) + tmp.mux_(self.get_mut(i).unwrap(), signals[i].to_bool()) .unwrap(); } - self.driver.drive(&tmp).unwrap() + self.source.drive(&tmp).unwrap(); + dag::Option::some_at_dagtime((), in_range) } // TODO we can do this // Drives with a one-hot vector of selectors. - //pub fn drive_priority(mut self, inx: impl Into) -> LoopHandle { + //pub fn drive_priority(mut self, inx: impl Into) { //pub fn drive_onehot(mut self, onehot) } @@ -225,19 +224,19 @@ impl Deref for Net { type Target = Bits; fn deref(&self) -> &Self::Target { - self.get() + &self.source } } impl Borrow for Net { fn borrow(&self) -> &Bits { - self.get() + &self.source } } impl AsRef for Net { fn as_ref(&self) -> &Bits { - self.get() + &self.source } } diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index eb69fe9b..ae0e5144 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -553,6 +553,8 @@ impl Ensemble { /*if !self.backrefs.contains(p_back) { return };*/ + // TODO eliminate equal TNodes, combine equal equivalences etc. + // TODO compare TNodes // TODO compress inverters by inverting inx table // TODO fusion of structures like diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index a21cba5d..0259be26 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -440,33 +440,27 @@ fn lower_elementary_to_tnodes_intermediate( } } Opaque(ref v, name) => { - if name == Some("LoopHandle") { - if v.len() != 2 { - return Err(EvalError::OtherStr( - "LoopHandle `Opaque` does not have 2 arguments", - )) + if name == Some("LoopSource") { + if v.len() != 1 { + return Err(EvalError::OtherStr("cannot lower an undriven `Loop`")) } - let v0 = v[0]; - let v1 = v[1]; - let w = this.stator.states[v0].p_self_bits.len(); - if w != this.stator.states[v1].p_self_bits.len() { + let p_driver_state = v[0]; + this.initialize_state_bits_if_needed(p_state).unwrap(); + let w = this.stator.states[p_state].p_self_bits.len(); + if w != this.stator.states[p_driver_state].p_self_bits.len() { return Err(EvalError::OtherStr( - "LoopHandle `Opaque` has a bitwidth mismatch of looper and driver", + "`Loop` has a bitwidth mismatch of looper and driver", )) } - // Loops work by an initial `Opaque` that gets registered earlier - // and is used by things that use the loop value. A second - // LoopHandle Opaque references the first with `p_looper` and - // supplies a driver. for i in 0..w { - let p_looper = this.stator.states[v0].p_self_bits[i].unwrap(); - let p_driver = this.stator.states[v1].p_self_bits[i].unwrap(); + let p_looper = this.stator.states[p_state].p_self_bits[i].unwrap(); + let p_driver = this.stator.states[p_driver_state].p_self_bits[i].unwrap(); this.make_loop(p_looper, p_driver, Value::Dynam(false)) .unwrap(); } } else if let Some(name) = name { return Err(EvalError::OtherString(format!( - "cannot lower opaque with name {name}" + "cannot lower opaque with name \"{name}\"" ))) } else { return Err(EvalError::OtherStr("cannot lower opaque with no name")) diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index 1c3e441f..0b9c6048 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -5,7 +5,6 @@ use awint::{ smallvec::{smallvec, SmallVec}, EvalError, Location, Op, PState, }, - awint_macro_internals::triple_arena::Advancer, Awi, Bits, }; @@ -348,7 +347,6 @@ impl Ensemble { } } - // TODO verify DAGness Ok(()) } diff --git a/starlight/src/lib.rs b/starlight/src/lib.rs index eb647a9d..ee7e17a8 100644 --- a/starlight/src/lib.rs +++ b/starlight/src/lib.rs @@ -165,9 +165,7 @@ mod awi_structs; pub mod ensemble; pub(crate) mod lower; mod misc; -pub use awi_structs::{ - epoch, Assertions, Epoch, EvalAwi, LazyAwi, LazyInlAwi, Loop, LoopHandle, Net, -}; +pub use awi_structs::{epoch, Assertions, Epoch, EvalAwi, LazyAwi, LazyInlAwi, Loop, Net}; #[cfg(feature = "debug")] pub use awint::awint_dag::triple_arena_render; pub use awint::{self, awint_dag, awint_dag::triple_arena}; @@ -191,9 +189,11 @@ pub mod dag { *, }; - pub use crate::{Loop, LoopHandle, Net}; + pub use crate::{Loop, Net}; } +// TODO fix the EvalError enum situation + // TODO use modified Lagrangians that appear different to nets with different // requirements on critical path, plus small differencing values to prevent // alternating constraint problems diff --git a/starlight/src/lower.rs b/starlight/src/lower.rs index 9a145437..41f8408e 100644 --- a/starlight/src/lower.rs +++ b/starlight/src/lower.rs @@ -1,5 +1,5 @@ mod lower_op; mod lower_state; -mod meta; +pub(crate) mod meta; pub use lower_op::{lower_op, LowerManagement}; diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index 71c463dd..23acea91 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -1,7 +1,7 @@ -use starlight::{awi, dag::*, Epoch, EvalAwi, Loop}; +use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi, Loop}; #[test] -fn invert_in_loop() { +fn loop_invert() { let epoch0 = Epoch::new(); let looper = Loop::zero(bw(1)); let mut x = awi!(looper); @@ -25,38 +25,54 @@ fn invert_in_loop() { drop(epoch0); } -/* // tests an incrementing counter #[test] -fn incrementer() { - let epoch0 = StateEpoch::new(); +fn loop_incrementer() { + let epoch0 = Epoch::new(); let looper = Loop::zero(bw(4)); - let val = Awi::from(looper.as_ref()); - let mut tmp = Awi::from(looper.as_ref()); + let val = EvalAwi::from(&looper); + let mut tmp = awi!(looper); tmp.inc_(true); looper.drive(&tmp).unwrap(); - let (mut op_dag, res) = OpDag::from_epoch(&epoch0); - res.unwrap(); - - let p_val = op_dag.note_pstate(&epoch0, val.state()).unwrap(); - - op_dag.lower_all().unwrap(); - - let (mut t_dag, res) = TDag::from_op_dag(&mut op_dag); - res.unwrap(); + { + for i in 0..16 { + awi::assert_eq!(i, val.eval().unwrap().to_usize()); + epoch0.drive_loops().unwrap(); + } + } + drop(epoch0); +} - t_dag.verify_integrity().unwrap(); +#[test] +fn loop_net() { + let epoch0 = Epoch::new(); + let mut net = Net::zero(bw(4)); + net.push(&awi!(0xa_u4)).unwrap(); + net.push(&awi!(0xb_u4)).unwrap(); + net.push(&awi!(0xc_u4)).unwrap(); + net.push(&awi!(0xd_u4)).unwrap(); + let val = EvalAwi::from(&net); + let inx = LazyAwi::opaque(bw(64)); + net.drive(inx.to_usize()).unwrap(); - t_dag.eval_all().unwrap(); + { + use awi::{assert_eq, *}; + inx.retro_(&awi!(0_u64)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(val.eval().unwrap(), awi!(0xa_u4)); - t_dag.optimize_basic(); + inx.retro_(&awi!(2_u64)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(val.eval().unwrap(), awi!(0xc_u4)); - for i in 0..16 { - std::assert_eq!(i, t_dag.get_noted_as_extawi(p_val).unwrap().to_usize()); + inx.retro_(&awi!(1_u64)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(val.eval().unwrap(), awi!(0xb_u4)); - t_dag.drive_loops(); - t_dag.eval_all().unwrap(); + inx.retro_(&awi!(2_u64)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(val.eval().unwrap(), awi!(0xd_u4)); } + drop(epoch0); } -*/ From 4bd23486edbee2d6dd4acf87b0c7c51f6e2a0925 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sun, 10 Dec 2023 20:58:29 -0600 Subject: [PATCH 56/62] fix bad drop bug --- starlight/src/awi_structs/epoch.rs | 13 +++++++++++++ starlight/src/awi_structs/eval_awi.rs | 7 ++++++- starlight/src/awi_structs/lazy_awi.rs | 5 +++++ starlight/src/awi_structs/temporal.rs | 5 ++--- starlight/src/lib.rs | 2 +- starlight/src/lower.rs | 2 +- testcrate/tests/loop.rs | 2 +- 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 1930f179..86f4e01b 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -64,6 +64,19 @@ impl Drop for EpochData { fn drop(&mut self) { // prevent invoking recursive panics and a buffer overrun if !panicking() { + // if `responsible_for` is not empty, then this `EpochData` is probably being + // dropped in a special case like a panic (I have `panicking` guards on all the + // impls, but it seems that in some cases that for some reason a panic on unwrap + // can start dropping `EpochData`s before the `Epoch`s, and there are + // arbitrarily bad interactions so we always need to forget any `EvalAwi`s here) + // in which the `Epoch` is not going to be useful anyway. We need to + // `mem::forget` just the `EvalAwi`s of the assertions + for (_, mut shared) in self.responsible_for.drain() { + for eval_awi in shared.assertions.bits.drain(..) { + // avoid the `EvalAwi` drop code trying to access recursively + mem::forget(eval_awi); + } + } self.epoch_key.pop_off_epoch_stack(); } } diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index 36f7715c..f8428a13 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -11,6 +11,11 @@ use crate::{ epoch::get_current_epoch, }; +// Note: `mem::forget` can be used on `EvalAwi`s, but in this crate it should +// only be done in special cases like if a `EpochShared` is being force dropped +// by a panic or something that would necessitate giving up on `Epoch` +// invariants anyway + /// When created from a type implementing `AsRef`, it can later be /// used to evaluate its dynamic value. /// @@ -19,7 +24,7 @@ use crate::{ /// # Custom Drop /// /// Upon being dropped, this will remove special references being kept by the -/// current `Epoch` +/// current `Epoch`. pub struct EvalAwi { p_state: PState, p_note: PNote, diff --git a/starlight/src/awi_structs/lazy_awi.rs b/starlight/src/awi_structs/lazy_awi.rs index 045740af..45457182 100644 --- a/starlight/src/awi_structs/lazy_awi.rs +++ b/starlight/src/awi_structs/lazy_awi.rs @@ -20,6 +20,11 @@ use crate::{ // do not implement `Clone` for this, we would need a separate `LazyCellAwi` // type +// Note: `mem::forget` can be used on `LazyAwi`s, but in this crate it should +// only be done in special cases like if a `EpochShared` is being force dropped +// by a panic or something that would necessitate giving up on `Epoch` +// invariants anyway + /// When other mimicking types are created from a reference of this, `retro_` /// can later be called to retroactively change the input values of the DAG. /// diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index b357c2a1..eb8c6259 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -186,7 +186,7 @@ impl Net { /// If `inx` is out of range, the return value is a runtime or dynamic /// `None`. #[must_use] - pub fn drive(mut self, inx: impl Into) -> dag::Option<()> { + pub fn drive(self, inx: impl Into) -> dag::Option<()> { if self.is_empty() { return dag::Option::None; } @@ -207,8 +207,7 @@ impl Net { let mut tmp = Awi::zero(self.nzbw()); for i in 0..self.len() { - tmp.mux_(self.get_mut(i).unwrap(), signals[i].to_bool()) - .unwrap(); + tmp.mux_(&self.ports[i], signals[i].to_bool()).unwrap(); } self.source.drive(&tmp).unwrap(); dag::Option::some_at_dagtime((), in_range) diff --git a/starlight/src/lib.rs b/starlight/src/lib.rs index ee7e17a8..2b0a8788 100644 --- a/starlight/src/lib.rs +++ b/starlight/src/lib.rs @@ -163,7 +163,7 @@ mod awi_structs; /// Internals used by this crate to deal with states and TNode DAGs pub mod ensemble; -pub(crate) mod lower; +pub mod lower; mod misc; pub use awi_structs::{epoch, Assertions, Epoch, EvalAwi, LazyAwi, LazyInlAwi, Loop, Net}; #[cfg(feature = "debug")] diff --git a/starlight/src/lower.rs b/starlight/src/lower.rs index 41f8408e..22a7915a 100644 --- a/starlight/src/lower.rs +++ b/starlight/src/lower.rs @@ -1,5 +1,5 @@ mod lower_op; mod lower_state; -pub(crate) mod meta; +pub mod meta; pub use lower_op::{lower_op, LowerManagement}; diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index 23acea91..8584b2f8 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -70,7 +70,7 @@ fn loop_net() { epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xb_u4)); - inx.retro_(&awi!(2_u64)).unwrap(); + inx.retro_(&awi!(3_u64)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xd_u4)); } From c50c6f4c54cbec898a932e8295ff21d7c00baabc Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Sun, 10 Dec 2023 22:05:36 -0600 Subject: [PATCH 57/62] temporal fixes --- starlight/src/awi_structs/epoch.rs | 14 +++++--------- starlight/src/awi_structs/temporal.rs | 28 ++++++++++++++++++++------- testcrate/tests/loop.rs | 14 ++++++++------ 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 86f4e01b..0650c712 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -579,15 +579,11 @@ impl Epoch { // second do all loopback changes let mut lock = epoch_shared.epoch_data.borrow_mut(); let mut adv = lock.ensemble.lnodes.advancer(); - loop { - if let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { - let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); - let val = lock.ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; - let p_self = lnode.p_self; - lock.ensemble.change_value(p_self, val).unwrap(); - } else { - break - } + while let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { + let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); + let val = lock.ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; + let p_self = lnode.p_self; + lock.ensemble.change_value(p_self, val).unwrap(); } Ok(()) } diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index eb8c6259..25d8be69 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -186,22 +186,36 @@ impl Net { /// If `inx` is out of range, the return value is a runtime or dynamic /// `None`. #[must_use] - pub fn drive(self, inx: impl Into) -> dag::Option<()> { + pub fn drive(self, inx: &Bits) -> dag::Option<()> { if self.is_empty() { return dag::Option::None; } + if self.len() == 1 { + self.source.drive(&self.ports[0]).unwrap(); + return dag::Option::some_at_dagtime((), inx.is_zero()); + } let max_inx = self.len() - 1; - let max_inx_bits = max_inx.next_power_of_two().trailing_zeros() as usize; - let inx = InlAwi::from_usize(inx.into()); + let max_inx_bits = self.len().next_power_of_two().trailing_zeros() as usize; // we detect overflow by seeing if any of these bits are nonzero or if the rest // of the index is greater than the expected max bits (only needed if the // self.len() is not a power of two) - let should_stay_zero = awi!(inx[max_inx_bits..]).unwrap(); + let should_stay_zero = if max_inx_bits < inx.bw() { + awi!(inx[max_inx_bits..]).unwrap() + } else { + awi!(0) + }; let mut in_range = should_stay_zero.is_zero(); - let inx = awi!(inx[..max_inx_bits]).unwrap(); + let inx = if max_inx_bits < inx.bw() { + awi!(inx[..max_inx_bits]).unwrap() + } else { + Awi::from(inx) + }; let signals = selector(&inx, None); - if !self.len().is_power_of_two() { - let le = inx.ule(&InlAwi::from_usize(max_inx)).unwrap(); + if (!self.len().is_power_of_two()) && (inx.bw() == max_inx_bits) { + // dance to avoid stuff that can get lowered into a full `BITS` sized comparison + let mut max = Awi::zero(inx.nzbw()); + max.usize_(max_inx); + let le = inx.ule(&max).unwrap(); in_range &= le; } diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index 8584b2f8..9b41ce1d 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -53,24 +53,26 @@ fn loop_net() { net.push(&awi!(0xc_u4)).unwrap(); net.push(&awi!(0xd_u4)).unwrap(); let val = EvalAwi::from(&net); - let inx = LazyAwi::opaque(bw(64)); - net.drive(inx.to_usize()).unwrap(); + let inx = LazyAwi::opaque(bw(2)); + net.drive(&inx).unwrap(); { use awi::{assert_eq, *}; - inx.retro_(&awi!(0_u64)).unwrap(); + // TODO + epoch0.lower().unwrap(); + inx.retro_(&awi!(0_u2)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xa_u4)); - inx.retro_(&awi!(2_u64)).unwrap(); + inx.retro_(&awi!(2_u2)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xc_u4)); - inx.retro_(&awi!(1_u64)).unwrap(); + inx.retro_(&awi!(1_u2)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xb_u4)); - inx.retro_(&awi!(3_u64)).unwrap(); + inx.retro_(&awi!(3_u2)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xd_u4)); } From 241f698f8fffcd29e65bffbf9b00ed002751c631 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Mon, 11 Dec 2023 13:54:36 -0600 Subject: [PATCH 58/62] fix test --- starlight/src/awi_structs/epoch.rs | 2 ++ starlight/src/awi_structs/temporal.rs | 6 ++++++ starlight/src/ensemble/state.rs | 14 +++++--------- starlight/src/ensemble/value.rs | 2 +- testcrate/tests/loop.rs | 2 -- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 0650c712..45721c95 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -561,6 +561,8 @@ impl Epoch { if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { return Err(EvalError::OtherStr("epoch is not the current epoch")) } + // `Loop`s register states to lower so that the below loops can find them + Ensemble::handle_requests(&epoch_shared)?; // first evaluate all loop drivers let lock = epoch_shared.epoch_data.borrow(); let mut adv = lock.ensemble.lnodes.advancer(); diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index 25d8be69..0a64645a 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -68,6 +68,12 @@ impl Loop { .get_mut(driver.state()) .unwrap() .inc_rc(); + // in order for loop driving to always work we need to do this (otherwise + // `drive_loops` would have to search all states) + lock.ensemble + .stator + .states_to_lower + .push(self.source.state()); Some(()) } } diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 0259be26..595264ef 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -254,6 +254,10 @@ impl Ensemble { } else { let p_next = ops[i]; if self.stator.states[p_next].lowered_to_tnodes { + // in the case of circular cases with `Loop`s, if the DFS goes around and does + // not encounter a root, the argument needs to be initialized or else any branch + // of `lower_elementary_to_tnodes_intermediate` could fail + self.initialize_state_bits_if_needed(p_next).unwrap(); // do not visit path.last_mut().unwrap().0 += 1; } else { @@ -305,11 +309,11 @@ fn lower_elementary_to_tnodes_intermediate( this: &mut Ensemble, p_state: PState, ) -> Result<(), EvalError> { + this.initialize_state_bits_if_needed(p_state).unwrap(); match this.stator.states[p_state].op { Assert([x]) => { // this is the only foolproof way of doing this, at least without more // branches - this.initialize_state_bits_if_needed(p_state).unwrap(); let len = this.stator.states[p_state].p_self_bits.len(); assert_eq!(len, this.stator.states[x].p_self_bits.len()); for i in 0..len { @@ -321,7 +325,6 @@ fn lower_elementary_to_tnodes_intermediate( Copy([x]) => { // this is the only foolproof way of doing this, at least without more // branches - this.initialize_state_bits_if_needed(p_state).unwrap(); let len = this.stator.states[p_state].p_self_bits.len(); assert_eq!(len, this.stator.states[x].p_self_bits.len()); for i in 0..len { @@ -331,7 +334,6 @@ fn lower_elementary_to_tnodes_intermediate( } } StaticGet([bits], inx) => { - this.initialize_state_bits_if_needed(p_state).unwrap(); let len = this.stator.states[bits].p_self_bits.len(); assert!(inx < len); let p_self_bits = &this.stator.states[p_state].p_self_bits; @@ -342,7 +344,6 @@ fn lower_elementary_to_tnodes_intermediate( } Concat(ref concat) => { let concat_len = concat.len(); - this.initialize_state_bits_if_needed(p_state).unwrap(); let total_len = this.stator.states[p_state].p_self_bits.len(); let mut to = 0; for c_i in 0..concat_len { @@ -363,7 +364,6 @@ fn lower_elementary_to_tnodes_intermediate( } ConcatFields(ref concat) => { let concat_len = concat.len(); - this.initialize_state_bits_if_needed(p_state).unwrap(); let total_len = this.stator.states[p_state].p_self_bits.len(); let mut to = 0; for c_i in 0..concat_len { @@ -384,7 +384,6 @@ fn lower_elementary_to_tnodes_intermediate( assert_eq!(total_len, to); } Repeat([x]) => { - this.initialize_state_bits_if_needed(p_state).unwrap(); let len = this.stator.states[p_state].p_self_bits.len(); let x_w = this.stator.states[x].p_self_bits.len(); assert!((len % x_w) == 0); @@ -402,7 +401,6 @@ fn lower_elementary_to_tnodes_intermediate( StaticLut(ref concat, ref table) => { let table = table.clone(); let concat_len = concat.len(); - this.initialize_state_bits_if_needed(p_state).unwrap(); let mut inx_bits: SmallVec<[Option; 8]> = smallvec![]; for c_i in 0..concat_len { let c = if let StaticLut(ref concat, _) = this.stator.states[p_state].op { @@ -414,7 +412,6 @@ fn lower_elementary_to_tnodes_intermediate( inx_bits.extend(bits.iter().cloned()); } - this.initialize_state_bits_if_needed(p_state).unwrap(); let inx_len = inx_bits.len(); let out_bw = this.stator.states[p_state].p_self_bits.len(); let num_entries = 1usize.checked_shl(u32::try_from(inx_len).unwrap()).unwrap(); @@ -445,7 +442,6 @@ fn lower_elementary_to_tnodes_intermediate( return Err(EvalError::OtherStr("cannot lower an undriven `Loop`")) } let p_driver_state = v[0]; - this.initialize_state_bits_if_needed(p_state).unwrap(); let w = this.stator.states[p_state].p_self_bits.len(); if w != this.stator.states[p_driver_state].p_self_bits.len() { return Err(EvalError::OtherStr( diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index fead50f4..8e92077e 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -369,7 +369,7 @@ impl Ensemble { } } - fn handle_requests(epoch_shared: &EpochShared) -> Result<(), EvalError> { + pub(crate) fn handle_requests(epoch_shared: &EpochShared) -> Result<(), EvalError> { // TODO currently, the only way of avoiding N^2 worst case scenarios where // different change cascades lead to large groups of nodes being evaluated // repeatedly, is to use the front strategy. Only a powers of two reduction tree diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index 9b41ce1d..d668d0e7 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -58,8 +58,6 @@ fn loop_net() { { use awi::{assert_eq, *}; - // TODO - epoch0.lower().unwrap(); inx.retro_(&awi!(0_u2)).unwrap(); epoch0.drive_loops().unwrap(); assert_eq!(val.eval().unwrap(), awi!(0xa_u4)); From f53d0f8e587fcd2011e3d6602e106f8b3aa4607d Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Mon, 11 Dec 2023 16:32:08 -0600 Subject: [PATCH 59/62] fix many issues related to `Net`s --- starlight/src/awi_structs/epoch.rs | 6 +- starlight/src/awi_structs/eval_awi.rs | 87 +++++++++++++++++++++------ starlight/src/awi_structs/temporal.rs | 8 ++- starlight/src/ensemble/debug.rs | 14 ++--- starlight/src/ensemble/optimize.rs | 2 +- starlight/src/ensemble/state.rs | 6 ++ starlight/src/ensemble/value.rs | 2 +- testcrate/tests/loop.rs | 82 ++++++++++++++++++++++++- 8 files changed, 170 insertions(+), 37 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 45721c95..2b176b28 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -136,9 +136,7 @@ impl EpochShared { drop(epoch_data); let mut cloned = vec![]; for p_state in states { - if let Some(eval) = EvalAwi::from_state(p_state) { - cloned.push(eval) - } + cloned.push(EvalAwi::from_state(p_state)) } Assertions { bits: cloned } } @@ -371,7 +369,7 @@ pub fn _callback() -> EpochCallback { fn register_assertion_bit(bit: dag::bool, location: Location) { // need a new bit to attach new location data to let new_bit = new_pstate(bw(1), Op::Assert([bit.state()]), Some(location)); - let eval_awi = EvalAwi::from_state(new_bit).unwrap(); + let eval_awi = EvalAwi::from_state(new_bit); // manual to get around closure issue CURRENT_EPOCH.with(|top| { let mut top = top.borrow_mut(); diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index f8428a13..dfefed58 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -62,12 +62,40 @@ impl Lineage for EvalAwi { impl Clone for EvalAwi { /// This makes another note to the same state that `self` pointed to. + #[track_caller] fn clone(&self) -> Self { - Self::from_state(self.p_state).unwrap() + Self::from_state(self.p_state) + } +} + +macro_rules! evalawi_from_impl { + ($($fn:ident $t:ident);*;) => { + $( + #[track_caller] + pub fn $fn(x: dag::$t) -> Self { + Self::from_state(x.state()) + } + )* } } impl EvalAwi { + evalawi_from_impl!( + from_bool bool; + from_u8 u8; + from_i8 i8; + from_u16 u16; + from_i16 i16; + from_u32 u32; + from_i32 i32; + from_u64 u64; + from_i64 i64; + from_u128 u128; + from_i128 i128; + from_usize usize; + from_isize isize; + ); + pub fn nzbw(&self) -> NonZeroUsize { epoch::get_nzbw_from_current_epoch(self.p_state) } @@ -80,21 +108,40 @@ impl EvalAwi { self.p_note } - pub(crate) fn from_state(p_state: PState) -> Option { - let epoch_data = get_current_epoch().unwrap().epoch_data; - let mut lock = epoch_data.borrow_mut(); - let p_note = lock.ensemble.note_pstate(p_state)?; - lock.ensemble - .stator - .states - .get_mut(p_state) - .unwrap() - .inc_extern_rc(); - Some(Self { p_state, p_note }) + /// Used internally to create `EvalAwi`s + /// + /// # Panics + /// + /// If an `Epoch` does not exist or the `PState` was pruned + #[track_caller] + pub fn from_state(p_state: PState) -> Self { + if let Some(epoch) = get_current_epoch() { + let mut lock = epoch.epoch_data.borrow_mut(); + match lock.ensemble.note_pstate(p_state) { + Some(p_note) => { + lock.ensemble + .stator + .states + .get_mut(p_state) + .unwrap() + .inc_extern_rc(); + Self { p_state, p_note } + } + None => { + panic!( + "could not create an `EvalAwi` from the given mimicking state, probably \ + because the state was pruned or came from a different `Epoch`" + ) + } + } + } else { + panic!("attempted to create an `EvalAwi` when no live `Epoch` exists") + } } - /// Can return `None` if the state has been pruned - pub fn from_bits(bits: &dag::Bits) -> Option { + /// Can panic if the state has been pruned + #[track_caller] + pub fn from_bits(bits: &dag::Bits) -> Self { Self::from_state(bits.state()) } @@ -122,23 +169,23 @@ impl EvalAwi { } pub fn zero(w: NonZeroUsize) -> Self { - Self::from_bits(&dag::Awi::zero(w)).unwrap() + Self::from_bits(&dag::Awi::zero(w)) } pub fn umax(w: NonZeroUsize) -> Self { - Self::from_bits(&dag::Awi::umax(w)).unwrap() + Self::from_bits(&dag::Awi::umax(w)) } pub fn imax(w: NonZeroUsize) -> Self { - Self::from_bits(&dag::Awi::imax(w)).unwrap() + Self::from_bits(&dag::Awi::imax(w)) } pub fn imin(w: NonZeroUsize) -> Self { - Self::from_bits(&dag::Awi::imin(w)).unwrap() + Self::from_bits(&dag::Awi::imin(w)) } pub fn uone(w: NonZeroUsize) -> Self { - Self::from_bits(&dag::Awi::uone(w)).unwrap() + Self::from_bits(&dag::Awi::uone(w)) } } @@ -163,6 +210,6 @@ forward_debug_fmt!(EvalAwi); impl> From for EvalAwi { #[track_caller] fn from(b: B) -> Self { - Self::from_bits(b.as_ref()).unwrap() + Self::from_bits(b.as_ref()) } } diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index 0a64645a..b1564433 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, num::NonZeroUsize, ops::Deref}; +use std::{borrow::Borrow, cmp::min, num::NonZeroUsize, ops::Deref}; use awint::{ awint_dag::{smallvec::smallvec, Lineage, Op}, @@ -190,7 +190,9 @@ impl Net { /// a dynamic `dag::usize`. /// /// If `inx` is out of range, the return value is a runtime or dynamic - /// `None`. + /// `None`. The source value if the `inx` is out of range is not specified, + /// and it may result in an undriven `Loop` in some cases, so the return + /// `Option` should probably be `unwrap`ed. #[must_use] pub fn drive(self, inx: &Bits) -> dag::Option<()> { if self.is_empty() { @@ -226,7 +228,7 @@ impl Net { } let mut tmp = Awi::zero(self.nzbw()); - for i in 0..self.len() { + for i in 0..min(self.len(), signals.len()) { tmp.mux_(&self.ports[i], signals[i].to_bool()).unwrap(); } self.source.drive(&tmp).unwrap(); diff --git a/starlight/src/ensemble/debug.rs b/starlight/src/ensemble/debug.rs index f1370dae..f5dbe3bc 100644 --- a/starlight/src/ensemble/debug.rs +++ b/starlight/src/ensemble/debug.rs @@ -105,19 +105,19 @@ impl DebugNodeTrait for NodeKind { fn debug_node(p_this: PBack, this: &Self) -> DebugNode { match this { NodeKind::StateBit(state_bit) => DebugNode { - sources: { + sources: vec![], + center: { + let mut v = vec![format!("{:?}", p_this)]; + v.push(format!("{} [{}]", state_bit.p_state, state_bit.i)); + v + }, + sinks: { if let Some(p_equiv) = state_bit.p_equiv { vec![(p_equiv, "".to_string())] } else { vec![] } }, - center: { - let mut v = vec![format!("{:?}", p_this)]; - v.push(format!("{} [{}]", state_bit.p_state, state_bit.i)); - v - }, - sinks: vec![], }, NodeKind::TNode(tnode) => DebugNode { sources: tnode diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index ae0e5144..beea86af 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -352,7 +352,7 @@ impl Ensemble { Referent::ThisTNode(p_tnode) => { self.remove_tnode_not_p_self(*p_tnode); } - Referent::LoopDriver(p_lnode) => { + Referent::ThisLNode(p_lnode) => { self.remove_lnode_not_p_self(*p_lnode); } _ => unreachable!(), diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index 595264ef..ab8aec26 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -231,6 +231,12 @@ impl Ensemble { } Opaque(_, name) => { if let Some(name) = name { + if name == "LoopSource" { + return Err(EvalError::OtherString(format!( + "cannot lower LoopSource opaque with no driver, most likely \ + some `Loop` or `Net` has been left undriven" + ))) + } return Err(EvalError::OtherString(format!( "cannot lower root opaque with name {name}" ))) diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 8e92077e..e4a15ec5 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -316,7 +316,7 @@ impl Ensemble { pub fn change_value(&mut self, p_back: PBack, value: Value) -> Option<()> { if let Some(equiv) = self.backrefs.get_val_mut(p_back) { - if equiv.val.is_const() { + if equiv.val.is_const() && (equiv.val != value) { // not allowed panic!(); } diff --git a/testcrate/tests/loop.rs b/testcrate/tests/loop.rs index d668d0e7..3a1f2155 100644 --- a/testcrate/tests/loop.rs +++ b/testcrate/tests/loop.rs @@ -1,3 +1,5 @@ +use std::num::NonZeroUsize; + use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi, Loop}; #[test] @@ -45,7 +47,7 @@ fn loop_incrementer() { } #[test] -fn loop_net() { +fn loop_net4() { let epoch0 = Epoch::new(); let mut net = Net::zero(bw(4)); net.push(&awi!(0xa_u4)).unwrap(); @@ -76,3 +78,81 @@ fn loop_net() { } drop(epoch0); } + +fn exhaustive_net_test(epoch0: &Epoch, num_ports: awi::usize, diff: awi::isize) { + let mut net = Net::zero(bw(5)); + for i in 0..num_ports { + let mut port = awi!(0u5); + port.usize_(i); + net.push(&port).unwrap(); + } + let min_w = num_ports.next_power_of_two().trailing_zeros() as awi::usize; + let w = NonZeroUsize::new((min_w as awi::isize + diff) as awi::usize).unwrap(); + let lazy = LazyAwi::opaque(w); + let eval_net = EvalAwi::from(&net); + let res = net.drive(&lazy); + let eval_res = EvalAwi::from_bool(res.is_none()); + { + use awi::*; + epoch0.optimize().unwrap(); + for i in 0..(1 << w.get()) { + let mut inx = Awi::zero(w); + inx.usize_(i); + lazy.retro_(&inx).unwrap(); + epoch0.drive_loops().unwrap(); + awi::assert_eq!(eval_res.eval().unwrap().to_bool(), i >= num_ports); + if i < num_ports { + awi::assert_eq!(eval_net.eval().unwrap().to_usize(), i); + } + } + } +} + +#[test] +fn loop_net_no_ports() { + let epoch0 = Epoch::new(); + // done separately because it results in an undriven `Loop` + { + let net = Net::zero(bw(5)); + let res = net.drive(&awi!(0)); + { + use awi::assert; + // always none + assert!(res.is_none_at_runtime()); + } + } + drop(epoch0); +} + +#[test] +fn loop_net() { + let epoch0 = Epoch::new(); + // one port + { + let mut net = Net::zero(bw(5)); + net.push(&awi!(0xa_u5)).unwrap(); + let lazy = LazyAwi::opaque(bw(1)); + let eval_net = EvalAwi::from(&net); + let res = net.drive(&lazy); + let eval_res = EvalAwi::from_bool(res.is_none()); + { + use awi::{assert_eq, *}; + lazy.retro_(&awi!(0)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(eval_res.eval().unwrap(), awi!(0)); + assert_eq!(eval_net.eval().unwrap(), awi!(0xa_u5)); + // any nonzero index always returns a `None` from the function + lazy.retro_(&awi!(1)).unwrap(); + epoch0.drive_loops().unwrap(); + assert_eq!(eval_res.eval().unwrap(), awi!(1)); + } + } + for num_ports in 3..17 { + // test with index size one less than needed to index all ports + exhaustive_net_test(&epoch0, num_ports, -1); + exhaustive_net_test(&epoch0, num_ports, 0); + exhaustive_net_test(&epoch0, num_ports, 1); + } + + drop(epoch0); +} From 777bab34bbf682b97bcacacbd6be343dba6cebf8 Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Mon, 11 Dec 2023 17:28:53 -0600 Subject: [PATCH 60/62] prune all states in optimization --- starlight/src/awi_structs/epoch.rs | 1 + starlight/src/awi_structs/eval_awi.rs | 13 +++++-------- starlight/src/ensemble/optimize.rs | 2 ++ starlight/src/ensemble/state.rs | 6 +++--- starlight/src/ensemble/together.rs | 11 +++++++++++ starlight/src/ensemble/value.rs | 27 ++++++++++++++------------- testcrate/tests/stats.rs | 4 ++-- 7 files changed, 38 insertions(+), 26 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index 2b176b28..d7d711a6 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -540,6 +540,7 @@ impl Epoch { Ok(()) } + /// Runs optimization including lowering then pruning all states. pub fn optimize(&self) -> Result<(), EvalError> { let epoch_shared = get_current_epoch().unwrap(); if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { diff --git a/starlight/src/awi_structs/eval_awi.rs b/starlight/src/awi_structs/eval_awi.rs index dfefed58..cb082352 100644 --- a/starlight/src/awi_structs/eval_awi.rs +++ b/starlight/src/awi_structs/eval_awi.rs @@ -1,7 +1,7 @@ use std::{fmt, num::NonZeroUsize, thread::panicking}; use awint::{ - awint_dag::{dag, epoch, EvalError, Lineage, PState}, + awint_dag::{dag, EvalError, Lineage, PState}, awint_internals::forward_debug_fmt, }; @@ -42,12 +42,9 @@ impl Drop for EvalAwi { "most likely, an `EvalAwi` created in one `Epoch` was dropped in another" ) } - lock.ensemble - .stator - .states - .get_mut(self.p_state) - .unwrap() - .dec_extern_rc(); + if let Some(state) = lock.ensemble.stator.states.get_mut(self.p_state) { + state.dec_extern_rc(); + } } // else the epoch has been dropped } @@ -97,7 +94,7 @@ impl EvalAwi { ); pub fn nzbw(&self) -> NonZeroUsize { - epoch::get_nzbw_from_current_epoch(self.p_state) + Ensemble::get_thread_local_note_nzbw(self.p_note).unwrap() } pub fn bw(&self) -> usize { diff --git a/starlight/src/ensemble/optimize.rs b/starlight/src/ensemble/optimize.rs index beea86af..53a7286c 100644 --- a/starlight/src/ensemble/optimize.rs +++ b/starlight/src/ensemble/optimize.rs @@ -311,7 +311,9 @@ impl Ensemble { self.backrefs.remove_key(lnode.p_driver).unwrap(); } + /// Also removes all states pub fn optimize_all(&mut self) { + self.force_remove_all_states().unwrap(); // need to preinvestigate everything before starting a priority loop let mut adv = self.backrefs.advancer(); while let Some(p_back) = adv.advance(&self.backrefs) { diff --git a/starlight/src/ensemble/state.rs b/starlight/src/ensemble/state.rs index ab8aec26..ff776336 100644 --- a/starlight/src/ensemble/state.rs +++ b/starlight/src/ensemble/state.rs @@ -232,10 +232,10 @@ impl Ensemble { Opaque(_, name) => { if let Some(name) = name { if name == "LoopSource" { - return Err(EvalError::OtherString(format!( + return Err(EvalError::OtherStr( "cannot lower LoopSource opaque with no driver, most likely \ - some `Loop` or `Net` has been left undriven" - ))) + some `Loop` or `Net` has been left undriven", + )) } return Err(EvalError::OtherString(format!( "cannot lower root opaque with name {name}" diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index 0b9c6048..8b52be10 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -556,6 +556,17 @@ impl Ensemble { } Ok(()) } + + pub fn force_remove_all_states(&mut self) -> Result<(), EvalError> { + for (_, mut state) in self.stator.states.drain() { + for p_self_state in state.p_self_bits.drain(..) { + if let Some(p_self_state) = p_self_state { + self.backrefs.remove_key(p_self_state).unwrap(); + } + } + } + Ok(()) + } } impl Default for Ensemble { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index e4a15ec5..0838357b 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -381,21 +381,22 @@ impl Ensemble { loop { let mut lock = epoch_shared.epoch_data.borrow_mut(); if let Some(p_state) = lock.ensemble.stator.states_to_lower.pop() { - let state = &lock.ensemble.stator.states[p_state]; - // first check that it has not already been lowered - if !state.lowered_to_tnodes { - drop(lock); - Ensemble::dfs_lower(epoch_shared, p_state)?; - let mut lock = epoch_shared.epoch_data.borrow_mut(); - // reinvestigate - let len = lock.ensemble.stator.states[p_state].p_self_bits.len(); - for i in 0..len { - let p_bit = lock.ensemble.stator.states[p_state].p_self_bits[i]; - if let Some(p_bit) = p_bit { - lock.ensemble.evaluator.insert(Eval::Investigate0(0, p_bit)); + if let Some(state) = lock.ensemble.stator.states.get(p_state) { + // first check that it has not already been lowered + if !state.lowered_to_tnodes { + drop(lock); + Ensemble::dfs_lower(epoch_shared, p_state)?; + let mut lock = epoch_shared.epoch_data.borrow_mut(); + // reinvestigate + let len = lock.ensemble.stator.states[p_state].p_self_bits.len(); + for i in 0..len { + let p_bit = lock.ensemble.stator.states[p_state].p_self_bits[i]; + if let Some(p_bit) = p_bit { + lock.ensemble.evaluator.insert(Eval::Investigate0(0, p_bit)); + } } + drop(lock); } - drop(lock); } } else { break diff --git a/testcrate/tests/stats.rs b/testcrate/tests/stats.rs index 4d9cba6d..f8abba32 100644 --- a/testcrate/tests/stats.rs +++ b/testcrate/tests/stats.rs @@ -21,7 +21,7 @@ fn stats_optimize_funnel() { epoch0.optimize().unwrap(); epoch0.assert_assertions().unwrap(); let ensemble = epoch0.ensemble(); - awi::assert_eq!(ensemble.stator.states.len(), 2436); - awi::assert_eq!(ensemble.backrefs.len_keys(), 8319); + awi::assert_eq!(ensemble.stator.states.len(), 0); + awi::assert_eq!(ensemble.backrefs.len_keys(), 5818); awi::assert_eq!(ensemble.backrefs.len_vals(), 1237); } From d5485a544408cac8bf37382f216b282daa136eae Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Mon, 11 Dec 2023 19:37:44 -0600 Subject: [PATCH 61/62] minor perf improvement --- starlight/src/awi_structs/epoch.rs | 93 +++++++++++++++++++++--------- starlight/src/ensemble/note.rs | 13 ++++- starlight/src/ensemble/together.rs | 6 ++ starlight/src/ensemble/value.rs | 41 ++++++++++++- testcrate/benches/bench.rs | 37 +++++++++++- 5 files changed, 157 insertions(+), 33 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index d7d711a6..a6e5c1c0 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -308,6 +308,62 @@ impl EpochShared { } }); } + + fn internal_drive_loops_with_lower_capability(&self) -> Result<(), EvalError> { + // `Loop`s register states to lower so that the below loops can find them + Ensemble::handle_requests_with_lower_capability(self)?; + // first evaluate all loop drivers + let lock = self.epoch_data.borrow(); + let mut adv = lock.ensemble.lnodes.advancer(); + drop(lock); + loop { + let lock = self.epoch_data.borrow(); + if let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { + let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); + let p_driver = lnode.p_driver; + drop(lock); + Ensemble::calculate_value_with_lower_capability(&self, p_driver)?; + } else { + break + } + } + // second do all loopback changes + let mut lock = self.epoch_data.borrow_mut(); + let mut adv = lock.ensemble.lnodes.advancer(); + while let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { + let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); + let val = lock.ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; + let p_self = lnode.p_self; + lock.ensemble.change_value(p_self, val).unwrap(); + } + Ok(()) + } + + fn internal_drive_loops(&self) -> Result<(), EvalError> { + // first evaluate all loop drivers + let mut lock = self.epoch_data.borrow_mut(); + let ensemble = &mut lock.ensemble; + + let mut adv = ensemble.lnodes.advancer(); + loop { + if let Some(p_lnode) = adv.advance(&ensemble.lnodes) { + let lnode = ensemble.lnodes.get(p_lnode).unwrap(); + let p_driver = lnode.p_driver; + ensemble.calculate_value(p_driver)?; + } else { + break + } + } + // second do all loopback changes + let mut adv = ensemble.lnodes.advancer(); + while let Some(p_lnode) = adv.advance(&ensemble.lnodes) { + let lnode = ensemble.lnodes.get(p_lnode).unwrap(); + let val = ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; + let p_self = lnode.p_self; + ensemble.change_value(p_self, val).unwrap(); + } + Ok(()) + } } thread_local!( @@ -560,32 +616,17 @@ impl Epoch { if !Rc::ptr_eq(&epoch_shared.epoch_data, &self.shared.epoch_data) { return Err(EvalError::OtherStr("epoch is not the current epoch")) } - // `Loop`s register states to lower so that the below loops can find them - Ensemble::handle_requests(&epoch_shared)?; - // first evaluate all loop drivers - let lock = epoch_shared.epoch_data.borrow(); - let mut adv = lock.ensemble.lnodes.advancer(); - drop(lock); - loop { - let lock = epoch_shared.epoch_data.borrow(); - if let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { - let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); - let p_driver = lnode.p_driver; - drop(lock); - Ensemble::calculate_value(&epoch_shared, p_driver)?; - } else { - break - } - } - // second do all loopback changes - let mut lock = epoch_shared.epoch_data.borrow_mut(); - let mut adv = lock.ensemble.lnodes.advancer(); - while let Some(p_lnode) = adv.advance(&lock.ensemble.lnodes) { - let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); - let val = lock.ensemble.backrefs.get_val(lnode.p_driver).unwrap().val; - let p_self = lnode.p_self; - lock.ensemble.change_value(p_self, val).unwrap(); + if epoch_shared + .epoch_data + .borrow() + .ensemble + .stator + .states + .is_empty() + { + epoch_shared.internal_drive_loops() + } else { + epoch_shared.internal_drive_loops_with_lower_capability() } - Ok(()) } } diff --git a/starlight/src/ensemble/note.rs b/starlight/src/ensemble/note.rs index 519b0978..17d64e56 100644 --- a/starlight/src/ensemble/note.rs +++ b/starlight/src/ensemble/note.rs @@ -109,7 +109,9 @@ impl Ensemble { let ensemble = &mut lock.ensemble; let p_back = if let Some(note) = ensemble.notes.get(p_note) { if bit_i >= note.bits.len() { - return Err(EvalError::WrongBitwidth); + return Err(EvalError::OtherStr( + "something went wrong with note bitwidth", + )); } if let Some(p_back) = note.bits[bit_i] { p_back @@ -121,8 +123,13 @@ impl Ensemble { } else { return Err(EvalError::OtherStr("could not find thread local `Note`")) }; - drop(lock); - Ensemble::calculate_value(&epoch_shared, p_back) + if ensemble.stator.states.is_empty() { + // optimization after total pruning from `optimization` + ensemble.calculate_value(p_back) + } else { + drop(lock); + Ensemble::calculate_value_with_lower_capability(&epoch_shared, p_back) + } } } diff --git a/starlight/src/ensemble/together.rs b/starlight/src/ensemble/together.rs index 8b52be10..e5c61080 100644 --- a/starlight/src/ensemble/together.rs +++ b/starlight/src/ensemble/together.rs @@ -67,6 +67,7 @@ pub struct Ensemble { pub lnodes: Arena, pub evaluator: Evaluator, pub optimizer: Optimizer, + pub debug_counter: u64, } impl Ensemble { @@ -79,6 +80,7 @@ impl Ensemble { lnodes: Arena::new(), evaluator: Evaluator::new(), optimizer: Optimizer::new(), + debug_counter: 0, } } @@ -567,6 +569,10 @@ impl Ensemble { } Ok(()) } + + pub fn inc_debug_counter(&mut self) { + self.debug_counter = self.debug_counter.checked_add(1).unwrap() + } } impl Default for Ensemble { diff --git a/starlight/src/ensemble/value.rs b/starlight/src/ensemble/value.rs index 0838357b..1056c6fd 100644 --- a/starlight/src/ensemble/value.rs +++ b/starlight/src/ensemble/value.rs @@ -333,7 +333,10 @@ impl Ensemble { } } - pub fn calculate_value(epoch_shared: &EpochShared, p_back: PBack) -> Result { + pub fn calculate_value_with_lower_capability( + epoch_shared: &EpochShared, + p_back: PBack, + ) -> Result { let mut lock = epoch_shared.epoch_data.borrow_mut(); let ensemble = &mut lock.ensemble; if let Some(equiv) = ensemble.backrefs.get_val_mut(p_back) { @@ -352,7 +355,7 @@ impl Ensemble { .evaluator .insert(Eval::Investigate0(0, equiv.p_self_equiv)); drop(lock); - Ensemble::handle_requests(epoch_shared)?; + Ensemble::handle_requests_with_lower_capability(epoch_shared)?; } else { drop(lock); } @@ -369,7 +372,32 @@ impl Ensemble { } } - pub(crate) fn handle_requests(epoch_shared: &EpochShared) -> Result<(), EvalError> { + pub fn calculate_value(&mut self, p_back: PBack) -> Result { + if let Some(equiv) = self.backrefs.get_val_mut(p_back) { + if equiv.val.is_const() { + return Ok(equiv.val) + } + // switch to request phase if not already + if self.evaluator.phase != EvalPhase::Request { + self.evaluator.phase = EvalPhase::Request; + self.evaluator.next_request_visit_gen(); + } + let visit = self.evaluator.request_visit_gen(); + if equiv.request_visit != visit { + equiv.request_visit = visit; + self.evaluator + .insert(Eval::Investigate0(0, equiv.p_self_equiv)); + self.handle_requests()?; + } + Ok(self.backrefs.get_val(p_back).unwrap().val) + } else { + Err(EvalError::InvalidPtr) + } + } + + pub(crate) fn handle_requests_with_lower_capability( + epoch_shared: &EpochShared, + ) -> Result<(), EvalError> { // TODO currently, the only way of avoiding N^2 worst case scenarios where // different change cascades lead to large groups of nodes being evaluated // repeatedly, is to use the front strategy. Only a powers of two reduction tree @@ -418,6 +446,13 @@ impl Ensemble { Ok(()) } + pub(crate) fn handle_requests(&mut self) -> Result<(), EvalError> { + while let Some(p_eval) = self.evaluator.evaluations.min() { + self.evaluate(p_eval); + } + Ok(()) + } + fn evaluate(&mut self, p_eval: PEval) { let evaluation = self.evaluator.evaluations.remove(p_eval).unwrap().0; match evaluation { diff --git a/testcrate/benches/bench.rs b/testcrate/benches/bench.rs index 1504cfbd..01a20f60 100644 --- a/testcrate/benches/bench.rs +++ b/testcrate/benches/bench.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use starlight::{dag::*, Epoch, EvalAwi, LazyAwi}; +use starlight::{awi, dag::*, Epoch, EvalAwi, LazyAwi}; use test::Bencher; #[bench] @@ -35,3 +35,38 @@ fn optimize_funnel(bencher: &mut Bencher) { epoch0.assert_assertions().unwrap(); }) } + +#[bench] +fn loop_net(bencher: &mut Bencher) { + let epoch0 = Epoch::new(); + + let num_ports = 16; + let mut net = Net::zero(bw(5)); + for i in 0..num_ports { + let mut port = awi!(0u5); + port.usize_(i); + net.push(&port).unwrap(); + } + let w = bw(4); + let lazy = LazyAwi::opaque(w); + let eval_net = EvalAwi::from(&net); + let res = net.drive(&lazy); + let eval_res = EvalAwi::from_bool(res.is_none()); + { + use awi::*; + epoch0.optimize().unwrap(); + bencher.iter(|| { + for i in 0..(1 << w.get()) { + let mut inx = Awi::zero(w); + inx.usize_(i); + lazy.retro_(&inx).unwrap(); + epoch0.drive_loops().unwrap(); + awi::assert_eq!(eval_res.eval().unwrap().to_bool(), i >= num_ports); + if i < num_ports { + awi::assert_eq!(eval_net.eval().unwrap().to_usize(), i); + } + } + }); + drop(epoch0); + } +} From 2a8fbb9118dfb7b3c7c311f1dc297a810495011c Mon Sep 17 00:00:00 2001 From: Aaron Kutch Date: Mon, 11 Dec 2023 23:25:43 -0600 Subject: [PATCH 62/62] misc --- starlight/src/awi_structs/epoch.rs | 14 +++++-------- starlight/src/awi_structs/temporal.rs | 29 +++++++++++++++++++++++++-- starlight/src/lib.rs | 4 +--- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/starlight/src/awi_structs/epoch.rs b/starlight/src/awi_structs/epoch.rs index a6e5c1c0..28bad472 100644 --- a/starlight/src/awi_structs/epoch.rs +++ b/starlight/src/awi_structs/epoch.rs @@ -322,7 +322,7 @@ impl EpochShared { let lnode = lock.ensemble.lnodes.get(p_lnode).unwrap(); let p_driver = lnode.p_driver; drop(lock); - Ensemble::calculate_value_with_lower_capability(&self, p_driver)?; + Ensemble::calculate_value_with_lower_capability(self, p_driver)?; } else { break } @@ -345,14 +345,10 @@ impl EpochShared { let ensemble = &mut lock.ensemble; let mut adv = ensemble.lnodes.advancer(); - loop { - if let Some(p_lnode) = adv.advance(&ensemble.lnodes) { - let lnode = ensemble.lnodes.get(p_lnode).unwrap(); - let p_driver = lnode.p_driver; - ensemble.calculate_value(p_driver)?; - } else { - break - } + while let Some(p_lnode) = adv.advance(&ensemble.lnodes) { + let lnode = ensemble.lnodes.get(p_lnode).unwrap(); + let p_driver = lnode.p_driver; + ensemble.calculate_value(p_driver)?; } // second do all loopback changes let mut adv = ensemble.lnodes.advancer(); diff --git a/starlight/src/awi_structs/temporal.rs b/starlight/src/awi_structs/temporal.rs index b1564433..8a1d295a 100644 --- a/starlight/src/awi_structs/temporal.rs +++ b/starlight/src/awi_structs/temporal.rs @@ -15,8 +15,33 @@ use crate::{epoch::get_current_epoch, lower::meta::selector}; /// value of the driver and use that to retroactively change the temporal value /// of the loop. /// -/// The fundamental reason for temporal asymmetry is that there needs to be a -/// well defined root evaluation state and value. +/// ``` +/// use starlight::{awi, dag::*, Epoch, EvalAwi, Loop}; +/// let epoch = Epoch::new(); +/// +/// let looper = Loop::zero(bw(4)); +/// // evaluate the value of `looper` at this point later +/// let val = EvalAwi::from(&looper); +/// let mut tmp = awi!(looper); +/// tmp.inc_(true); +/// // drive the `Loop` with itself incremented +/// looper.drive(&tmp).unwrap(); +/// +/// { +/// use awi::*; +/// for i in 0..16 { +/// // check that the evaluated value is equal to +/// // this loop iteration number +/// awi::assert_eq!(i, val.eval().unwrap().to_usize()); +/// // every time `drive_loops` is called, +/// // the evaluated value increases by one +/// epoch.drive_loops().unwrap(); +/// } +/// } +/// drop(epoch); +/// ``` +// The fundamental reason for temporal asymmetry is that there needs to be a +// well defined root evaluation state and value. #[derive(Debug)] // do not implement `Clone`, but maybe implement a `duplicate` function that // explicitly duplicates drivers and loopbacks? pub struct Loop { diff --git a/starlight/src/lib.rs b/starlight/src/lib.rs index 2b0a8788..20504b80 100644 --- a/starlight/src/lib.rs +++ b/starlight/src/lib.rs @@ -2,9 +2,7 @@ //! typical DSL (Domain Specific Language) approach, this allows RTL //! descriptions in ordinary Rust code with all the features that Rust provides. //! -//! This crate is still a WIP, but it currently can describe most combinational -//! logic. The temporal structs (`Loop` and `Net`) need more development before -//! they will work properly. Many optimizations are planned in the near future. +//! This crate still has a considerable amount of WIP stuff //! //! See the documentation of `awint`/`awint_dag` which is used as the backend //! for this.