From 48c58aa0194275d2980411b5cb3f270274290719 Mon Sep 17 00:00:00 2001 From: evan-schott <53463459+evan-schott@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:28:30 -0800 Subject: [PATCH] Initialize harvard variable memory to 0 (#329) * fix * fix test * move opt level test * split up tests --- tests/testing-framework/Cargo.toml | 1 + tests/testing-framework/src/lib.rs | 219 +++++++++++++++++++++++++---- vm/src/memory/unified.rs | 2 +- vm/src/memory/variable.rs | 7 +- 4 files changed, 197 insertions(+), 32 deletions(-) diff --git a/tests/testing-framework/Cargo.toml b/tests/testing-framework/Cargo.toml index 9cf7d2564..9e09478bc 100644 --- a/tests/testing-framework/Cargo.toml +++ b/tests/testing-framework/Cargo.toml @@ -15,3 +15,4 @@ nexus-common = { path = "../../common" } nexus-vm = { path = "../../vm" } postcard = { version = "1.0.10", features = ["alloc"] } serde.workspace = true +serial_test = "3.2.0" diff --git a/tests/testing-framework/src/lib.rs b/tests/testing-framework/src/lib.rs index 58d442688..146723689 100644 --- a/tests/testing-framework/src/lib.rs +++ b/tests/testing-framework/src/lib.rs @@ -7,6 +7,7 @@ mod test { use nexus_vm::emulator::{Emulator, HarvardEmulator, LinearEmulator, LinearMemoryLayout}; use postcard::{from_bytes, to_allocvec}; use serde::{de::DeserializeOwned, Serialize}; + use serial_test::serial; use std::{path::PathBuf, process::Command}; use tempfile::{tempdir, TempDir}; @@ -138,7 +139,7 @@ mod test { elfs: Vec, input: Option, expected_output: Option, - expected_result: Result< + expected_result: &Result< (Vec, MemoryTranscript), nexus_vm::error::VMError, >, @@ -160,7 +161,7 @@ mod test { let mut emulator = HarvardEmulator::from_elf(elf.clone(), &input_bytes, &[]); // Check that the program exits correctly. - assert_eq!(emulator.execute(), expected_result); + assert_eq!(&emulator.execute(), expected_result); // Deserialize the output. if expected_output.is_some() { @@ -179,7 +180,7 @@ mod test { LinearEmulator::from_harvard(emulator, elf, &ad, &[]).unwrap(); // Check that the program exits correctly. - assert_eq!(linear_emulator.execute(), expected_result); + assert_eq!(&linear_emulator.execute(), expected_result); // Deserialize the output. if expected_output.is_some() { @@ -213,7 +214,7 @@ mod test { LinearEmulator::from_elf(memory_layout, &ad, elf, &input_bytes, &[]); // Check that the program exits correctly. - assert_eq!(emulator.execute(), expected_result); + assert_eq!(&emulator.execute(), expected_result); // Deserialize the output. if expected_output.is_some() { @@ -229,8 +230,175 @@ mod test { assert_eq!(deserialized_output, expected_output); } + /// Helper function to run test accross multiple emulators, multiple opt levels, and multiple inputs. + fn test_example_multi< + T: Serialize + Clone, + U: Serialize + DeserializeOwned + std::fmt::Debug + PartialEq + Clone, + >( + emulators: Vec, + compile_flags: Vec<&str>, + name: &str, + inputs: Vec, + outputs: Vec, + expected_result: Result< + (Vec, MemoryTranscript), + nexus_vm::error::VMError, + >, + ) { + let elfs = compile_multi(name, &compile_flags); + + for emulator in &emulators { + for (input, output) in inputs.iter().zip(outputs.iter()) { + emulate::( + elfs.clone(), + Some(input.clone()), + Some(output.clone()), + &expected_result, + emulator.clone(), + ); + } + } + } + + #[test] + #[serial] + fn test_fact_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/fact", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_fib_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/fib", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_fib1000_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/fib1000", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_main_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/main", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_palindromes_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/palindromes", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + #[test] - fn test_examples() { + #[serial] + fn test_galeshapley_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/galeshapley", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_lambda_calculus_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/lambda_calculus", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(0)), + ); + } + + #[test] + #[serial] + fn test_fail_example() { + test_example_multi( + vec![ + EmulatorType::Harvard, + EmulatorType::default_linear(), + EmulatorType::TwoPass, + ], + vec!["-C opt-level=3"], + "../../examples/src/fail", + vec![()], + vec![()], + Err(nexus_vm::error::VMError::VMExited(1)), + ); + } + + #[test] + #[ignore] + fn test_examples_all_opt_levels() { let emulators = vec![ EmulatorType::Harvard, EmulatorType::default_linear(), @@ -242,7 +410,15 @@ mod test { "-C opt-level=2", "-C opt-level=3", ]; - let examples = vec!["fact", "fib", "fib1000", "main", "palindromes"]; + let examples = vec![ + "fact", + "fib", + "fib1000", + "main", + "palindromes", + "galeshapley", + "lambda_calculus", + ]; // Test simple examples. for example in examples { @@ -254,7 +430,7 @@ mod test { elfs.clone(), None, Some(()), - Err(nexus_vm::error::VMError::VMExited(0)), + &Err(nexus_vm::error::VMError::VMExited(0)), emulator.clone(), ); } @@ -269,25 +445,21 @@ mod test { fail_elfs.clone(), None, Some(()), - Err(nexus_vm::error::VMError::VMExited(1)), + &Err(nexus_vm::error::VMError::VMExited(1)), emulator.clone(), ); } } #[test] + #[serial] fn test_emulate() { let emulators = vec![ EmulatorType::Harvard, EmulatorType::default_linear(), EmulatorType::TwoPass, ]; - let compile_flags = vec![ - "-C opt-level=0", - "-C opt-level=1", - "-C opt-level=2", - "-C opt-level=3", - ]; + let compile_flags = vec!["-C opt-level=3"]; let io_u32_elfs = compile_multi("io_u32", &compile_flags); let io_u64_elfs = compile_multi("io_u64", &compile_flags); let io_u128_elfs = compile_multi("io_u128", &compile_flags); @@ -297,27 +469,28 @@ mod test { io_u32_elfs.clone(), Some(123u32), Some(123u32), - Err(nexus_vm::error::VMError::VMExited(0)), + &Err(nexus_vm::error::VMError::VMExited(0)), emulator.clone(), ); emulate::( io_u64_elfs.clone(), Some(1u64 << 32), Some(1u64 << 32), - Err(nexus_vm::error::VMError::VMExited(0)), + &Err(nexus_vm::error::VMError::VMExited(0)), emulator.clone(), ); emulate::( io_u128_elfs.clone(), Some(332306998946228968225970211937533483u128), Some(332306998946228968225970211937533483u128), - Err(nexus_vm::error::VMError::VMExited(0)), + &Err(nexus_vm::error::VMError::VMExited(0)), emulator, ); } } #[test] + #[serial] fn test_fib() { let inputs = vec![1u32, 10u32, 20u32]; let outputs = vec![1u32, 34u32, 4181u32]; @@ -326,15 +499,7 @@ mod test { EmulatorType::default_linear(), EmulatorType::TwoPass, ]; - let elfs = compile_multi( - "fib", - &[ - "-C opt-level=0", - "-C opt-level=1", - "-C opt-level=2", - "-C opt-level=3", - ], - ); + let elfs = compile_multi("fib", &["-C opt-level=3"]); for (input, output) in inputs.iter().zip(outputs.iter()) { for emulator in emulators.clone() { @@ -342,7 +507,7 @@ mod test { elfs.clone(), Some(input.clone()), Some(output.clone()), - Err(nexus_vm::error::VMError::VMExited(0)), + &Err(nexus_vm::error::VMError::VMExited(0)), emulator.clone(), ); } diff --git a/vm/src/memory/unified.rs b/vm/src/memory/unified.rs index 4b0247428..4c8ffb0ac 100644 --- a/vm/src/memory/unified.rs +++ b/vm/src/memory/unified.rs @@ -916,7 +916,7 @@ mod tests { // Read from an uninitialized address assert_eq!( memory.read(0x4000, MemAccessSize::Word), - Err(MemoryError::InvalidMemoryAccess(0x4000)) + Ok(LoadOp::Op(MemAccessSize::Word, 0x4000, 0x00000000)) ); } diff --git a/vm/src/memory/variable.rs b/vm/src/memory/variable.rs index ed4d95d34..55ed37109 100644 --- a/vm/src/memory/variable.rs +++ b/vm/src/memory/variable.rs @@ -108,8 +108,7 @@ impl VariableMemory { .0 .get(&aligned_address) // Align to word boundary .map(|&value| ((value >> shift) & mask)) - .ok_or(MemoryError::InvalidMemoryAccess(address))?; - + .unwrap_or(0); Ok(LoadOp::Op(size, address, value)) } /// For bounded segments, returns a slice of memory between start and end addresses, if they form a contiguous segment. @@ -441,13 +440,13 @@ mod tests { } #[test] - fn test_invalid_read() { + fn test_uninitialized_read() { let memory = VariableMemory::::default(); // Read from an uninitialized address assert_eq!( memory.read(0x2000, MemAccessSize::Word), - Err(MemoryError::InvalidMemoryAccess(0x2000)) + Ok(LoadOp::Op(MemAccessSize::Word, 0x2000, 0x00000000)) ); }