From d195989ccd69fb581adcdc158bb2a085ad6a9515 Mon Sep 17 00:00:00 2001 From: Kevin Laeufer Date: Mon, 16 Sep 2024 14:31:36 -0400 Subject: [PATCH] [Cider2] use baa as bit-vector library (#2281) This replaces the homespun bit-vector library in Cider with [baa](https://github.com/ekiwi/baa) from crates.io. --- Cargo.lock | 17 +- interp/Cargo.toml | 1 + interp/proptest-regressions/tests/values.txt | 7 + .../src/debugger/commands/command_parser.rs | 9 +- interp/src/errors.rs | 14 +- interp/src/flatten/flat_ir/base.rs | 58 +- interp/src/flatten/primitives/btor2_prim.rs | 13 +- interp/src/flatten/primitives/builder.rs | 20 +- .../src/flatten/primitives/combinational.rs | 414 ++------- interp/src/flatten/primitives/macros.rs | 4 +- interp/src/flatten/primitives/prim_trait.rs | 15 +- .../src/flatten/primitives/stateful/math.rs | 219 +++-- .../flatten/primitives/stateful/memories.rs | 84 +- .../src/flatten/structures/environment/env.rs | 45 +- interp/src/lib.rs | 3 - interp/src/macros.rs | 6 +- interp/src/serialization/formatting.rs | 27 +- interp/src/structures/mod.rs | 1 - interp/src/structures/values.rs | 789 ------------------ interp/src/tests/values.rs | 227 +++-- tools/btor2/btor2i/src/program.rs | 8 +- 21 files changed, 453 insertions(+), 1528 deletions(-) create mode 100644 interp/proptest-regressions/tests/values.txt delete mode 100644 interp/src/structures/mod.rs delete mode 100644 interp/src/structures/values.rs diff --git a/Cargo.lock b/Cargo.lock index af5521b32f..3f5171fb67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "baa" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfafaa7f46ae31982d3e55dad223ebb884a5da626aba403c66673ce9d1831f53" +dependencies = [ + "fraction", + "num-bigint", + "serde", + "smallvec", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -1595,6 +1607,7 @@ version = "0.1.1" dependencies = [ "ahash 0.8.10", "argh", + "baa", "bitvec", "btor2i", "calyx-frontend", @@ -2766,9 +2779,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] diff --git a/interp/Cargo.toml b/interp/Cargo.toml index da6bddfabf..db7d69353b 100644 --- a/interp/Cargo.toml +++ b/interp/Cargo.toml @@ -46,6 +46,7 @@ calyx-frontend = { path = "../calyx-frontend" } btor2i = { path = "../tools/btor2/btor2i" } ciborium = "0.2.2" +baa = { version = "0.6.0", features = ["bigint", "serde1", "fraction1"] } [dev-dependencies] proptest = "1.0.0" diff --git a/interp/proptest-regressions/tests/values.txt b/interp/proptest-regressions/tests/values.txt new file mode 100644 index 0000000000..af8ee0ee7b --- /dev/null +++ b/interp/proptest-regressions/tests/values.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 8bfbc0242b84cd41283ecc9c000e88751215cf3bbef619b67f08dcb64f8ab602 # shrinks to input = 0 diff --git a/interp/src/debugger/commands/command_parser.rs b/interp/src/debugger/commands/command_parser.rs index cd512c5a17..0dda9c6d8e 100644 --- a/interp/src/debugger/commands/command_parser.rs +++ b/interp/src/debugger/commands/command_parser.rs @@ -1,6 +1,7 @@ use super::core::{ Command, ParsedBreakPointID, ParsedGroupName, PrintMode, WatchPosition, }; +use baa::WidthInt; use pest_consume::{match_nodes, Error, Parser}; type ParseResult = std::result::Result>; @@ -87,15 +88,15 @@ impl CommandParser { )) } - fn pc_ufx(input: Node) -> ParseResult { + fn pc_ufx(input: Node) -> ParseResult { Ok(match_nodes!(input.into_children(); - [num(n)] => n as usize + [num(n)] => n as WidthInt )) } - fn pc_sfx(input: Node) -> ParseResult { + fn pc_sfx(input: Node) -> ParseResult { Ok(match_nodes!(input.into_children(); - [num(n)] => n as usize + [num(n)] => n as WidthInt )) } diff --git a/interp/src/errors.rs b/interp/src/errors.rs index 62e7ed55c0..c0088d8a25 100644 --- a/interp/src/errors.rs +++ b/interp/src/errors.rs @@ -5,7 +5,7 @@ use crate::flatten::{ }, structures::environment::Environment, }; -use crate::values::Value; +use baa::{BitVecOps, BitVecValue}; use calyx_ir::Id; use calyx_utils::{Error as CalyxError, MultiError as CalyxMultiError}; use rustyline::error::ReadlineError; @@ -136,14 +136,14 @@ pub enum InterpreterError { /// A currently defunct error type for cross branch conflicts #[error( "par assignments not disjoint: {parent_id}.{port_id} - 1. {v1} - 2. {v2}" + 1. {v1:?} + 2. {v2:?}" )] ParOverlap { port_id: Id, parent_id: Id, - v1: Value, - v2: Value, + v1: BitVecValue, + v2: BitVecValue, }, #[error("{mem_dim} Memory given initialization data with invalid dimension. @@ -277,8 +277,8 @@ impl InterpreterError { let (a1_str, a1_source) = assign_to_string(&a1, env); let (a2_str, a2_source) = assign_to_string(&a2, env); - let a1_v = a1.val(); - let a2_v = a2.val(); + let a1_v = a1.val().to_bit_str(); + let a2_v = a2.val().to_bit_str(); let a1_source = a1_source .map(|(comp, s)| source_to_string(&s, comp, env)) .unwrap_or_default(); diff --git a/interp/src/flatten/flat_ir/base.rs b/interp/src/flatten/flat_ir/base.rs index 7b5fbed8ac..9f612d2754 100644 --- a/interp/src/flatten/flat_ir/base.rs +++ b/interp/src/flatten/flat_ir/base.rs @@ -3,15 +3,14 @@ use std::{ ops::{Add, Sub}, }; +use super::{cell_prototype::CellPrototype, prelude::Identifier}; use crate::{ flatten::structures::index_trait::{ impl_index, impl_index_nonzero, IndexRange, IndexRef, }, serialization::PrintCode, - values::Value, }; - -use super::{cell_prototype::CellPrototype, prelude::Identifier}; +use baa::{BitVecOps, BitVecValue}; // making these all u32 for now, can give the macro an optional type as the // second arg to contract or expand as needed @@ -423,14 +422,14 @@ impl From for AssignmentWinner { /// concrete value and the "winner" which assigned it. #[derive(Clone, PartialEq)] pub struct AssignedValue { - val: Value, + val: BitVecValue, winner: AssignmentWinner, } impl std::fmt::Debug for AssignedValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("AssignedValue") - .field("val", &format!("{}", &self.val)) + .field("val", &self.val.to_bit_str()) .field("winner", &self.winner) .finish() } @@ -445,7 +444,7 @@ impl std::fmt::Display for AssignedValue { impl AssignedValue { /// Creates a new AssignedValue - pub fn new>(val: Value, winner: T) -> Self { + pub fn new>(val: BitVecValue, winner: T) -> Self { Self { val, winner: winner.into(), @@ -458,7 +457,7 @@ impl AssignedValue { } /// Returns the value of the assigned value - pub fn val(&self) -> &Value { + pub fn val(&self) -> &BitVecValue { &self.val } @@ -471,7 +470,7 @@ impl AssignedValue { /// a one bit high value pub fn implicit_bit_high() -> Self { Self { - val: Value::bit_high(), + val: BitVecValue::tru(), winner: AssignmentWinner::Implicit, } } @@ -479,7 +478,7 @@ impl AssignedValue { /// A utility constructor which returns an [`AssignedValue`] with the given /// value and a [`AssignmentWinner::Cell`] as the winner #[inline] - pub fn cell_value(val: Value) -> Self { + pub fn cell_value(val: BitVecValue) -> Self { Self { val, winner: AssignmentWinner::Cell, @@ -489,7 +488,7 @@ impl AssignedValue { /// A utility constructor which returns an [`AssignedValue`] with the given /// value and a [`AssignmentWinner::Implicit`] as the winner #[inline] - pub fn implicit_value(val: Value) -> Self { + pub fn implicit_value(val: BitVecValue) -> Self { Self { val, winner: AssignmentWinner::Implicit, @@ -500,13 +499,13 @@ impl AssignedValue { /// high value and a [`AssignmentWinner::Cell`] as the winner #[inline] pub fn cell_b_high() -> Self { - Self::cell_value(Value::bit_high()) + Self::cell_value(BitVecValue::tru()) } /// A utility constructor which returns an [`AssignedValue`] with a one bit /// low value and a [`AssignmentWinner::Cell`] as the winner #[inline] pub fn cell_b_low() -> Self { - Self::cell_value(Value::bit_low()) + Self::cell_value(BitVecValue::fals()) } } @@ -541,21 +540,20 @@ impl PortValue { } /// If the value is defined, returns the value cast to a boolean. Otherwise - /// returns `None`. It uses the [`Value::as_bool`] method and will panic if - /// the given value is not one bit wide. + /// returns `None`. It will panic if the given value is not one bit wide. pub fn as_bool(&self) -> Option { - self.0.as_ref().map(|x| x.val().as_bool()) + self.0.as_ref().map(|x| x.val().to_bool().unwrap()) } - /// If the value is defined, returns the value cast to a usize. Otherwise - /// returns `None`. It uses the [`Value::as_usize`] method. - pub fn as_usize(&self) -> Option { - self.0.as_ref().map(|x| x.val().as_usize()) + /// If the value is defined, returns the value cast to a u64. Otherwise, + /// returns `None`. It uses the [`BitVecValue::to_u64`] method. + pub fn as_u64(&self) -> Option { + self.0.as_ref().map(|x| x.val().to_u64().unwrap()) } /// Returns a reference to the underlying value if it is defined. Otherwise /// returns `None`. - pub fn val(&self) -> Option<&Value> { + pub fn val(&self) -> Option<&BitVecValue> { self.0.as_ref().map(|x| &x.val) } @@ -576,17 +574,17 @@ impl PortValue { } /// Creates a [PortValue] that has the "winner" as a cell - pub fn new_cell(val: Value) -> Self { + pub fn new_cell(val: BitVecValue) -> Self { Self(Some(AssignedValue::cell_value(val))) } /// Creates a width-bit zero [PortValue] that has the "winner" as a cell pub fn new_cell_zeroes(width: u32) -> Self { - Self::new_cell(Value::zeroes(width)) + Self::new_cell(BitVecValue::zero(width)) } /// Creates a [PortValue] that has the "winner" as implicit - pub fn new_implicit(val: Value) -> Self { + pub fn new_implicit(val: BitVecValue) -> Self { Self(Some(AssignedValue::implicit_value(val))) } @@ -602,11 +600,15 @@ impl PortValue { if let Some(v) = self.0.as_ref() { let v = &v.val; match print_code { - PrintCode::Unsigned => format!("{}", v.as_unsigned()), - PrintCode::Signed => format!("{}", v.as_signed()), - PrintCode::UFixed(num) => format!("{}", v.as_ufp(num)), - PrintCode::SFixed(num) => format!("{}", v.as_sfp(num)), - PrintCode::Binary => format!("{}", v), + PrintCode::Unsigned => format!("{}", v.to_big_uint()), + PrintCode::Signed => format!("{}", v.to_big_int()), + PrintCode::UFixed(num) => { + format!("{}", v.to_unsigned_fixed_point(num).unwrap()) + } + PrintCode::SFixed(num) => { + format!("{}", v.to_signed_fixed_point(num).unwrap()) + } + PrintCode::Binary => v.to_bit_str(), } } else { "undef".to_string() diff --git a/interp/src/flatten/primitives/btor2_prim.rs b/interp/src/flatten/primitives/btor2_prim.rs index a5a0ee5231..8d45e18060 100644 --- a/interp/src/flatten/primitives/btor2_prim.rs +++ b/interp/src/flatten/primitives/btor2_prim.rs @@ -5,8 +5,7 @@ use crate::flatten::primitives::prim_trait::{Primitive, UpdateResult}; use crate::flatten::primitives::{declare_ports, ports}; use crate::flatten::structures::environment::PortMap; -use crate::values::Value; - +use baa::{BitVecValue, WidthInt}; // use std::env; use std::cell::RefCell; @@ -15,12 +14,12 @@ use std::collections::HashMap; pub struct MyBtor2Add<'a> { program: RefCell>, base_port: GlobalPortIdx, - width: usize, // do stuff + width: WidthInt, // do stuff } impl<'a> MyBtor2Add<'a> { declare_ports![ LEFT:0, RIGHT:1, OUT:2 ]; - pub fn new(base: GlobalPortIdx, width: usize) -> Self { + pub fn new(base: GlobalPortIdx, width: WidthInt) -> Self { Self { program: RefCell::new(Btor2Program::new( "../tools/btor2/core/std_add.btor", @@ -37,17 +36,17 @@ impl<'a> Primitive for MyBtor2Add<'a> { let input_map = HashMap::from([ ( "left".to_string(), - port_map[left].as_usize().unwrap_or(0).to_string(), + port_map[left].as_u64().unwrap_or(0).to_string(), ), ( "right".to_string(), - port_map[right].as_usize().unwrap_or(0).to_string(), + port_map[right].as_u64().unwrap_or(0).to_string(), ), ]); match self.program.borrow_mut().run(input_map) { Ok(output_map) => Ok(port_map.insert_val( out, - AssignedValue::cell_value(Value::from( + AssignedValue::cell_value(BitVecValue::from_u64( output_map["out"], self.width, )), diff --git a/interp/src/flatten/primitives/builder.rs b/interp/src/flatten/primitives/builder.rs index f82551fb81..db10e6657c 100644 --- a/interp/src/flatten/primitives/builder.rs +++ b/interp/src/flatten/primitives/builder.rs @@ -14,9 +14,10 @@ use crate::{ structures::context::Context, }, serialization::DataDump, - values::Value, }; +use baa::BitVecValue; + pub fn build_primitive( prim: &CellInfo, base_port: GlobalPortIdx, @@ -33,7 +34,7 @@ pub fn build_primitive( width, c_type: _, } => { - let v = Value::from(*val, *width); + let v = BitVecValue::from_u64(*val, *width); Box::new(StdConst::new(v, base_port)) } @@ -56,8 +57,8 @@ pub fn build_primitive( SingleWidthType::Neq => Box::new(StdNeq::new(base_port)), SingleWidthType::Ge => Box::new(StdGe::new(base_port)), SingleWidthType::Le => Box::new(StdLe::new(base_port)), - SingleWidthType::Lsh => Box::new(StdLsh::new(base_port, *width)), - SingleWidthType::Rsh => Box::new(StdRsh::new(base_port, *width)), + SingleWidthType::Lsh => Box::new(StdLsh::new(base_port)), + SingleWidthType::Rsh => Box::new(StdRsh::new(base_port)), SingleWidthType::Mux => Box::new(StdMux::new(base_port)), SingleWidthType::Wire => Box::new(StdWire::new(base_port)), SingleWidthType::SignedAdd => Box::new(StdAdd::new(base_port)), @@ -87,7 +88,7 @@ pub fn build_primitive( Box::new(Sqrt::::new(base_port, *width, None)) } SingleWidthType::UnsynMult => { - Box::new(StdUnsynMult::new(base_port, *width)) + Box::new(StdUnsynMult::new(base_port)) } SingleWidthType::UnsynDiv => { Box::new(StdUnsynDiv::new(base_port, *width)) @@ -96,7 +97,7 @@ pub fn build_primitive( Box::new(StdUnsynMod::new(base_port, *width)) } SingleWidthType::UnsynSMult => { - Box::new(StdUnsynSmult::new(base_port, *width)) + Box::new(StdUnsynSmult::new(base_port)) } SingleWidthType::UnsynSDiv => { Box::new(StdUnsynSdiv::new(base_port, *width)) @@ -146,10 +147,7 @@ pub fn build_primitive( DoubleWidthType::Pad => Box::new(StdPad::new(base_port, *width2)), }, CellPrototype::TripleWidth { - op, - width1, - width2, - width3, + op, width1, width2, .. } => match op { TripleWidthType::Cat => { // Turns out under the assumption that the primitive is well formed, @@ -157,7 +155,7 @@ pub fn build_primitive( Box::new(StdCat::new(base_port)) } TripleWidthType::BitSlice => { - Box::new(StdBitSlice::new(base_port, *width1, *width2, *width3)) + Box::new(StdBitSlice::new(base_port, *width1, *width2)) } }, diff --git a/interp/src/flatten/primitives/combinational.rs b/interp/src/flatten/primitives/combinational.rs index e1ef437999..a581de244a 100644 --- a/interp/src/flatten/primitives/combinational.rs +++ b/interp/src/flatten/primitives/combinational.rs @@ -1,28 +1,23 @@ -use std::ops::Not; - -use bitvec::vec::BitVec; - -use crate::{ - flatten::{ - flat_ir::prelude::{AssignedValue, GlobalPortIdx, PortValue}, - primitives::{ - all_defined, comb_primitive, declare_ports, ports, - prim_trait::UpdateStatus, utils::floored_division, Primitive, - }, - structures::environment::PortMap, +use crate::flatten::{ + flat_ir::prelude::{AssignedValue, GlobalPortIdx, PortValue}, + primitives::{ + all_defined, comb_primitive, declare_ports, ports, + prim_trait::UpdateStatus, utils::floored_division, Primitive, }, - values::Value, + structures::environment::PortMap, }; +use baa::{BitVecOps, BitVecValue}; + use super::prim_trait::UpdateResult; pub struct StdConst { - value: Value, + value: BitVecValue, out: GlobalPortIdx, } impl StdConst { - pub fn new(value: Value, out: GlobalPortIdx) -> Self { + pub fn new(value: BitVecValue, out: GlobalPortIdx) -> Self { Self { value, out } } } @@ -88,7 +83,7 @@ impl Primitive for StdMux { comb_primitive!(StdNot(input [0]) -> (out [1]) { all_defined!(input); - Ok(Some(input.clone_bit_vec().not().into())) + Ok(Some(input.not())) }); comb_primitive!(StdWire(input [0] ) -> (out [1]) { @@ -98,464 +93,185 @@ comb_primitive!(StdWire(input [0] ) -> (out [1]) { // ===================== Unsigned binary operations ====================== comb_primitive!(StdAdd(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut c_in = false; - let mut sum = BitVec::new(); - for (ai, bi) in a_iter.zip(b_iter) { - sum.push( - c_in & !ai & !bi - || bi & !c_in & !ai - || ai & !c_in & !bi - || ai & bi & c_in, - ); - c_in = bi & c_in || ai & c_in || ai & bi || ai & c_in & bi; - } - - let tr: Value = sum.into(); - //as a sanity check, check tr has same width as left - debug_assert_eq!(tr.width(), left.width()); - Ok(Some(tr)) + Ok(Some(left.add(right))) }); comb_primitive!(StdSub(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - // TODO griffin: the old approach is not possible with the way primitives - // work. - // this is dubious - let result = Value::from(left.as_signed() - right.as_signed(), left.width()); - Ok(Some(result)) + Ok(Some(left.sub(right))) }); -// TODO (Griffin): Make these wrappers around the normal add comb_primitive!(StdFpAdd(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut c_in = false; - let mut sum = BitVec::new(); - for (ai, bi) in a_iter.zip(b_iter) { - sum.push( - c_in & !ai & !bi - || bi & !c_in & !ai - || ai & !c_in & !bi - || ai & bi & c_in, - ); - c_in = bi & c_in || ai & c_in || ai & bi || ai & c_in & bi; - } - let tr = Value::from_bv(sum); - - //as a sanity check, check tr has same width as left - debug_assert_eq!(tr.width(), left.width()); - Ok(Some(tr)) + Ok(Some(left.add(right))) }); comb_primitive!(StdFpSub(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - let result = Value::from(left.as_unsigned() - right.as_unsigned(), left.width()); - - Ok(Some(result)) + Ok(Some(left.sub(right))) }); // ===================== Shift Operations ====================== -comb_primitive!(StdLsh[WIDTH](left [0], right [1]) -> (out [2]) { - all_defined!(left, right); - //to avoid the casting overflow, - //we know that [left], [right], and [self] - //are capped at bitwidths as large as largest u64 (2^64 - 1 = 1.84 * 10^19 ...) - //so, if [self] has a width greater than 64, - //and the 65th index is a 1, we just automatically return a 0 of the - //appropriate bitwidth! - - if WIDTH > 64 { - //check if right is greater than or equal to 2 ^ 64 - - for bit in right.iter().by_ref().skip(64) { - if bit { - return Ok( Some(Value::zeroes(WIDTH as usize))); - } - } - } - - //but that's not the only problem. we can't let [place] get to - //2^64 or above. However the right value couldn't have even been specified - //to be greater than or equal to 2^64, because it's constrained by u64. - //so instead of incrementing [place], just calculate place, but only - //when [bit] is 1, which can only be true for bits below the 65th (first - // bit is 2^0) - - let mut tr = BitVec::new(); - //first push the requisite # of zeroes - for (index, bit) in right.iter().enumerate() { - if bit { - //not possible for bit to be 1 after the 64th bit - for _ in 0..u64::pow(2, index as u32) { - if tr.len() < WIDTH as usize { - tr.push(false); - } - //no point in appending once we've filled it all with zeroes - } - } - } - //then copy over the bits from [left] onto the back (higher-place bits) of - //[tr]. Then truncate, aka slicing off the bits that exceed the width of this - //component - let mut to_append = left.clone_bit_vec(); - tr.append(&mut to_append); - tr.truncate(WIDTH as usize); - let tr = Value::from_bv(tr); - debug_assert_eq!(tr.width(), WIDTH as u64); - //sanity check the widths - Ok(Some(tr)) -}); - -comb_primitive!(StdRsh[WIDTH](left [0], right [1]) -> (out [2]) { - all_defined!(left, right); - - //remove [right] bits from index 0 - //extend to proper size - - //same check as in LSH - if WIDTH > 64 { - //check if right is greater than or equal to 2 ^ 64 - for bit in right.iter().skip(64) { - if bit { - return Ok( Some(Value::zeroes(WIDTH as usize))); - } - } - } +comb_primitive!(StdLsh(left [0], right [1]) -> (out [2]) { + all_defined!(left, right); + Ok(Some(left.shift_left(right))) +}); - let mut tr = left.clone_bit_vec(); - //first remove [right] bits - for (index, bit) in right.iter().enumerate() { - if bit { - for _ in 0..u64::pow(2, index as u32) { - if !tr.is_empty() { - tr.remove(0); - } - } - } - } - //now resize to proper size, putting 0s at the end (0 is false) - tr.resize(WIDTH as usize, false); - let tr = Value::from_bv(tr); - debug_assert_eq!(tr.width(), WIDTH as u64); - //sanity check the widths - Ok(Some(tr)) +comb_primitive!(StdRsh(left [0], right [1]) -> (out [2]) { + all_defined!(left, right); + Ok(Some(left.shift_right(right))) }); // ===================== Signed Shift Operations ====================== comb_primitive!(StdSlsh(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let shift_amount = right.as_usize(); - let mut val = left.clone_bit_vec(); - val.shift_right(shift_amount); - let result: Value = val.into(); - Ok(Some(result)) - + Ok(Some(left.shift_left(right))) }); comb_primitive!(StdSrsh(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let shift_amount = right.as_usize(); - let sign: bool = left[left.len()-1]; //msb - let mut val = left.clone_bit_vec(); - val.shift_left(shift_amount); - if sign { - for mut bit in val.iter_mut().rev().take(shift_amount) { - *bit = true; - } - } - let result: Value = val.into(); - Ok(Some(result)) + Ok(Some(left.arithmetic_shift_right(right))) }); // ===================== Logial Operations ====================== comb_primitive!(StdAnd(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let result: Value = (left.clone_bit_vec() & right.clone_bit_vec()).into(); - Ok(Some(result)) + Ok(Some(left.and(right))) }); comb_primitive!(StdOr(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let result: Value = (left.clone_bit_vec() | right.clone_bit_vec()).into(); - Ok(Some(result)) + Ok(Some(left.or(right))) }); comb_primitive!(StdXor(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let result: Value = (left.clone_bit_vec() ^ right.clone_bit_vec()).into(); - Ok(Some(result)) + Ok(Some(left.xor(right))) }); // ===================== Comparison Operations ====================== comb_primitive!(StdGt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut tr = false; - - //as we proceed up in magnitude, it doesn't matter which port was less - //b/c [100....000] > [011....111] always. - //but if ai = bi, it matters which was higher previously - for (ai, bi) in a_iter.zip(b_iter) { - tr = ai & !bi || tr & !bi || tr & ai; - } - - Ok(Some(if tr { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_greater(right).into())) }); comb_primitive!(StdLt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut tr = false; - - //tr represents a < b - for (ai, bi) in a_iter.zip(b_iter) { - tr = !ai & bi | tr & !ai | tr & bi; - } - - //same as gt, just reverse the if. - //but actually not so if they are equal... should change the loop - Ok(Some(if tr { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_less(right).into())) }); comb_primitive!(StdGe(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut tr = true; //diff between gt and ge is just assume they r equal - - //as we proceed up in magnitude, it doesn't matter which port was less - //b/c [100....000] > [011....111] always. - //but if ai = bi, it matters which was higher previously - for (ai, bi) in a_iter.zip(b_iter) { - tr = ai & !bi || tr & !bi || tr & ai; - } - - Ok(Some(if tr { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_greater_or_equal(right).into())) }); comb_primitive!(StdLe(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - let mut tr = true; //diff between le and lt is just assume they are equal - - //tr represents a <= b - for (ai, bi) in a_iter.zip(b_iter) { - tr = !ai & bi | tr & !ai | tr & bi; - } - - //same as gt, just reverse the if. - //but actually not so if they are equal... should change the loop - Ok(Some(if tr { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_less_or_equal(right).into())) }); comb_primitive!(StdEq(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - - //tr represents a = b - for (ai, bi) in a_iter.zip(b_iter) { - if !ai & bi || !bi & ai { - return Ok(Some(Value::bit_low())); - } - } - - Ok(Some(Value::bit_high())) + Ok(Some(left.is_equal(right).into())) }); comb_primitive!(StdNeq(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - let a_iter = left.iter(); - let b_iter = right.iter(); - - //tr represents a = b - for (ai, bi) in a_iter.zip(b_iter) { - if bi & !ai || !bi & ai { - return Ok(Some(Value::bit_high())); - } - } - - Ok(Some(Value::bit_low())) + Ok(Some(left.is_not_equal(right).into())) }); // ===================== Signed Comparison Operations ====================== comb_primitive!(StdSgt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() > right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_greater_signed(right).into())) }); comb_primitive!(StdSlt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() < right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_less_signed(right).into())) }); comb_primitive!(StdSge(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() >= right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_greater_or_equal_signed(right).into())) }); comb_primitive!(StdSle(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() <= right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_less_or_equal_signed(right).into())) }); comb_primitive!(StdSeq(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() == right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_equal(right).into())) }); comb_primitive!(StdSneq(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() != right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_not_equal(right).into())) }); // ===================== Unsigned FP Comparison Operators ====================== comb_primitive!(StdFpGt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( - Some(if left.as_unsigned() > right.as_unsigned() { - Value::bit_high() - } else { - Value::bit_low() - }) - ) + Ok(Some(left.is_greater(right).into())) }); // ===================== Signed FP Comparison Operators ====================== comb_primitive!(StdFpSgt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() > right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_greater_signed(right).into())) }); comb_primitive!(StdFpSlt(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(if left.as_signed() < right.as_signed() { - Value::bit_high() - } else { - Value::bit_low() - })) + Ok(Some(left.is_less_signed(right).into())) }); // ===================== Resizing Operations ====================== comb_primitive!(StdSlice[OUT_WIDTH](input [0]) -> (out [1]) { all_defined!(input); - - Ok( Some(input.truncate(OUT_WIDTH as usize))) + let msb = OUT_WIDTH - 1; + Ok(Some(input.slice(msb, 0))) }); comb_primitive!(StdPad[OUT_WIDTH](input [0]) -> (out [1]) { all_defined!(input); - - Ok( Some(input.ext(OUT_WIDTH as usize))) + let by = OUT_WIDTH - input.width(); + Ok(Some(input.zero_extend(by))) }); comb_primitive!(StdCat(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok(Some(Value::concat(left, right))) + Ok(Some(left.concat(right))) }); -comb_primitive!(StdBitSlice[START_IDX, END_IDX, OUT_WIDTH](input [0]) -> (out [1]) { +comb_primitive!(StdBitSlice[START_IDX, END_IDX](input [0]) -> (out [1]) { all_defined!(input); - let output = input.clone(); - let output = output.slice_out( START_IDX as usize, END_IDX as usize); - assert_eq!(output.len(), OUT_WIDTH as usize); - - Ok(Some(output)) + let (msb, lsb) = (END_IDX, START_IDX); + Ok(Some(input.slice(msb, lsb))) }); // ===================== Unsynthesizeable Operations ====================== -comb_primitive!(StdUnsynMult[WIDTH](left [0], right [1]) -> (out [2]) { +comb_primitive!(StdUnsynMult(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_unsigned() * right.as_unsigned(), WIDTH))) + Ok(Some(left.mul(right))) }); comb_primitive!(StdUnsynDiv[WIDTH](left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_unsigned() / right.as_unsigned(), WIDTH))) + let res = left.to_big_uint() / right.to_big_uint(); + Ok(Some(BitVecValue::from_big_uint(&res, WIDTH))) }); -comb_primitive!(StdUnsynSmult[WIDTH](left [0], right [1]) -> (out [2]) { +comb_primitive!(StdUnsynSmult(left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_signed() * right.as_signed(), WIDTH))) + // FIXME: is there a difference for signed? + Ok(Some(left.mul(right))) }); comb_primitive!(StdUnsynSdiv[WIDTH](left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_signed() / right.as_signed(), WIDTH))) + let res = left.to_big_int() / right.to_big_int(); + Ok(Some(BitVecValue::from_big_int(&res, WIDTH))) }); comb_primitive!(StdUnsynMod[WIDTH](left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_unsigned() % right.as_unsigned(), WIDTH))) + let res = left.to_big_uint() % right.to_big_uint(); + Ok(Some(BitVecValue::from_big_uint(&res, WIDTH))) }); comb_primitive!(StdUnsynSmod[WIDTH](left [0], right [1]) -> (out [2]) { all_defined!(left, right); - - Ok( Some(Value::from(left.as_signed() - right.as_signed() * floored_division( - &left.as_signed(), - &right.as_signed()), WIDTH))) + let res = left.to_big_int() - right.to_big_int() * floored_division( + &left.to_big_int(), + &right.to_big_int()); + Ok(Some(BitVecValue::from_big_int(&res, WIDTH))) }); pub struct StdUndef(GlobalPortIdx); diff --git a/interp/src/flatten/primitives/macros.rs b/interp/src/flatten/primitives/macros.rs index 3a5708ecc0..bb1dd95e92 100644 --- a/interp/src/flatten/primitives/macros.rs +++ b/interp/src/flatten/primitives/macros.rs @@ -9,7 +9,7 @@ macro_rules! declare_ports { $( #[allow(non_upper_case_globals)] - const $port: usize = $offset; + const $port: usize = $offset; // this is a usize because it encodes the position of the port! )+ } } @@ -74,7 +74,7 @@ macro_rules! comb_primitive { #[allow(non_snake_case)] - let exec_func = |$($($param: u32,)+)? $($port: &$crate::flatten::flat_ir::prelude::PortValue),+| ->$crate::errors::InterpreterResult> { + let exec_func = |$($($param: u32,)+)? $($port: &$crate::flatten::flat_ir::prelude::PortValue),+| ->$crate::errors::InterpreterResult> { $execute }; diff --git a/interp/src/flatten/primitives/prim_trait.rs b/interp/src/flatten/primitives/prim_trait.rs index c89488548a..6519b72292 100644 --- a/interp/src/flatten/primitives/prim_trait.rs +++ b/interp/src/flatten/primitives/prim_trait.rs @@ -3,28 +3,29 @@ use crate::{ flatten::{flat_ir::base::GlobalPortIdx, structures::environment::PortMap}, serialization::PrintCode, serialization::Serializable, - values::Value, }; +use baa::BitVecValue; + pub struct AssignResult { pub destination: GlobalPortIdx, - pub value: Value, + pub value: BitVecValue, } impl AssignResult { - pub fn new(destination: GlobalPortIdx, value: Value) -> Self { + pub fn new(destination: GlobalPortIdx, value: BitVecValue) -> Self { Self { destination, value } } } -impl From<(GlobalPortIdx, Value)> for AssignResult { - fn from(value: (GlobalPortIdx, Value)) -> Self { +impl From<(GlobalPortIdx, BitVecValue)> for AssignResult { + fn from(value: (GlobalPortIdx, BitVecValue)) -> Self { Self::new(value.0, value.1) } } -impl From<(Value, GlobalPortIdx)> for AssignResult { - fn from(value: (Value, GlobalPortIdx)) -> Self { +impl From<(BitVecValue, GlobalPortIdx)> for AssignResult { + fn from(value: (BitVecValue, GlobalPortIdx)) -> Self { Self::new(value.1, value.0) } } diff --git a/interp/src/flatten/primitives/stateful/math.rs b/interp/src/flatten/primitives/stateful/math.rs index d3daecf8fa..cd4fb9c6ce 100644 --- a/interp/src/flatten/primitives/stateful/math.rs +++ b/interp/src/flatten/primitives/stateful/math.rs @@ -1,15 +1,13 @@ -use crate::{ - flatten::{ - flat_ir::prelude::*, - primitives::{ - declare_ports, ports, - prim_trait::*, - utils::{floored_division, int_sqrt, ShiftBuffer}, - }, - structures::environment::PortMap, +use crate::flatten::{ + flat_ir::prelude::*, + primitives::{ + declare_ports, ports, + prim_trait::*, + utils::{floored_division, int_sqrt, ShiftBuffer}, }, - values::{InputNumber, Value}, + structures::environment::PortMap, }; +use baa::{BitVecOps, BitVecValue, WidthInt}; use num_traits::Euclid; pub struct StdMultPipe { @@ -26,7 +24,7 @@ impl StdMultPipe { Self { base_port, pipeline: ShiftBuffer::default(), - current_output: PortValue::new_cell(Value::zeroes(width)), + current_output: PortValue::new_cell(BitVecValue::zero(width)), width, done_is_high: false, } @@ -43,9 +41,9 @@ impl Primitive for StdMultPipe { let done_signal = port_map.insert_val( done, AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -64,7 +62,7 @@ impl Primitive for StdMultPipe { if port_map[reset].as_bool().unwrap_or_default() { self.current_output = - PortValue::new_cell(Value::zeroes(self.width)); + PortValue::new_cell(BitVecValue::zero(self.width)); self.done_is_high = false; self.pipeline.reset(); } else if port_map[go].as_bool().unwrap_or_default() { @@ -74,11 +72,9 @@ impl Primitive for StdMultPipe { if let Some((l, r)) = output { let out_val = l.as_option().and_then(|left| { r.as_option().map(|right| { - Value::from( - left.val().as_unsigned() - * right.val().as_unsigned(), - self.width, - ) + let value = left.val().to_big_uint() + * right.val().to_big_uint(); + BitVecValue::from_big_uint(&value, self.width) }) }); self.current_output = @@ -86,7 +82,7 @@ impl Primitive for StdMultPipe { self.done_is_high = true; } else { self.current_output = - PortValue::new_cell(Value::zeroes(self.width)); + PortValue::new_cell(BitVecValue::zero(self.width)); self.done_is_high = false; } } else { @@ -97,9 +93,9 @@ impl Primitive for StdMultPipe { let done_signal = port_map.insert_val( done, AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -115,7 +111,7 @@ pub struct StdDivPipe { pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, output_quotient: PortValue, output_remainder: PortValue, - width: u32, + width: WidthInt, done_is_high: bool, } @@ -125,8 +121,8 @@ impl StdDivPipe { Self { base_port, pipeline: ShiftBuffer::default(), - output_quotient: PortValue::new_cell(Value::zeroes(width)), - output_remainder: PortValue::new_cell(Value::zeroes(width)), + output_quotient: PortValue::new_cell(BitVecValue::zero(width)), + output_remainder: PortValue::new_cell(BitVecValue::zero(width)), width, done_is_high: false, } @@ -165,7 +161,7 @@ impl Primitive if port_map[reset].as_bool().unwrap_or_default() { self.output_quotient = - PortValue::new_cell(Value::zeroes(self.width)); + PortValue::new_cell(BitVecValue::zero(self.width)); self.done_is_high = false; self.pipeline.reset(); } else if port_map[go].as_bool().unwrap_or_default() { @@ -176,36 +172,30 @@ impl Primitive let out_val = l.as_option().and_then(|left| { r.as_option().map(|right| { ( - Value::from::( - if !SIGNED { - (left.val().as_unsigned() - / right.val().as_unsigned()) - .into() - } else { - (left.val().as_signed() - / right.val().as_signed()) - .into() - }, - self.width, - ), - Value::from::( - if !SIGNED { - (left - .val() - .as_unsigned() - .rem_euclid(&right.val().as_unsigned())) - .into() - } else { - (left.val().as_signed() - - right.val().as_signed() - * floored_division( - &left.val().as_signed(), - &right.val().as_signed(), - )) - .into() - }, - self.width, - ), + if !SIGNED { + let val = left.val().to_big_uint() + / right.val().to_big_uint(); + BitVecValue::from_big_uint(&val, self.width) + } else { + let val = left.val().to_big_int() + / right.val().to_big_int(); + BitVecValue::from_big_int(&val, self.width) + }, + if !SIGNED { + let val = left + .val() + .to_big_uint() + .rem_euclid(&right.val().to_big_uint()); + BitVecValue::from_big_uint(&val, self.width) + } else { + let val = left.val().to_big_int() + - right.val().to_big_int() + * floored_division( + &left.val().to_big_int(), + &right.val().to_big_int(), + ); + BitVecValue::from_big_int(&val, self.width) + }, ) }) }); @@ -216,9 +206,9 @@ impl Primitive self.done_is_high = true; } else { self.output_quotient = - PortValue::new_cell(Value::zeroes(self.width)); + PortValue::new_cell(BitVecValue::zero(self.width)); self.output_remainder = - PortValue::new_cell(Value::zeroes(self.width)); + PortValue::new_cell(BitVecValue::zero(self.width)); self.done_is_high = false; } } else { @@ -284,19 +274,23 @@ impl Primitive for Sqrt { if port_map[reset].as_bool().unwrap_or_default() { self.done_is_high = false; - self.output = PortValue::new_cell(Value::zeroes(self.width)); + self.output = PortValue::new_cell(BitVecValue::zero(self.width)); } else if port_map[go].as_bool().unwrap_or_default() { let input = port_map[in_val].as_option(); if let Some(input) = input { self.output = if IS_FIXED_POINT { let val = int_sqrt( - &(input.val().as_unsigned() + &(input.val().to_big_uint() << (self.frac_width.unwrap() as usize)), ); - PortValue::new_cell(Value::from(val, self.width)) + PortValue::new_cell(BitVecValue::from_big_uint( + &val, self.width, + )) } else { - let val = int_sqrt(&input.val().as_unsigned()); - PortValue::new_cell(Value::from(val, self.width)) + let val = int_sqrt(&input.val().to_big_uint()); + PortValue::new_cell(BitVecValue::from_big_uint( + &val, self.width, + )) }; } else { // TODO griffin: should probably put an error or warning here? @@ -319,8 +313,8 @@ pub struct FxpMultPipe { base_port: GlobalPortIdx, pipeline: ShiftBuffer<(PortValue, PortValue), DEPTH>, current_output: PortValue, - int_width: u32, - frac_width: u32, + int_width: WidthInt, + frac_width: WidthInt, done_is_high: bool, } @@ -334,7 +328,7 @@ impl FxpMultPipe { Self { base_port, pipeline: ShiftBuffer::default(), - current_output: PortValue::new_cell(Value::zeroes( + current_output: PortValue::new_cell(BitVecValue::zero( int_width + frac_width, )), int_width, @@ -354,9 +348,9 @@ impl Primitive for FxpMultPipe { let done_signal = port_map.insert_val( done, AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -374,7 +368,7 @@ impl Primitive for FxpMultPipe { ]; if port_map[reset].as_bool().unwrap_or_default() { - self.current_output = PortValue::new_cell(Value::zeroes( + self.current_output = PortValue::new_cell(BitVecValue::zero( self.int_width + self.frac_width, )); self.done_is_high = false; @@ -386,14 +380,15 @@ impl Primitive for FxpMultPipe { if let Some((l, r)) = output { let out_val = l.as_option().and_then(|left| { r.as_option().map(|right| { - Value::from( - left.val().as_unsigned() - * right.val().as_unsigned(), + let val = left.val().to_big_uint() + * right.val().to_big_uint(); + BitVecValue::from_big_uint( + &val, 2 * (self.frac_width + self.int_width), ) - .slice_out( - self.frac_width as usize, - (2 * self.frac_width + self.int_width) as usize, + .slice( + 2 * self.frac_width + self.int_width, + self.frac_width, ) }) }); @@ -401,7 +396,7 @@ impl Primitive for FxpMultPipe { out_val.map_or(PortValue::new_undef(), PortValue::new_cell); self.done_is_high = true; } else { - self.current_output = PortValue::new_cell(Value::zeroes( + self.current_output = PortValue::new_cell(BitVecValue::zero( self.frac_width + self.int_width, )); self.done_is_high = false; @@ -414,9 +409,9 @@ impl Primitive for FxpMultPipe { let done_signal = port_map.insert_val( done, AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -447,8 +442,8 @@ impl FxpDivPipe { Self { base_port, pipeline: ShiftBuffer::default(), - output_quotient: PortValue::new_cell(Value::zeroes(int_width)), - output_remainder: PortValue::new_cell(Value::zeroes( + output_quotient: PortValue::new_cell(BitVecValue::zero(int_width)), + output_remainder: PortValue::new_cell(BitVecValue::zero( frac_width + int_width, )), int_width, @@ -494,7 +489,7 @@ impl Primitive if port_map[reset].as_bool().unwrap_or_default() { self.output_quotient = - PortValue::new_cell(Value::zeroes(self.width())); + PortValue::new_cell(BitVecValue::zero(self.width())); self.done_is_high = false; self.pipeline.reset(); } else if port_map[go].as_bool().unwrap_or_default() { @@ -505,38 +500,32 @@ impl Primitive let out_val = l.as_option().and_then(|left| { r.as_option().map(|right| { ( - Value::from::( - if !SIGNED { - ((left.val().as_unsigned() - << self.frac_width as usize) - / right.val().as_unsigned()) - .into() - } else { - ((left.val().as_signed() - << self.frac_width as usize) - / right.val().as_signed()) - .into() - }, - self.width(), - ), - Value::from::( - if !SIGNED { - (left - .val() - .as_unsigned() - .rem_euclid(&right.val().as_unsigned())) - .into() - } else { - (left.val().as_signed() - - right.val().as_signed() - * floored_division( - &left.val().as_signed(), - &right.val().as_signed(), - )) - .into() - }, - self.width(), - ), + if !SIGNED { + let val = (left.val().to_big_uint() + << self.frac_width as usize) + / right.val().to_big_uint(); + BitVecValue::from_big_uint(&val, self.width()) + } else { + let val = (left.val().to_big_int() + << self.frac_width as usize) + / right.val().to_big_int(); + BitVecValue::from_big_int(&val, self.width()) + }, + if !SIGNED { + let val = left + .val() + .to_big_uint() + .rem_euclid(&right.val().to_big_uint()); + BitVecValue::from_big_uint(&val, self.width()) + } else { + let val = left.val().to_big_int() + - right.val().to_big_int() + * floored_division( + &left.val().to_big_int(), + &right.val().to_big_int(), + ); + BitVecValue::from_big_int(&val, self.width()) + }, ) }) }); @@ -547,9 +536,9 @@ impl Primitive self.done_is_high = true; } else { self.output_quotient = - PortValue::new_cell(Value::zeroes(self.width())); + PortValue::new_cell(BitVecValue::zero(self.width())); self.output_remainder = - PortValue::new_cell(Value::zeroes(self.width())); + PortValue::new_cell(BitVecValue::zero(self.width())); self.done_is_high = false; } } else { diff --git a/interp/src/flatten/primitives/stateful/memories.rs b/interp/src/flatten/primitives/stateful/memories.rs index ceb05badce..b4753a9ec7 100644 --- a/interp/src/flatten/primitives/stateful/memories.rs +++ b/interp/src/flatten/primitives/stateful/memories.rs @@ -15,13 +15,14 @@ use crate::{ structures::{environment::PortMap, index_trait::IndexRef}, }, serialization::{Entry, PrintCode, Serializable, Shape}, - values::Value, }; +use baa::{BitVecOps, BitVecValue, WidthInt}; + pub struct StdReg { base_port: GlobalPortIdx, + internal_state: BitVecValue, global_idx: GlobalCellIdx, - internal_state: Value, done_is_high: bool, } @@ -33,7 +34,7 @@ impl StdReg { global_idx: GlobalCellIdx, width: u32, ) -> Self { - let internal_state = Value::zeroes(width); + let internal_state = BitVecValue::zero(width); Self { base_port, global_idx, @@ -54,9 +55,12 @@ impl Primitive for StdReg { ]; let done_port = if port_map[reset].as_bool().unwrap_or_default() { - self.internal_state = Value::zeroes(self.internal_state.width()); - port_map - .insert_val(done, AssignedValue::cell_value(Value::bit_low()))? + self.internal_state = + BitVecValue::zero(self.internal_state.width()); + port_map.insert_val( + done, + AssignedValue::cell_value(BitVecValue::fals()), + )? } else if port_map[write_en].as_bool().unwrap_or_default() { self.internal_state = port_map[input] .as_option() @@ -68,15 +72,17 @@ impl Primitive for StdReg { port_map.insert_val( done, - AssignedValue::cell_value(Value::bit_high()), + AssignedValue::cell_value(BitVecValue::tru()), )? | port_map.insert_val( out_idx, AssignedValue::cell_value(self.internal_state.clone()), )? } else { self.done_is_high = false; - port_map - .insert_val(done, AssignedValue::cell_value(Value::bit_low()))? + port_map.insert_val( + done, + AssignedValue::cell_value(BitVecValue::fals()), + )? }; Ok(done_port @@ -97,9 +103,9 @@ impl Primitive for StdReg { let done_signal = port_map.insert_val( done, AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -118,7 +124,7 @@ impl Primitive for StdReg { } fn dump_memory_state(&self) -> Option> { - Some(self.internal_state.clone().to_bytes()) + Some(self.internal_state.clone().to_bytes_le()) } } @@ -168,25 +174,25 @@ impl MemDx { }; match self.shape { - Shape::D1(_d0_size) => port_map[addr0].as_usize(), + Shape::D1(_d0_size) => port_map[addr0].as_u64().map(|v| v as usize), Shape::D2(_d0_size, d1_size) => { - let a0 = port_map[addr0].as_usize()?; - let a1 = port_map[addr1].as_usize()?; + let a0 = port_map[addr0].as_u64()? as usize; + let a1 = port_map[addr1].as_u64()? as usize; Some(a0 * d1_size + a1) } Shape::D3(_d0_size, d1_size, d2_size) => { - let a0 = port_map[addr0].as_usize()?; - let a1 = port_map[addr1].as_usize()?; - let a2 = port_map[addr2].as_usize()?; + let a0 = port_map[addr0].as_u64()? as usize; + let a1 = port_map[addr1].as_u64()? as usize; + let a2 = port_map[addr2].as_u64()? as usize; Some(a0 * (d1_size * d2_size) + a1 * d2_size + a2) } Shape::D4(_d0_size, d1_size, d2_size, d3_size) => { - let a0 = port_map[addr0].as_usize()?; - let a1 = port_map[addr1].as_usize()?; - let a2 = port_map[addr2].as_usize()?; - let a3 = port_map[addr3].as_usize()?; + let a0 = port_map[addr0].as_u64()? as usize; + let a1 = port_map[addr1].as_u64()? as usize; + let a2 = port_map[addr2].as_u64()? as usize; + let a3 = port_map[addr3].as_u64()? as usize; Some( a0 * (d1_size * d2_size * d3_size) @@ -223,7 +229,7 @@ impl MemDx { pub struct CombMem { base_port: GlobalPortIdx, - internal_state: Vec, + internal_state: Vec, // TODO griffin: This bool is unused in the actual struct and should either // be removed or _allow_invalid_access: bool, @@ -261,7 +267,7 @@ impl CombMem { T: Into, { let shape = size.into(); - let internal_state = vec![Value::zeroes(width); shape.size()]; + let internal_state = vec![BitVecValue::zero(width); shape.size()]; Self { base_port: base, @@ -277,7 +283,7 @@ impl CombMem { pub fn new_with_init( base_port: GlobalPortIdx, global_idx: GlobalCellIdx, - width: u32, + width: WidthInt, allow_invalid: bool, size: T, data: &[u8], @@ -290,7 +296,7 @@ impl CombMem { let internal_state = data .chunks_exact(byte_count as usize) - .map(|x| Value::from_bytes_le(x, width as usize)) + .map(|x| BitVecValue::from_bytes_le(x, width)) .collect_vec(); assert_eq!(internal_state.len(), size.size()); @@ -313,7 +319,7 @@ impl CombMem { pub fn dump_data(&self) -> Vec { self.internal_state .iter() - .flat_map(|x| x.to_bytes()) + .flat_map(|x| x.to_bytes_le()) .collect() } } @@ -342,9 +348,9 @@ impl Primitive for CombMem { let done_signal = port_map.insert_val( self.done(), AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; Ok(done_signal | read) @@ -407,8 +413,8 @@ impl Primitive for CombMem { pub struct SeqMem { base_port: GlobalPortIdx, + internal_state: Vec, global_idx: GlobalCellIdx, - internal_state: Vec, // TODO griffin: This bool is unused in the actual struct and should either // be removed or _allow_invalid_access: bool, @@ -427,7 +433,7 @@ impl SeqMem { size: T, ) -> Self { let shape = size.into(); - let internal_state = vec![Value::zeroes(width); shape.size()]; + let internal_state = vec![BitVecValue::zero(width); shape.size()]; Self { base_port: base, @@ -444,7 +450,7 @@ impl SeqMem { pub fn new_with_init( base_port: GlobalPortIdx, global_idx: GlobalCellIdx, - width: u32, + width: WidthInt, allow_invalid: bool, size: T, data: &[u8], @@ -457,7 +463,7 @@ impl SeqMem { let internal_state = data .chunks_exact(byte_count as usize) - .map(|x| Value::from_bytes_le(x, width as usize)) + .map(|x| BitVecValue::from_bytes_le(x, width)) .collect_vec(); assert_eq!(internal_state.len(), size.size()); @@ -512,7 +518,7 @@ impl SeqMem { pub fn dump_data(&self) -> Vec { self.internal_state .iter() - .flat_map(|x| x.to_bytes()) + .flat_map(|x| x.to_bytes_le()) .collect() } } @@ -522,9 +528,9 @@ impl Primitive for SeqMem { let done_signal = port_map.insert_val( self.done(), AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), )?; @@ -553,7 +559,7 @@ impl Primitive for SeqMem { if reset { self.done_is_high = false; - self.read_out = PortValue::new_cell(Value::zeroes(self._width)); + self.read_out = PortValue::new_cell(BitVecValue::zero(self._width)); } else if content_en && write_en { self.done_is_high = true; self.read_out = PortValue::new_undef(); @@ -576,9 +582,9 @@ impl Primitive for SeqMem { let done_changed = port_map.insert_val( self.done(), AssignedValue::cell_value(if self.done_is_high { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), ); Ok(done_changed? diff --git a/interp/src/flatten/structures/environment/env.rs b/interp/src/flatten/structures/environment/env.rs index 3a2f4af820..619cc0f020 100644 --- a/interp/src/flatten/structures/environment/env.rs +++ b/interp/src/flatten/structures/environment/env.rs @@ -36,11 +36,11 @@ use crate::{ }, logging, serialization::{DataDump, MemoryDeclaration, PrintCode}, - values::Value, }; use ahash::HashSet; use ahash::HashSetExt; use ahash::{HashMap, HashMapExt}; +use baa::{BitVecOps, BitVecValue}; use itertools::Itertools; use owo_colors::OwoColorize; use slog::warn; @@ -122,9 +122,9 @@ impl PortMap { self.insert_val( target, AssignedValue::cell_value(if done_bool { - Value::bit_high() + BitVecValue::tru() } else { - Value::bit_low() + BitVecValue::fals() }), ) } @@ -231,11 +231,13 @@ impl Debug for CellLedger { #[derive(Debug, Clone)] struct PinnedPorts { - map: HashMap, + map: HashMap, } impl PinnedPorts { - pub fn iter(&self) -> impl Iterator + '_ { + pub fn iter( + &self, + ) -> impl Iterator + '_ { self.map.iter() } @@ -245,7 +247,7 @@ impl PinnedPorts { } } - pub fn insert(&mut self, port: GlobalPortIdx, val: Value) { + pub fn insert(&mut self, port: GlobalPortIdx, val: BitVecValue) { self.map.insert(port, val); } @@ -1078,7 +1080,7 @@ impl + Clone> Environment { pub fn lookup_port_from_string>( &self, port: S, - ) -> Option { + ) -> Option { // this is not the best way to do this but it's fine for now let path = self .traverse_name_vec(&[port.as_ref().to_string()]) @@ -1110,7 +1112,7 @@ impl + Clone> Environment { /// used for input ports on the entrypoint component (excluding the go port) /// and will panic if used otherwise. Intended for external use. Unrelated /// to the rust pin. - pub fn pin_value>(&mut self, port: S, val: Value) { + pub fn pin_value>(&mut self, port: S, val: BitVecValue) { let port = self.get_root_input_port(port); let go = self.get_comp_go(Self::get_root()); @@ -1256,7 +1258,7 @@ impl + Clone> Simulator { /// Pins the port with the given name to the given value. This may only be /// used for input ports on the entrypoint component (excluding the go port) /// and will panic if used otherwise. Intended for external use. - pub fn pin_value>(&mut self, port: S, val: Value) { + pub fn pin_value>(&mut self, port: S, val: BitVecValue) { self.env.pin_value(port, val) } @@ -1269,7 +1271,10 @@ impl + Clone> Simulator { /// Lookup the value of a port on the entrypoint component by name. Will /// error if the port is not found. - pub fn lookup_port_from_string(&self, port: &String) -> Option { + pub fn lookup_port_from_string( + &self, + port: &String, + ) -> Option { self.env.lookup_port_from_string(port) } } @@ -1337,7 +1342,7 @@ impl + Clone> Simulator { let ledger = self.get_root_component(); let go = &ledger.index_bases + self.env.ctx.as_ref().primary[ledger.comp_id].go; - self.env.ports[go] = PortValue::new_implicit(Value::bit_high()); + self.env.ports[go] = PortValue::new_implicit(BitVecValue::tru()); } // may want to make this iterate directly if it turns out that the vec @@ -1515,7 +1520,7 @@ impl + Clone> Simulator { for comp in self.env.pc.finished_comps() { let done_port = self.env.get_comp_done(*comp); self.env.ports[done_port] = - PortValue::new_implicit(Value::bit_high()); + PortValue::new_implicit(BitVecValue::tru()); } let (vecs, par_map, mut with_map, repeat_map) = @@ -1532,7 +1537,7 @@ impl + Clone> Simulator { // if the done is not high & defined, we need to set it to low if !self.env.ports[comp_done].as_bool().unwrap_or_default() { self.env.ports[comp_done] = - PortValue::new_implicit(Value::bit_low()); + PortValue::new_implicit(BitVecValue::fals()); } match &ctx_ref.primary[node.control_node_idx] { @@ -1547,7 +1552,7 @@ impl + Clone> Simulator { // set go high let go_idx = index_bases + go_local; self.env.ports[go_idx] = - PortValue::new_implicit(Value::bit_high()); + PortValue::new_implicit(BitVecValue::tru()); } ControlNode::Invoke(invoke) => { if invoke.comb_group.is_some() @@ -1561,7 +1566,7 @@ impl + Clone> Simulator { let go = self.get_global_port_idx(&invoke.go, node.comp); self.env.ports[go] = - PortValue::new_implicit(Value::bit_high()); + PortValue::new_implicit(BitVecValue::tru()); // TODO griffin: should make this skip initialization if // it's already initialized @@ -1906,10 +1911,10 @@ impl + Clone> Simulator { match c { calyx_ir::PortComp::Eq => a_val == b_val, calyx_ir::PortComp::Neq => a_val != b_val, - calyx_ir::PortComp::Gt => a_val > b_val, - calyx_ir::PortComp::Lt => a_val < b_val, - calyx_ir::PortComp::Geq => a_val >= b_val, - calyx_ir::PortComp::Leq => a_val <= b_val, + calyx_ir::PortComp::Gt => a_val.is_greater(b_val), + calyx_ir::PortComp::Lt => a_val.is_less(b_val), + calyx_ir::PortComp::Geq => a_val.is_greater_or_equal(b_val), + calyx_ir::PortComp::Leq => a_val.is_less_or_equal(b_val), } .into() } @@ -2043,7 +2048,7 @@ impl + Clone> Simulator { for &done_port in &done_ports { if self.env.ports[done_port].is_undef() { self.env.ports[done_port] = - PortValue::new_implicit(Value::bit_low()); + PortValue::new_implicit(BitVecValue::fals()); has_changed = true; } } diff --git a/interp/src/lib.rs b/interp/src/lib.rs index ec8c107aef..34b3d98a9d 100644 --- a/interp/src/lib.rs +++ b/interp/src/lib.rs @@ -5,9 +5,6 @@ pub mod errors; pub mod logging; mod macros; pub mod serialization; -mod structures; mod tests; pub mod flatten; - -pub use structures::values; diff --git a/interp/src/macros.rs b/interp/src/macros.rs index 0b2f45489f..2eade11a04 100644 --- a/interp/src/macros.rs +++ b/interp/src/macros.rs @@ -39,7 +39,7 @@ macro_rules! lit_or_id { /// ``` macro_rules! port_bindings { ( $binds: ident; $( $port: ident -> ($val: tt, $width: tt) ),+ ) => { - $( let $port = $crate::values::Value::from($crate::lit_or_id!($val), $crate::lit_or_id!($width)); )+ + $( let $port = baa::BitVecValue::from_u64($crate::lit_or_id!($val), $crate::lit_or_id!($width)); )+ let $binds = vec![ $( (calyx_ir::Id::from($crate::in_fix!($port)), &$port) ),+ ]; } } @@ -47,7 +47,7 @@ macro_rules! port_bindings { /// Helper macro to generate validation checks for the input passed to primitives /// ``` /// # use interp::validate; -/// # use interp::values::Value; +/// # use baa::BitVecValue; /// # let input = [("left", [4,4,4,4])]; /// # let inputs = &input; /// # let width = 4; @@ -73,7 +73,7 @@ macro_rules! validate { /// primitives, does not error on unknown ports /// ``` /// # use interp::validate_friendly; -/// # use interp::values::Value; +/// # use baa::BitVecValue; /// # let input = [("left", [4,4,4,4])]; /// # let inputs = &input; /// # let width = 4; diff --git a/interp/src/serialization/formatting.rs b/interp/src/serialization/formatting.rs index b529b8b678..ed41367c2a 100644 --- a/interp/src/serialization/formatting.rs +++ b/interp/src/serialization/formatting.rs @@ -3,9 +3,8 @@ use itertools::Itertools; use serde::Serialize; use std::fmt::{Debug, Display}; -use crate::{ - flatten::flat_ir::cell_prototype::MemoryDimensions, values::Value, -}; +use crate::flatten::flat_ir::cell_prototype::MemoryDimensions; +use baa::{BitVecOps, BitVecValue, WidthInt}; /// An enum wrapping over a tuple representing the shape of a multi-dimensional /// array @@ -107,7 +106,7 @@ pub enum Entry { U(u64), I(i64), Frac(Fraction), - Value(Value), + Value(BitVecValue), } impl From for Entry { @@ -129,12 +128,16 @@ impl From for Entry { } impl Entry { - pub fn from_val_code(val: &Value, code: &PrintCode) -> Self { + pub fn from_val_code(val: &BitVecValue, code: &PrintCode) -> Self { match code { - PrintCode::Unsigned => val.as_u64().into(), - PrintCode::Signed => val.as_i64().into(), - PrintCode::UFixed(f) => val.as_ufp(*f).into(), - PrintCode::SFixed(f) => val.as_sfp(*f).into(), + PrintCode::Unsigned => val.to_u64().unwrap().into(), + PrintCode::Signed => val.to_i64().unwrap().into(), + PrintCode::UFixed(f) => { + val.to_unsigned_fixed_point(*f).unwrap().into() + } + PrintCode::SFixed(f) => { + val.to_signed_fixed_point(*f).unwrap().into() + } PrintCode::Binary => Entry::Value(val.clone()), } } @@ -146,7 +149,7 @@ impl Display for Entry { Entry::U(v) => write!(f, "{}", v), Entry::I(v) => write!(f, "{}", v), Entry::Frac(v) => write!(f, "{}", v), - Entry::Value(v) => write!(f, "{}", v), + Entry::Value(v) => write!(f, "{}", v.to_bit_str()), } } } @@ -162,8 +165,8 @@ pub enum PrintCode { Binary, Unsigned, Signed, - UFixed(usize), - SFixed(usize), + UFixed(WidthInt), + SFixed(WidthInt), } impl Default for PrintCode { diff --git a/interp/src/structures/mod.rs b/interp/src/structures/mod.rs deleted file mode 100644 index 3a8f76794c..0000000000 --- a/interp/src/structures/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod values; diff --git a/interp/src/structures/values.rs b/interp/src/structures/values.rs deleted file mode 100644 index a370b50242..0000000000 --- a/interp/src/structures/values.rs +++ /dev/null @@ -1,789 +0,0 @@ -use std::iter::once; -use std::{fmt::Write, ops::Index}; - -use bitvec::prelude::*; -use fraction::Fraction; -use itertools::Itertools; -use num_bigint::{BigInt, BigUint}; -use serde::Serialize; - -pub type BitString = BitVec; - -/// Retrieves the unsigned fixed point representation of `v`. This splits the representation into -/// integral and fractional bits. The width of the integral bits is described as: -/// `total width - fractional_width`. -fn get_unsigned_fixed_point(v: &Value, fractional_width: usize) -> Fraction { - let integer_width: usize = v.width() as usize - fractional_width; - - // Calculate the integral part of the value. For each set bit at index `i`, add `2^i`. - let whole: Fraction = v - .vec - .iter() - .rev() // ...since the integer bits are most significant. - .take(integer_width) - .zip((0..integer_width).rev()) // Reverse indices as well. - .fold(0u64, |acc, (bit, idx)| -> u64 { - acc | ((*bit as u64) << idx) - }) - .into(); - - // Calculate the fractional part of the value. For each set bit at index `i`, add `2^-i`. - // This begins at `1`, since the first fractional index has value `2^-1` = `1/2`. - let fraction: Fraction = - v.vec.iter().rev().skip(integer_width).enumerate().fold( - Fraction::from(0u64), - |acc, (idx, bit)| -> Fraction { - let denom: u64 = (*bit as u64) << (idx + 1); - // Avoid adding Infinity. - if denom == 0u64 { - acc - } else { - acc + Fraction::new(1u64, denom) - } - }, - ); - whole + fraction -} - -// Lsb0 means [10010] gives 0 at index 0, 1 at index 1, 0 at index 2, etc -// from documentation, usize is the best data type to use in bitvec. -#[derive(Debug)] -pub struct ValueError {} - -#[derive(Debug, Clone)] -pub enum InputNumber { - // unsigned - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - U(BigUint), - // signed - I8(i8), - I16(i16), - I32(i32), - I64(i64), - I128(i128), - I(BigInt), - // usize - Usize(usize), -} - -impl From for InputNumber { - fn from(i: u8) -> Self { - Self::U8(i) - } -} -impl From for InputNumber { - fn from(i: u16) -> Self { - Self::U16(i) - } -} -impl From for InputNumber { - fn from(i: u32) -> Self { - Self::U32(i) - } -} -impl From for InputNumber { - fn from(i: u64) -> Self { - Self::U64(i) - } -} -impl From for InputNumber { - fn from(i: u128) -> Self { - Self::U128(i) - } -} -impl From for InputNumber { - fn from(i: i8) -> Self { - Self::I8(i) - } -} -impl From for InputNumber { - fn from(i: i16) -> Self { - Self::I16(i) - } -} -impl From for InputNumber { - fn from(i: i32) -> Self { - Self::I32(i) - } -} -impl From for InputNumber { - fn from(i: i64) -> Self { - Self::I64(i) - } -} -impl From for InputNumber { - fn from(i: i128) -> Self { - Self::I128(i) - } -} -impl From for InputNumber { - fn from(i: usize) -> Self { - Self::Usize(i) - } -} - -impl From for InputNumber { - fn from(u: BigUint) -> Self { - Self::U(u) - } -} - -impl From for InputNumber { - fn from(i: BigInt) -> Self { - Self::I(i) - } -} - -impl InputNumber { - fn is_negative(&self) -> bool { - match self { - InputNumber::I8(i) => *i < 0, - InputNumber::I16(i) => *i < 0, - InputNumber::I32(i) => *i < 0, - InputNumber::I64(i) => *i < 0, - InputNumber::I128(i) => *i < 0, - InputNumber::I(i) => *i < 0.into(), - _ => false, - } - } - - fn as_usize(&self) -> usize { - match self { - InputNumber::U8(i) => *i as usize, - InputNumber::U16(i) => *i as usize, - InputNumber::U32(i) => *i as usize, - InputNumber::U64(i) => *i as usize, - InputNumber::U128(i) => *i as usize, - InputNumber::I8(i) => *i as usize, - InputNumber::I16(i) => *i as usize, - InputNumber::I32(i) => *i as usize, - InputNumber::I64(i) => *i as usize, - InputNumber::I128(i) => *i as usize, - InputNumber::Usize(i) => *i, - InputNumber::U(_) => unimplemented!(), - InputNumber::I(_) => unimplemented!(), - } - } - - fn as_bit_vec(&self) -> BitString { - match self { - InputNumber::U8(i) => BitVec::from_element(*i as usize), - InputNumber::U16(i) => BitVec::from_element(*i as usize), - InputNumber::U32(i) => BitVec::from_element(*i as usize), - InputNumber::U64(i) => BitVec::from_element(*i as usize), - InputNumber::U128(i) => { - let lower = (i & (u64::MAX as u128)) as usize; - let upper = ((i >> 64) & u64::MAX as u128) as usize; - BitVec::from_slice(&[lower, upper]) - } - InputNumber::I8(i) => BitVec::from_element(*i as usize), - InputNumber::I16(i) => BitVec::from_element(*i as usize), - InputNumber::I32(i) => BitVec::from_element(*i as usize), - InputNumber::I64(i) => BitVec::from_element(*i as usize), - InputNumber::I128(i) => { - let lower = (i & (u64::MAX as i128)) as usize; - let upper = ((i >> 64) & u64::MAX as i128) as usize; - BitVec::from_slice(&[lower, upper]) - } - InputNumber::Usize(i) => BitVec::from_element(*i), - InputNumber::U(u) => { - let bytes_64: Vec<_> = u - .iter_u64_digits() - .map(|x| { - x.try_into().expect("u64 to usize conversion failed") - }) - .collect(); - - BitString::from_slice(&bytes_64) - } - InputNumber::I(i) => match i.sign() { - num_bigint::Sign::Minus => { - let signed_bytes_le = i.to_signed_bytes_le(); - let chunks = signed_bytes_le.chunks_exact(8); - let rem = chunks.remainder(); - let mut bv: BitString = chunks - .map(|bytes| { - let mut array = [0_u8; 8]; - array.copy_from_slice(bytes); - usize::from_le_bytes(array) - }) - .chain(once({ - // in the case of even division this will add an - // extra - let mut array = [0_u8; 8]; - array[..rem.len()].copy_from_slice(rem); - usize::from_le_bytes(array) - })) - .collect(); - // this truncation is _critically_ important for getting the - // correct result - bv.truncate(bv.last_one().map(|x| x + 1).unwrap_or(0)); - - bv - } - num_bigint::Sign::NoSign | num_bigint::Sign::Plus => i - .magnitude() - .to_u64_digits() - .into_iter() - .map(|x| { - let x: usize = x - .try_into() - .expect("u64 to usize conversion failed"); - x - }) - .collect(), - }, - } - } -} - -#[derive(Clone, Debug)] -/// The type of all inputs and outputs to all components in Calyx. -/// Wraps a BitVector. -pub struct Value { - // Lsb0 means the 0th index contains the LSB. This is useful because - // a 7-bit bitvector and 17-bit bitvector representing the number 6 have - // ones in the same index. - vec: BitString, -} - -impl From for Value { - fn from(bv: BitString) -> Self { - Self { vec: bv } - } -} - -impl Value { - pub fn unsigned_value_fits_in(vec: &BitString, width: usize) -> bool { - vec.len() <= width // obviously fits then - || vec - .last_one() // returns an index - .map(|x| x < width) - .unwrap_or(true) // if there is no high bit then it can fit in the given width - } - - pub fn signed_value_fits_in(vec: &BitString, width: usize) -> bool { - vec.len() <= width // obviously fits then - || (vec.ends_with(bits![0]) && Value::unsigned_value_fits_in(vec, width - 1)) // positive value (technically wastes a check) - || (vec.ends_with(bits![1]) && ((vec.len() - vec.trailing_ones()) < width) || vec.trailing_ones() == 0) - // negative value greater than or equal to lowest in new width - } - - pub fn width(&self) -> u64 { - self.vec.len() as u64 - } - - pub fn iter(&self) -> impl DoubleEndedIterator + '_ { - self.vec.iter().by_vals() - } - - pub fn clone_bit_vec(&self) -> BitString { - self.vec.clone() - } - - pub fn bv_ref(&self) -> &BitString { - &self.vec - } - /// Creates a Value with the specified bandwidth. - /// - /// # Example: - /// ``` - /// use interp::values::*; - /// let empty_val = Value::new(2 as usize); - /// ``` - pub fn new(bitwidth: usize) -> Value { - Value::zeroes(bitwidth) - } - - /// Creates a new Value initialized to all 0s given a bitwidth. - /// - /// # Example: - /// ``` - /// use interp::values::*; - /// let zeroed_val = Value::zeroes(2 as usize); - /// ``` - pub fn zeroes>(bitwidth: I) -> Value { - let input_num: InputNumber = bitwidth.into(); - Value { - vec: bitvec![usize, Lsb0; 0; input_num.as_usize()], - } - } - - pub fn bit_high() -> Value { - Value::from(1_usize, 1_usize) - } - - pub fn bit_low() -> Value { - Value::from(0_usize, 1_usize) - } - - /// Create a new Value of a given bitwidth out of an initial_val. You do - /// not have to guarantee initial_val satisifies Into. Note: will error if the - /// given width cannot be made into a usize. - /// # Example: - /// ``` - /// use interp::values::*; - /// let val_16_16 = Value::from(16, 16); - /// ``` - pub fn from, T2: Into>( - initial_val: T1, - bitwidth: T2, - ) -> Self { - let (v, _) = Value::from_checked(initial_val, bitwidth); - v - } - - /// Returns a bit vector for the given input value of the desired width and a bool - /// representing whether the given value could fit in the required width. The result - /// is truncated if it cannot fit. - pub fn from_checked, T2: Into>( - initial_val: T1, - bitwidth: T2, - ) -> (Self, bool) { - let init: InputNumber = initial_val.into(); - let width: InputNumber = bitwidth.into(); - let width = width.as_usize(); - let mut bv = init.as_bit_vec(); - - let flag = init.is_negative() - && !Value::signed_value_fits_in(&bv, width) - || !init.is_negative() - && !Value::unsigned_value_fits_in(&bv, width); - - bv.resize(width, init.is_negative()); - (Value { vec: bv }, flag) - } - - #[inline] - pub fn from_bv(bv: BitString) -> Self { - bv.into() - } - /// Returns a Value truncated to length new_size. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let val_4_4 = Value::from(4, 16).truncate(4); - /// ``` - pub fn truncate(&self, new_size: usize) -> Value { - let mut vec = self.vec.clone(); - vec.truncate(new_size); - Value { vec } - } - - pub fn truncate_in_place(&mut self, new_size: usize) { - self.vec.truncate(new_size); - } - - /// returns a value which consists of all the bits from left as the upper - /// bits and all the bits from right as the lower bits of a value whose - /// width is the sum of the widths of left and right - pub fn concat(left: &Self, right: &Self) -> Self { - Value { - vec: left.vec.iter().chain(right.vec.iter()).collect(), - } - } - - /// Zero-extend the vector to length ext. - /// - /// # Example: - /// ``` - /// use interp::values::*; - /// let val_4_16 = Value::from(4, 4).ext(16); - /// ``` - pub fn ext(&self, ext: usize) -> Value { - let mut vec = self.vec.clone(); - for _x in 0..(ext - vec.len()) { - vec.push(false); - } - Value { vec } - } - - /// Sign-extend the vector to length ext. - /// - /// # Example: - /// ``` - /// use interp::values::*; - /// // [1111] -> [11111]. In 2'sC these are both -1 - /// let val_31_5 = Value::from(15, 4).sext(5); - /// ``` - pub fn sext(&self, ext: usize) -> Value { - let mut vec = self.vec.clone(); - let sign = vec[vec.len() - 1]; - for _x in 0..(ext - vec.len()) { - vec.push(sign); - } - Value { vec } - } - - /// Converts value into u64 type. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let unsign_64_16 = Value::from(16, 16).as_u64(); - /// ``` - pub fn as_u64(&self) -> u64 { - assert!( - Value::unsigned_value_fits_in(&self.vec, 64), - "Cannot fit value into an u64" - ); - self.vec - .iter() - .enumerate() - .take(64) - .fold(0_u64, |acc, (idx, bit)| -> u64 { - acc | ((*bit as u64) << idx) - }) - } - - /// Converts value into unsigned fixed point type. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// use fraction::Fraction; - /// let ufixed_point_Q4_2 = Value::from(0b0110, 4).as_ufp(2); // 3/2 - /// ``` - pub fn as_ufp(&self, fractional_width: usize) -> Fraction { - assert!( - Value::unsigned_value_fits_in(&self.vec, 64), - "unsigned fixed point is supported up to 64 bits. Open an issue if you require more." - ); - get_unsigned_fixed_point(self, fractional_width) - } - - /// Converts value into signed fixed point type. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// use fraction::Fraction; - /// let sfixed_point_Q4_2 = Value::from(0b1110, 4).as_sfp(2); // -3/2 - /// ``` - pub fn as_sfp(&self, fractional_width: usize) -> Fraction { - assert!( - Value::signed_value_fits_in(&self.vec, 64), - "signed fixed point is supported up to 64 bits. Open an issue if you require more." - ); - match self.vec.last_one() { - Some(end) if (end + 1) == self.vec.len() => { - let mut vec = self.clone_bit_vec(); - // Flip each bit until the first "one". This is - // similar to flipping all bits and adding one. - let begin = vec.first_one().unwrap(); - for mut bit in vec.iter_mut().rev().take(end - begin) { - *bit = !*bit - } - -get_unsigned_fixed_point( - &Value::from_bv(vec), - fractional_width, - ) - } - // Either there are no set bits (zero) or this number is non-negative. - _ => get_unsigned_fixed_point(self, fractional_width), - } - } - - /// Converts value into u128 type. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let unsign_128 = Value::from(u128::MAX - 2, 128).as_u128(); - /// assert_eq!(unsign_128, u128::MAX - 2); - /// let unsign_128_32 = Value::from(u128::MAX - 4, 32).as_u128(); - /// assert_eq!(unsign_128_32, ((u128::MAX - 4) as u32) as u128); - /// ``` - pub fn as_u128(&self) -> u128 { - assert!( - Value::unsigned_value_fits_in(&self.vec, 128), - "Cannot fit value into an u128" - ); - self.vec - .iter() - .enumerate() - .take(128) - .fold(0_u128, |acc, (idx, bit)| -> u128 { - acc | ((*bit as u128) << idx) - }) - } - - /// Converts value into i64 type using 2C representation. Sign extends lower values. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let signed_neg_1_4 = Value::from(15, 4).as_i64(); - /// assert_eq!(signed_neg_1_4, -1); - /// ``` - pub fn as_i64(&self) -> i64 { - assert!( - Value::signed_value_fits_in(&self.vec, 64), - "Cannot fit value into an i64" - ); - let init = if *self.vec.last().unwrap() { -1 } else { 0 }; - self.vec.iter().enumerate().take(64).fold( - init, - |acc, (idx, bit)| -> i64 { - (acc & (!(1 << idx))) | ((*bit as i64) << idx) - }, - ) - } - - /// Converts value into i128 type using 2C representation. Sign extends lower values. - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let signed_neg_1_4 = Value::from(-1_i128, 4).as_i128(); - /// assert_eq!(signed_neg_1_4, -1); - /// let signed_pos = Value::from(5_i128,10).as_i128(); - /// assert_eq!(signed_pos, 5) - /// ``` - pub fn as_i128(&self) -> i128 { - assert!( - Value::signed_value_fits_in(&self.vec, 128), - "Cannot fit value into an i128" - ); - let init = if *self.vec.last().unwrap() { -1 } else { 0 }; - self.vec.iter().enumerate().take(128).fold( - init, - |acc, (idx, bit)| -> i128 { - (acc & (!(1 << idx))) | ((*bit as i128) << idx) - }, - ) - } - - pub fn as_usize(&self) -> usize { - assert!( - Value::unsigned_value_fits_in(&self.vec, usize::BITS as usize), - "Cannot fit value into an usize" - ); - - self.vec - .iter() - .enumerate() - .take(usize::BITS as usize) - .fold(0_usize, |acc, (idx, bit)| -> usize { - acc | ((*bit as usize) << idx) - }) - } - - fn as_bytes_le(&self) -> Vec { - self.vec - .iter() - .chunks(8) - .into_iter() - .map(|bits| { - let mut out = 0_u8; - for (idx, bit) in bits.enumerate() { - out |= (*bit as u8) << idx; - } - out - }) - .collect_vec() - } - - pub fn as_signed(&self) -> BigInt { - let mut bytes = self.as_bytes_le(); - let width_bits = self.vec.len(); - - let rem = width_bits % 8; - let msb = 1u8 << if rem != 0 { rem - 1 } else { 7 }; - - if (bytes.last().unwrap() & msb) != 0 { - // The value is negative - if msb != 0b1000_0000 { - let mask = 0b1111_1111u8 << if rem != 0 { rem - 1 } else { 7 }; - *(bytes.last_mut().unwrap()) |= mask; - } - } else { - // The value is positive. No additional work needed - } - - BigInt::from_signed_bytes_le(&bytes) - } - - pub fn as_unsigned(&self) -> BigUint { - let vec = self.as_bytes_le(); - // don't need to sign extend here - - BigUint::from_bytes_le(&vec) - } - - /// Interprets a 1bit value as a bool, will not panic for non-1-bit values - pub fn as_bool(&self) -> bool { - assert!(self.vec.len() == 1); - self.vec[0] - } - - #[allow(clippy::len_without_is_empty)] - /// Returns the length (bitwidth) of the value - /// - /// # Example - /// ``` - /// use interp::values::*; - /// let v = Value::from(1, 3); - /// assert_eq!(v.len(), 3) - /// ``` - pub fn len(&self) -> usize { - self.vec.len() - } - - /// Returns a value containing the sliced region \[lower,upper\), consumes the original - pub fn slice_out(self, lower_idx: usize, upper_idx: usize) -> Self { - assert!(upper_idx >= lower_idx); - assert!(upper_idx < self.vec.len()); - - let new_bv = (self.vec[lower_idx..upper_idx]).into(); - Value { vec: new_bv } - } - - /// Returns a value containing the sliced region \[lower,upper\] - pub fn slice(&self, upper_idx: usize, lower_idx: usize) -> Self { - assert!(upper_idx >= lower_idx); - assert!(upper_idx < self.vec.len()); - - let new_bv = BitVec::from_bitslice(&self.vec[lower_idx..=upper_idx]); - Value { vec: new_bv } - } - - /// Creates a value from a byte slice and truncates to the specified width. - /// The bytes are assumed to be little-endian. The slice of bytes must be - /// non-empty. And the width must be less than or equal to the number of - /// bits in the slice. In cases where the width of the value is less than - /// the bits provided, the unused upper values should be set to zero and will be - /// discarded. - pub fn from_bytes_le(bytes: &[u8], width: usize) -> Self { - assert!(!bytes.is_empty()); - assert!(width <= bytes.len() * 8); - // TODO griffin: Make this sanity check even mildly comprehensible - let overhead = (width.div_ceil(8) * 8) - width; - assert!( - bytes.last().unwrap().leading_zeros() >= overhead as u32, - "The upper byte of the provided value has non-zero values in the padding. Given byte is {} but the upper {} bit(s) should be zero", - bytes.last().unwrap(), - overhead - ); - - let chunks = bytes.chunks_exact(8); - let remainder = chunks.remainder(); - - let mut vec: Vec = chunks - .map(|x| { - usize::from_le_bytes([ - x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], - ]) - }) - .collect(); - - if !remainder.is_empty() { - let mut acc = 0_usize; - for (byte_number, u) in remainder.iter().enumerate() { - acc |= (*u as usize) << (byte_number * 8) - } - vec.push(acc); - } - - let mut bv = BitString::from_vec(vec); - bv.truncate(width); - Value { vec: bv } - } - - pub fn to_bytes(&self) -> Vec { - self.as_bytes_le() - } -} - -/* ============== Impls for Values to make them easier to use ============= */ -#[allow(clippy::from_over_into)] -impl Into for Value { - fn into(self) -> u64 { - self.as_u64() - } -} - -impl Index for Value { - type Output = bool; - - fn index(&self, index: usize) -> &Self::Output { - &self.vec[index] - } -} - -impl std::fmt::Display for Value { - fn fmt( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> Result<(), std::fmt::Error> { - let mut out = String::new(); - write!(out, "[")?; - for bit in self.vec.iter().rev() { - write!(out, "{}", i32::from(*bit))?; - } - write!(out, "]")?; - write!(f, "{}", out) - } -} - -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - self.vec.len() == other.vec.len() && *self.vec == *other.vec - } -} - -impl Eq for Value {} - -impl PartialOrd for Value { - /// Unsigned ordering comparison - fn partial_cmp(&self, other: &Self) -> Option { - assert!(self.vec.len() == other.vec.len()); - for (us_bit, them_bit) in self - .vec - .iter() - .by_refs() - .rev() - .zip(other.vec.iter().by_refs().rev()) - { - match (us_bit, them_bit) { - (true, true) | (false, false) => {} // so far equal - (true, false) => return Some(std::cmp::Ordering::Greater), - (false, true) => return Some(std::cmp::Ordering::Less), - }; - } - Some(std::cmp::Ordering::Equal) - } -} - -impl Serialize for Value { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - // this is stupid - self.to_bytes().serialize(serializer) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::prelude::*; - - proptest! { - #[test] - fn test_byte_roundtrip(data in proptest::collection::vec(any::(), 1..100)) { - // this doesn't really test the truncation since it's been hard to - // get that working in a way that still generates values correctly - // but this is good enough for now - let val = Value::from_bytes_le(&data, data.len() * 8); - let bytes = val.to_bytes(); - prop_assert_eq!(bytes, data); - } - } -} diff --git a/interp/src/tests/values.rs b/interp/src/tests/values.rs index 16afd5c4bf..88d2ad0e21 100644 --- a/interp/src/tests/values.rs +++ b/interp/src/tests/values.rs @@ -1,119 +1,121 @@ #[cfg(test)] mod val_test { - use crate::values::Value; + use baa::*; #[test] fn basic_print_test() { - let v1 = Value::from(12, 5); - println!("12 with bit width 5: {}", v1); - assert_eq!(v1.as_u64(), 12); + let v1 = BitVecValue::from_u64(12, 5); + println!("12 with bit width 5: {:?}", v1); + assert_eq!(v1.to_u64().unwrap(), 12); } #[test] fn basic_print_test2() { - let v1 = Value::from(33, 6); - println!("33 with bit width 6: {}", v1); - assert_eq!(v1.as_u64(), 33); - } - #[test] - fn too_few_bits() { - let v_16_4 = Value::from(16, 4); - println!("16 with bit width 4: {}", v_16_4); - assert_eq!(v_16_4.as_u64(), 0); - let v_31_4 = Value::from(31, 4); - println!("31 with bit width 4: {}", v_31_4); - let v_15_4 = Value::from(15, 4); - println!("15 with bit width 4: {}", v_15_4); - assert_eq!(v_31_4.as_u64(), v_15_4.as_u64()); + let v1 = BitVecValue::from_u64(33, 6); + println!("33 with bit width 6: {:?}", v1); + assert_eq!(v1.to_u64().unwrap(), 33); } #[test] fn ext() { - let v_15_4 = Value::from(15, 4); - assert_eq!(v_15_4.as_u64(), v_15_4.ext(8).as_u64()); + let v_15_4 = BitVecValue::from_u64(15, 4); + assert_eq!( + v_15_4.to_u64().unwrap(), + v_15_4.zero_extend(4).to_u64().unwrap() + ); } } #[cfg(test)] mod unsigned_fixed_point_tests { - use crate::values::Value; + use baa::*; use fraction::Fraction; #[test] fn test_zero() { assert_eq!( - Value::from(/*value=*/ 0, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(0u32, 1u32) ); } #[test] fn test_zero_fractional_width() { assert_eq!( - Value::from(/*value=*/ 0b1110, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 0), + BitVecValue::from_u64(/*value=*/ 0b1110, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 0) + .unwrap(), Fraction::new(14u32, 1u32) ); } #[test] fn test_high_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b1110, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b1110, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(7u32, 2u32) ); } #[test] fn test_middle_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b0110, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b0110, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(3u32, 2u32) ); } #[test] fn test_low_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b0111, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b0111, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(7u32, 4u32) ); } #[test] fn test_low_high_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b1001, /*width=*/ 4) - .as_ufp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b1001, /*width=*/ 4) + .to_unsigned_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(9u32, 4u32) ); } #[test] fn test_32bit_fractional_value() { assert_eq!( - Value::from(/*value=*/ 1u32, /*width=*/ 32) - .as_ufp(/*fractional_width=*/ 31), + BitVecValue::from_u64(/*value=*/ 1, /*width=*/ 32) + .to_unsigned_fixed_point(/*fractional_width=*/ 31) + .unwrap(), Fraction::new(1u32, 2147483648u32) ); } #[test] fn test_64bit_fractional_value() { assert_eq!( - Value::from(/*value=*/ 1u64, /*width=*/ 64) - .as_ufp(/*fractional_width=*/ 63), + BitVecValue::from_u64(/*value=*/ 1u64, /*width=*/ 64) + .to_unsigned_fixed_point(/*fractional_width=*/ 63) + .unwrap(), Fraction::new(1u64, 9223372036854775808u64) ); } #[test] fn test_alternating_ones() { assert_eq!( - Value::from(/*value=*/ 0b10101, /*width=*/ 5) - .as_ufp(/*fractional_width=*/ 3), + BitVecValue::from_u64(/*value=*/ 0b10101, /*width=*/ 5) + .to_unsigned_fixed_point(/*fractional_width=*/ 3) + .unwrap(), Fraction::new(21u32, 8u32) ); } #[test] fn test_all_ones() { assert_eq!( - Value::from(/*value=*/ 0b111, /*width=*/ 3) - .as_ufp(/*fractional_width=*/ 1), + BitVecValue::from_u64(/*value=*/ 0b111, /*width=*/ 3) + .to_unsigned_fixed_point(/*fractional_width=*/ 1) + .unwrap(), Fraction::new(7u32, 2u32) ); } @@ -121,116 +123,129 @@ mod unsigned_fixed_point_tests { #[cfg(test)] mod signed_fixed_point_tests { - use crate::values::Value; + use baa::*; use fraction::Fraction; #[test] fn test_zero() { assert_eq!( - Value::from(/*value=*/ 0, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(0u32, 1u32) ); } #[test] fn test_zero_fractional_width() { assert_eq!( - Value::from(/*value=*/ 0b1110, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 0), + BitVecValue::from_u64(/*value=*/ 0b1110, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 0) + .unwrap(), -Fraction::new(2u32, 1u32) ); } #[test] fn test_high_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b1110, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b1110, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 2) + .unwrap(), -Fraction::new(1u32, 2u32) ); } #[test] fn test_middle_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b0110, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b0110, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(3u32, 2u32) ); } #[test] fn test_low_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b0111, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b0111, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 2) + .unwrap(), Fraction::new(7u32, 4u32) ); } #[test] fn test_mixed_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b10110101, /*width=*/ 8) - .as_sfp(/*fractional_width=*/ 3), + BitVecValue::from_u64(/*value=*/ 0b10110101, /*width=*/ 8) + .to_signed_fixed_point(/*fractional_width=*/ 3) + .unwrap(), -Fraction::new(75u32, 8u32) ); } #[test] fn test_mixed_bits_set2() { assert_eq!( - Value::from(/*value=*/ 0b10100011, /*width=*/ 8) - .as_sfp(/*fractional_width=*/ 4), + BitVecValue::from_u64(/*value=*/ 0b10100011, /*width=*/ 8) + .to_signed_fixed_point(/*fractional_width=*/ 4) + .unwrap(), -Fraction::new(93u32, 16u32) ); } #[test] fn test_mixed_bits_set3() { assert_eq!( - Value::from(/*value=*/ 0b11111101, /*width=*/ 8) - .as_sfp(/*fractional_width=*/ 4), + BitVecValue::from_u64(/*value=*/ 0b11111101, /*width=*/ 8) + .to_signed_fixed_point(/*fractional_width=*/ 4) + .unwrap(), -Fraction::new(3u32, 16u32) ); } #[test] fn test_low_high_bits_set() { assert_eq!( - Value::from(/*value=*/ 0b1001, /*width=*/ 4) - .as_sfp(/*fractional_width=*/ 2), + BitVecValue::from_u64(/*value=*/ 0b1001, /*width=*/ 4) + .to_signed_fixed_point(/*fractional_width=*/ 2) + .unwrap(), -Fraction::new(7u32, 4u32) ); } #[test] fn test_single_bit_set() { assert_eq!( - Value::from( - /*value=*/ 0b10000000000000000000000000000000u32, + BitVecValue::from_u64( + /*value=*/ 0b10000000000000000000000000000000, /*width=*/ 32, ) - .as_sfp(/*fractional_width=*/ 31), + .to_signed_fixed_point(/*fractional_width=*/ 31) + .unwrap(), -Fraction::new(1u32, 1u32) ); } #[test] fn test_small_negative_value() { assert_eq!( - Value::from( - /*value=*/ 0b10000000000000000000000000000001u32, + BitVecValue::from_u64( + /*value=*/ 0b10000000000000000000000000000001, /*width=*/ 32, ) - .as_sfp(/*fractional_width=*/ 31), + .to_signed_fixed_point(/*fractional_width=*/ 31) + .unwrap(), -Fraction::new(2147483647u32, 2147483648u32) ); } #[test] fn test_alternating_ones() { assert_eq!( - Value::from(/*value=*/ 0b10101, /*width=*/ 5) - .as_sfp(/*fractional_width=*/ 3), + BitVecValue::from_u64(/*value=*/ 0b10101, /*width=*/ 5) + .to_signed_fixed_point(/*fractional_width=*/ 3) + .unwrap(), -Fraction::new(11u32, 8u32) ); } #[test] fn test_all_ones() { assert_eq!( - Value::from(/*value=*/ 0b111, /*width=*/ 3) - .as_sfp(/*fractional_width=*/ 1), + BitVecValue::from_u64(/*value=*/ 0b111, /*width=*/ 3) + .to_signed_fixed_point(/*fractional_width=*/ 1) + .unwrap(), -Fraction::new(1u32, 2u32) ); } @@ -238,7 +253,7 @@ mod signed_fixed_point_tests { #[cfg(test)] mod property_tests { - use crate::values::Value; + use baa::*; use num_bigint::{BigInt, BigUint}; use proptest::prelude::*; @@ -248,83 +263,45 @@ mod property_tests { #[test] fn u8_round_trip(input: u8) { - prop_assert_eq!(input as u64, Value::from(input, 8).as_u64()) + prop_assert_eq!(input as u64, BitVecValue::from_u64(input as u64, 8).to_u64().unwrap()) } #[test] fn u16_round_trip(input: u16) { - prop_assert_eq!(input as u64, Value::from(input, 16).as_u64()) + prop_assert_eq!(input as u64, BitVecValue::from_u64(input as u64, 16).to_u64().unwrap()) } #[test] fn u32_round_trip(input: u32) { - prop_assert_eq!(input as u64, Value::from(input, 32).as_u64()) + prop_assert_eq!(input as u64, BitVecValue::from_u64(input as u64, 32).to_u64().unwrap()) } #[test] fn u64_round_trip(input: u64) { - prop_assert_eq!(input, Value::from(input, 64).as_u64()) - } - - #[test] - fn u128_round_trip(input: u128) { - prop_assert_eq!(input, Value::from(input, 128).as_u128()) - } - - #[test] - fn i8_round_trip(input: i8) { - prop_assert_eq!(input as i64, Value::from(input, 8).as_i64()) - } - - #[test] - fn i16_round_trip(input: i16) { - prop_assert_eq!(input as i64, Value::from(input, 16).as_i64()) - } - - #[test] - fn i32_round_trip(input: i32) { - prop_assert_eq!(input as i64, Value::from(input, 32).as_i64()) + prop_assert_eq!(input, BitVecValue::from_u64(input, 64).to_u64().unwrap()) } #[test] fn i64_round_trip(input: i64) { - prop_assert_eq!(input, Value::from(input, 64).as_i64()) + prop_assert_eq!(input, BitVecValue::from_u64(input as u64, 64).to_i64().unwrap()) } - #[test] - fn i128_round_trip(input: i128) { - prop_assert_eq!(input, Value::from(input, 128).as_i128()) - } - - #[test] - fn i128_to_ibig(input: i128) { - let val = Value::from(input, 128); - prop_assert_eq!(val.as_signed(), input.into()) - } - - #[test] - fn u128_to_ubig(input: u128) { - let val = Value::from(input, 128); - prop_assert_eq!(val.as_unsigned(), input.into()) - } - - #[test] fn u32_to_ubig(input: u32) { - let val = Value::from(input, 32); - prop_assert_eq!(val.as_unsigned(), input.into()) + let val = BitVecValue::from_u64(input as u64, 32); + prop_assert_eq!(val.to_big_uint(), input.into()) } #[test] fn i32_to_ibig(input: i32) { - let val = Value::from(input, 32); - prop_assert_eq!(val.as_signed(), input.into()) + let val = BitVecValue::from_i64(input as i64, 32); + prop_assert_eq!(val.to_big_int(), input.into()) } #[test] fn ibig_to_ibig_neg(input in -350_i32..350_i32) { dbg!(input); - let val = Value::from(BigInt::from(input), 32); - prop_assert_eq!(val.as_signed(), BigInt::from(input)) + let val = BitVecValue::from_big_int(&BigInt::from(input), 32); + prop_assert_eq!(val.to_big_int(), BigInt::from(input)) } #[test] @@ -332,8 +309,8 @@ mod property_tests { let in_big: BigUint = input.into(); let mul_big: BigUint = mul.into(); let target: BigUint = in_big * mul_big; - let val = Value::from(target.clone(), target.bits()); - prop_assert_eq!(val.as_unsigned(), target) + let val = BitVecValue::from_big_uint(&target, target.bits() as WidthInt); + prop_assert_eq!(val.to_big_uint(), target) } #[test] @@ -341,9 +318,9 @@ mod property_tests { let in_big: BigInt = input.into(); let mul_big: BigInt = mul.into(); let target: BigInt = in_big * mul_big; - let val = Value::from(target.clone(), target.magnitude().bits() + 1); - println!("{}", val); - prop_assert_eq!(val.as_signed(), target) + let val = BitVecValue::from_big_int(&target, target.magnitude().bits() as WidthInt + 1); + println!("{:?}", val); + prop_assert_eq!(val.to_big_int(), target) } } } diff --git a/tools/btor2/btor2i/src/program.rs b/tools/btor2/btor2i/src/program.rs index 6ece1cc1ab..53035d8f58 100644 --- a/tools/btor2/btor2i/src/program.rs +++ b/tools/btor2/btor2i/src/program.rs @@ -10,8 +10,8 @@ use bitvec::prelude::*; pub type BitString = BitVec; -fn slice_to_usize(slice: &BitSlice) -> usize { - let mut ans: usize = 0; +fn slice_to_u64(slice: &BitSlice) -> u64 { + let mut ans = 0; for i in 0..slice.len() { if slice[i] { ans += 1 << i; @@ -60,7 +60,7 @@ impl<'a> Btor2Program<'a> { pub fn run( &mut self, inputs: HashMap, - ) -> Result, &str> { + ) -> Result, &str> { let btor2_lines: &Vec> = &self .parser .read_lines(self.path) @@ -115,7 +115,7 @@ impl<'a> Btor2Program<'a> { let src_node_idx = line.args()[0] as usize; let output_val = s_env.get(src_node_idx); - output_map.insert(output_name, slice_to_usize(output_val)); + output_map.insert(output_name, slice_to_u64(output_val)); } });