diff --git a/Cargo.lock b/Cargo.lock index 8b28c18..32b610b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,6 +144,8 @@ dependencies = [ "alloy-primitives", "anyhow", "once_cell", + "serde", + "serde_json", "tracing", ] @@ -204,7 +206,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.32", ] [[package]] @@ -447,9 +449,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -562,6 +564,12 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + [[package]] name = "semver" version = "1.0.18" @@ -570,9 +578,34 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "serde_json" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] name = "sharded-slab" @@ -617,9 +650,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.10" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", diff --git a/crates/mipsevm/Cargo.toml b/crates/mipsevm/Cargo.toml index 61424ed..ffbc093 100644 --- a/crates/mipsevm/Cargo.toml +++ b/crates/mipsevm/Cargo.toml @@ -9,6 +9,8 @@ authors.workspace = true [dependencies] alloy-primitives = "0.3.3" once_cell = "1.18.0" +serde = "1.0.188" +serde_json = "1.0.106" anyhow = "1.0.70" tracing = "0.1.37" diff --git a/crates/mipsevm/src/lib.rs b/crates/mipsevm/src/lib.rs index 6dc524d..e901da2 100644 --- a/crates/mipsevm/src/lib.rs +++ b/crates/mipsevm/src/lib.rs @@ -2,6 +2,7 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features, dead_code)] +mod memory; mod page; mod state; mod traits; diff --git a/crates/mipsevm/src/memory.rs b/crates/mipsevm/src/memory.rs new file mode 100644 index 0000000..75df397 --- /dev/null +++ b/crates/mipsevm/src/memory.rs @@ -0,0 +1,91 @@ +//! The memory module contains the memory data structures and functionality for the emulator. + +use crate::page::{self, CachedPage}; +use alloy_primitives::B256; +use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; + +type PageIndex = u32; + +struct Memory { + /// Map of generalized index -> the merkle root of each index. None if invalidated. + nodes: BTreeMap>, + /// Map of page indices to [CachedPage]s. + pages: BTreeMap>>, + /// We store two caches upfront; we often read instructions from one page and reserve another + /// for scratch memory. This prevents map lookups for each instruction. + last_page: [(PageIndex, Option>>); 2], +} + +impl Default for Memory { + fn default() -> Self { + Self { + nodes: BTreeMap::default(), + pages: BTreeMap::default(), + last_page: [(!0u32, None), (!0u32, None)], + } + } +} + +impl Memory { + /// Returns the number of allocated pages in memory. + pub fn page_count(&self) -> usize { + self.pages.len() + } + + /// Performs an operation on all pages in the memory. + /// + /// ### Takes + /// - `f`: A function that takes a page index and a mutable reference to a [CachedPage]. + pub fn for_each_page(&mut self, mut f: impl FnMut(u64, Rc>)) { + for (key, page) in self.pages.iter() { + f(*key, Rc::clone(page)); + } + } + + /// Invalidate a given memory address + /// + /// ### Takes + /// - `address`: The address to invalidate. + pub fn invalidate(&mut self, address: u32) { + if address & 0x3 != 0 { + panic!("Unaligned memory access: {:x}", address); + } + + // Find the page and invalidate the address within it. + if let Some(page) = self.page_lookup(address >> page::PAGE_ADDRESS_SIZE) { + let mut page = page.borrow_mut(); + page.invalidate(address & page::PAGE_ADDRESS_MASK as u32); + if !page.valid[1] { + return; + } + } else { + // Nothing to invalidate + return; + } + + // Find the generalized index of the first page covering the address + let mut g_index = ((1 << 32) | address) >> page::PAGE_ADDRESS_SIZE as u32; + while g_index > 0 { + self.nodes.insert(g_index.into(), None); + g_index >>= 1; + } + } + + fn page_lookup(&mut self, page_index: PageIndex) -> Option>> { + // Check caches before maps + if let Some((_, Some(page))) = self.last_page.iter().find(|(key, _)| *key == page_index) { + return Some(Rc::clone(page)); + } else if let Some(page) = self.pages.get(&(page_index as u64)) { + // Cache the page + self.last_page[1] = self.last_page[0].clone(); + self.last_page[0] = (page_index, Some(Rc::clone(page))); + + Some(Rc::clone(page)) + } else { + None + } + } +} + +#[cfg(test)] +mod test {} diff --git a/crates/mipsevm/src/page.rs b/crates/mipsevm/src/page.rs index 9c0668c..942e99f 100644 --- a/crates/mipsevm/src/page.rs +++ b/crates/mipsevm/src/page.rs @@ -25,13 +25,14 @@ pub(crate) static ZERO_HASHES: Lazy<[B256; 256]> = Lazy::new(|| { pub type Page = [u8; PAGE_SIZE]; /// A [CachedPage] is a [Page] with an in-memory cache of intermediate nodes. +#[derive(Debug, Clone, Copy)] pub struct CachedPage { - data: Page, + pub data: Page, /// Storage for intermediate nodes - cache: [[u8; 32]; PAGE_SIZE >> 5], + pub cache: [[u8; 32]; PAGE_SIZE >> 5], /// Maps to true if the node is valid /// TODO(clabby): Use a bitmap / roaring bitmap - valid: [bool; PAGE_SIZE >> 5], + pub valid: [bool; PAGE_SIZE >> 5], } impl Default for CachedPage {