diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1a9f9f5963..f796428231 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,8 +20,6 @@ jobs: uses: actions/checkout@v4 - name: Install Rust stable uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - name: Build calyx dev run: cargo build - name: Check calyx build diff --git a/Cargo.lock b/Cargo.lock index af1bbb683b..e5db4631e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -429,6 +429,7 @@ version = "0.7.1" dependencies = [ "atty", "itertools 0.11.0", + "lazy_static", "petgraph", "serde", "serde_json", diff --git a/calyx-frontend/src/parser.rs b/calyx-frontend/src/parser.rs index ce1044e934..47938508dc 100644 --- a/calyx-frontend/src/parser.rs +++ b/calyx-frontend/src/parser.rs @@ -60,10 +60,13 @@ impl CalyxParser { let file = GlobalPositionTable::as_mut() .add_file(path.to_string_lossy().to_string(), string_content); let user_data = UserData { file }; - let content = GlobalPositionTable::as_ref().get_source(file); + let gpos_ref = GlobalPositionTable::as_ref(); + let content = gpos_ref.get_source(file).to_owned(); + drop(gpos_ref); + // Parse the file let inputs = - CalyxParser::parse_with_userdata(Rule::file, content, user_data) + CalyxParser::parse_with_userdata(Rule::file, &content, user_data) .map_err(|e| e.with_path(&path.to_string_lossy())) .map_err(|e| { calyx_utils::Error::parse_error(e.variant.message()) @@ -96,10 +99,13 @@ impl CalyxParser { let file = GlobalPositionTable::as_mut().add_file("".to_string(), buf); let user_data = UserData { file }; - let contents = GlobalPositionTable::as_ref().get_source(file); + let gpos_ref = GlobalPositionTable::as_ref(); + let content = gpos_ref.get_source(file).to_owned(); + drop(gpos_ref); + // Parse the input let inputs = - CalyxParser::parse_with_userdata(Rule::file, contents, user_data) + CalyxParser::parse_with_userdata(Rule::file, &content, user_data) .map_err(|e| { calyx_utils::Error::parse_error(e.variant.message()) .with_pos(&Self::error_span(&e, file)) diff --git a/calyx-utils/Cargo.toml b/calyx-utils/Cargo.toml index da438c0d4c..6d3dc94693 100644 --- a/calyx-utils/Cargo.toml +++ b/calyx-utils/Cargo.toml @@ -23,3 +23,4 @@ string-interner.workspace = true itertools.workspace = true petgraph.workspace = true symbol_table = { version = "0.3", features = ["global"] } +lazy_static.workspace = true diff --git a/calyx-utils/src/errors.rs b/calyx-utils/src/errors.rs index b087d017aa..b560fdeec0 100644 --- a/calyx-utils/src/errors.rs +++ b/calyx-utils/src/errors.rs @@ -179,7 +179,7 @@ impl Error { post_msg: None, } } - pub fn location(&self) -> (&str, usize, usize) { + pub fn location(&self) -> (String, usize, usize) { self.pos.get_location() } pub fn message(&self) -> String { diff --git a/calyx-utils/src/position.rs b/calyx-utils/src/position.rs index 2b74e99a24..fb5333897f 100644 --- a/calyx-utils/src/position.rs +++ b/calyx-utils/src/position.rs @@ -1,7 +1,12 @@ //! Definitions for tracking source position information of Calyx programs use itertools::Itertools; -use std::{cmp, fmt::Write, mem, sync}; +use lazy_static::lazy_static; +use std::{ + cmp, + fmt::Write, + sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] /// Handle to a position in a [PositionTable] @@ -99,28 +104,28 @@ impl PositionTable { /// The global position table pub struct GlobalPositionTable; +lazy_static! { + static ref GPOS_TABLE: RwLock = + RwLock::new(PositionTable::default()); +} + impl GlobalPositionTable { - /// Return reference to a global [PositionTable] - pub fn as_mut() -> &'static mut PositionTable { - static mut SINGLETON: mem::MaybeUninit = - mem::MaybeUninit::uninit(); - static ONCE: sync::Once = sync::Once::new(); - - // SAFETY: - // - writing to the singleton is OK because we only do it one time - // - the ONCE guarantees that SINGLETON is init'ed before assume_init_ref - unsafe { - ONCE.call_once(|| { - SINGLETON.write(PositionTable::new()); - assert!(PositionTable::UNKNOWN == GPosIdx::UNKNOWN.0) - }); - SINGLETON.assume_init_mut() - } + /// Return reference to a global [PositionTable]. + /// + /// # Safety + /// + /// You may not call this function after any call to [`Self::as_ref`]. + pub fn as_mut() -> RwLockWriteGuard<'static, PositionTable> { + GPOS_TABLE + .try_write() + .expect("failed to get write lock for global position table") } /// Return an immutable reference to the global position table - pub fn as_ref() -> &'static PositionTable { - Self::as_mut() + pub fn as_ref() -> RwLockReadGuard<'static, PositionTable> { + GPOS_TABLE + .try_read() + .expect("failed to get read lock for global position table") } } @@ -152,7 +157,7 @@ impl GPosIdx { /// 1. lines associated with this span /// 2. start position of the first line in span /// 3. line number of the span - fn get_lines(&self) -> (Vec<&str>, usize, usize) { + fn get_lines(&self) -> (Vec, usize, usize) { let table = GlobalPositionTable::as_ref(); let pos_d = table.get_pos(self.0); let file = &table.get_file_data(pos_d.file).source; @@ -181,16 +186,20 @@ impl GPosIdx { pos = next_pos + 1; linum += 1; } - (buf, out_idx, out_line) + ( + buf.into_iter().map(|str| str.to_owned()).collect(), + out_idx, + out_line, + ) } /// returns: /// 1. the name of the file the span is in /// 2. the (inclusive) range of lines within the span - pub fn get_line_num(&self) -> (&String, (usize, usize)) { + pub fn get_line_num(&self) -> (String, (usize, usize)) { let table = GlobalPositionTable::as_ref(); let pos_data = table.get_pos(self.0); - let file_name = &table.get_file_data(pos_data.file).name; + let file_name = table.get_file_data(pos_data.file).name.clone(); let (buf, _, line_num) = self.get_lines(); //reformat to return the range (inclusive) let rng = (line_num, line_num + buf.len() - 1); @@ -205,7 +214,7 @@ impl GPosIdx { let (lines, pos, linum) = self.get_lines(); let mut buf = String::new(); - let l = lines[0]; + let l = lines[0].as_str(); let linum_text = format!("{} ", linum); let linum_space: String = " ".repeat(linum_text.len()); let mark: String = "^".repeat(cmp::min( @@ -238,17 +247,17 @@ impl GPosIdx { buf } - pub fn get_location(&self) -> (&str, usize, usize) { + pub fn get_location(&self) -> (String, usize, usize) { let table = GlobalPositionTable::as_ref(); let pos_d = table.get_pos(self.0); - let name = &table.get_file_data(pos_d.file).name; + let name = table.get_file_data(pos_d.file).name.clone(); (name, pos_d.start, pos_d.end) } /// Visualizes the span without any message or marking pub fn show(&self) -> String { let (lines, _, linum) = self.get_lines(); - let l = lines[0]; + let l = lines[0].as_str(); let linum_text = format!("{} ", linum); format!("{}|{}\n", linum_text, l) } diff --git a/frontends/queues/README.md b/frontends/queues/README.md index 139e974843..882359c905 100644 --- a/frontends/queues/README.md +++ b/frontends/queues/README.md @@ -1,12 +1,35 @@ # Queues Library -See the [docs](https://docs.calyxir.org/frontends/queues.html) for more details. +See the [docs][docs] for more details. ## Installation To use our queues: -1. Install [flit](https://flit.readthedocs.io/en/latest/#install) +1. Install [flit][flit] 2. Install the `queues` package: ``` $ cd frontends/queues/ $ flit install --symlink +``` + +## Converting Tests to Calyx + +To convert any of our [randomized tests][testing-harness] to a single Calyx file and their associated data and expect files: + +0. Follow the [installation instructions](#installation) +1. Choose a test by picking a `.py` file in [`tests/`][tests-dir] +2. Convert the test to Calyx: +``` +python3 _test.py 20000 --keepgoing > _test.futil ``` +3. Run the script [`gen_test_data.sh`][gen_test_data.sh] to generate data and expect files: +``` +./gen_test_data.sh +``` + +The files `_test.py`, `_test.data`, and `_test.expect` contain the Calyx program, input data, and expected outputs for the test. + +[docs]: https://docs.calyxir.org/frontends/queues.html +[flit]: https://flit.readthedocs.io/en/latest/#install +[testing-harness]: https://docs.calyxir.org/frontends/queues.html#shared-testing-harness +[tests-dir]: ./tests/ +[gen_test_data.sh]: ./test_data_gen/gen_test_data.sh \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..2e2b8c8521 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.82.0" diff --git a/tools/data-conversion/src/float_special_values b/tools/data-conversion/src/float_special_values new file mode 100644 index 0000000000..96aadf84ef --- /dev/null +++ b/tools/data-conversion/src/float_special_values @@ -0,0 +1,32 @@ +use num_bigint::BigUint; +use std::str::FromStr; + +pub struct IntermediateRepresentation { + pub sign: bool, + pub mantissa: BigUint, + pub exponent: i64, // Arbitrary precision exponent +} + +impl IntermediateRepresentation { + // Function to check if the value is NaN + pub fn is_nan(&self, bit_width: usize) -> bool { + let max_exponent_value = (1 << (bit_width - 1)) - 1; // Max exponent for NaN + self.exponent == max_exponent_value as i64 && !self.mantissa.is_zero() + } + + // Function to check if the value is infinity + pub fn is_infinity(&self, bit_width: usize) -> bool { + let max_exponent_value = (1 << (bit_width - 1)) - 1; // Max exponent for infinity + self.exponent == max_exponent_value as i64 && self.mantissa.is_zero() + } + + // Function to check if the value is denormalized + pub fn is_denormalized(&self) -> bool { + self.exponent == 0 && !self.mantissa.is_zero() + } + + // Function to check if the value is zero + pub fn is_zero(&self) -> bool { + self.exponent == 0 && self.mantissa.is_zero() + } +}