diff --git a/codegen/masm/src/codegen/emit/mem.rs b/codegen/masm/src/codegen/emit/mem.rs index 4791afb88..786fdefb5 100644 --- a/codegen/masm/src/codegen/emit/mem.rs +++ b/codegen/masm/src/codegen/emit/mem.rs @@ -49,7 +49,7 @@ impl<'a> OpEmitter<'a> { let ptr = self.stack.pop().expect("operand stack is empty"); match ptr.ty() { Type::Ptr(_) => { - // Converet the pointer to a native pointer representation + // Convert the pointer to a native pointer representation self.emit_native_ptr(); match &ty { Type::I128 => self.load_quad_word(None), diff --git a/codegen/masm/src/emulator/memory.rs b/codegen/masm/src/emulator/memory.rs new file mode 100644 index 000000000..1eb01db5f --- /dev/null +++ b/codegen/masm/src/emulator/memory.rs @@ -0,0 +1,59 @@ +use std::{ + collections::BTreeSet, + fmt::Debug, + ops::{Index, IndexMut}, +}; + +use miden_core::FieldElement; +use midenc_hir::Felt; + +const EMPTY_WORD: [Felt; 4] = [Felt::ZERO; 4]; + +pub struct Memory { + memory: Vec<[Felt; 4]>, + set_memory_addrs: BTreeSet, +} + +impl Memory { + pub fn new(memory_size: usize) -> Self { + Self { + memory: vec![EMPTY_WORD; memory_size], + set_memory_addrs: Default::default(), + } + } + + pub fn len(&self) -> usize { + self.memory.len() + } + + pub fn reset(&mut self) { + for addr in self.set_memory_addrs.iter() { + self.memory[*addr] = EMPTY_WORD; + } + self.set_memory_addrs = Default::default(); + } +} + +impl Index for Memory { + type Output = [Felt; 4]; + + fn index(&self, index: usize) -> &Self::Output { + &self.memory[index] + } +} + +impl IndexMut for Memory { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.set_memory_addrs.insert(index); + &mut self.memory[index] + } +} + +impl Debug for Memory { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for addr in self.set_memory_addrs.iter() { + write!(f, "{}: {:?}, ", addr, self[*addr])?; + } + Ok(()) + } +} diff --git a/codegen/masm/src/emulator/mod.rs b/codegen/masm/src/emulator/mod.rs index b1ae25e58..c3c80e6d1 100644 --- a/codegen/masm/src/emulator/mod.rs +++ b/codegen/masm/src/emulator/mod.rs @@ -2,9 +2,11 @@ mod breakpoints; mod debug; mod events; mod functions; +mod memory; use std::{cell::RefCell, cmp, rc::Rc, sync::Arc}; +use memory::Memory; use miden_assembly::{ast::ProcedureName, LibraryNamespace}; use midenc_hir::{assert_matches, Felt, FieldElement, FunctionIdent, Ident, OperandStack, Stack}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -118,7 +120,7 @@ pub struct Emulator { locals: FxHashMap, modules_loaded: FxHashMap>, modules_pending: FxHashSet, - memory: Vec<[Felt; 4]>, + memory: Memory, stack: OperandStack, advice_stack: OperandStack, callstack: Vec, @@ -131,17 +133,22 @@ pub struct Emulator { clk: usize, clk_limit: usize, entrypoint: Option, + print_trace: bool, } impl Default for Emulator { fn default() -> Self { - Self::new(Self::DEFAULT_HEAP_SIZE, Self::DEFAULT_HEAP_START, Self::DEFAULT_LOCALS_START) + Self::new( + Self::DEFAULT_HEAP_SIZE, + Self::DEFAULT_HEAP_START, + Self::DEFAULT_LOCALS_START, + false, + ) } } impl Emulator { pub const DEFAULT_HEAP_SIZE: u32 = (4 * Self::PAGE_SIZE) / 16; pub const DEFAULT_HEAP_START: u32 = (2 * Self::PAGE_SIZE) / 16; pub const DEFAULT_LOCALS_START: u32 = (3 * Self::PAGE_SIZE) / 16; - const EMPTY_WORD: [Felt; 4] = [Felt::ZERO; 4]; const PAGE_SIZE: u32 = 64 * 1024; /// Construct a new, empty emulator with: @@ -149,8 +156,8 @@ impl Emulator { /// * A linear memory heap of `memory_size` words /// * The start of the usable heap set to `hp` (an address in words) /// * The start of the reserved heap used for locals set to `lp` (an address in words) - pub fn new(memory_size: u32, hp: u32, lp: u32) -> Self { - let memory = vec![Self::EMPTY_WORD; memory_size as usize]; + pub fn new(memory_size: u32, hp: u32, lp: u32, print_stack: bool) -> Self { + let memory = Memory::new(memory_size as usize); Self { status: Status::Init, functions: Default::default(), @@ -170,6 +177,7 @@ impl Emulator { clk: 0, clk_limit: usize::MAX, entrypoint: None, + print_trace: print_stack, } } @@ -570,7 +578,7 @@ impl Emulator { pub fn stop(&mut self) { self.callstack.clear(); self.stack.clear(); - self.memory = vec![Self::EMPTY_WORD; self.memory.len()]; + self.memory.reset(); self.hp = self.hp_start; self.lp = self.lp_start; self.step_over = None; @@ -887,7 +895,12 @@ macro_rules! peek_u32 { macro_rules! pop_addr { ($emu:ident) => {{ let addr = pop_u32!($emu, "expected valid 32-bit address, got {}") as usize; - assert!(addr < $emu.memory.len(), "out of bounds memory access"); + assert!( + addr < $emu.memory.len(), + "out of bounds memory access, addr: {}, available memory: {}", + addr, + $emu.memory.len() + ); addr }}; } @@ -1241,6 +1254,11 @@ impl Emulator { // control flow effect occurred to reach it let ix_with_op = state.next(); if let Some(ix_with_op) = ix_with_op { + if self.print_trace { + eprintln!("mem: {:?}", self.memory); + eprintln!("stk: {}", self.stack.debug()); + eprintln!("op>: {:?}", ix_with_op.op); + } match ix_with_op.op { Op::Padw => { self.stack.padw(); diff --git a/codegen/masm/src/tests.rs b/codegen/masm/src/tests.rs index f3c57c921..bc7b0eeae 100644 --- a/codegen/masm/src/tests.rs +++ b/codegen/masm/src/tests.rs @@ -36,13 +36,19 @@ struct TestByEmulationHarness { } #[allow(unused)] impl TestByEmulationHarness { - pub fn with_emulator_config(memory_size: usize, hp: usize, lp: usize) -> Self { + pub fn with_emulator_config( + memory_size: usize, + hp: usize, + lp: usize, + print_stack: bool, + ) -> Self { let mut harness = Self { context: TestContext::default(), emulator: Emulator::new( memory_size.try_into().expect("invalid memory size"), hp.try_into().expect("invalid address"), lp.try_into().expect("invalid address"), + print_stack, ), }; harness.set_cycle_budget(2000); diff --git a/hir/src/asm/stack.rs b/hir/src/asm/stack.rs index dc864bc08..7e4bf1480 100644 --- a/hir/src/asm/stack.rs +++ b/hir/src/asm/stack.rs @@ -488,7 +488,22 @@ impl<'a, E: StackElement, T: ?Sized + Stack> fmt::Debug for DebugSt .finish() } } - +impl<'a, E: StackElement + fmt::Debug, T: ?Sized + Stack> fmt::Display + for DebugStack<'a, T> +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list() + .entries( + self.stack + .stack() + .iter() + .rev() + .enumerate() + .take(self.limit.unwrap_or(self.stack.len())), + ) + .finish() + } +} #[cfg(test)] mod tests { use super::*;