midenc_codegen_masm/emulator/
mod.rs

1mod breakpoints;
2mod debug;
3mod events;
4mod functions;
5mod memory;
6
7use std::{cell::RefCell, cmp, rc::Rc, sync::Arc};
8
9use memory::Memory;
10use miden_assembly::{ast::ProcedureName, LibraryNamespace};
11use midenc_hir::{assert_matches, Felt, FieldElement, FunctionIdent, Ident, OperandStack, Stack};
12use rustc_hash::{FxHashMap, FxHashSet};
13
14use self::functions::{Activation, Stub};
15pub use self::{
16    breakpoints::*,
17    debug::{CallFrame, DebugInfo, DebugInfoWithStack},
18    events::{BreakpointEvent, ControlEffect, EmulatorEvent},
19    functions::{Instruction, InstructionWithOp, NativeFn},
20};
21use crate::{BlockId, Function, Module, Op, Program};
22
23/// This type represents the various sorts of errors which can occur when
24/// running the emulator on a MASM program. Some errors may result in panics,
25/// but those which we can handle are represented here.
26#[derive(Debug, Clone, thiserror::Error, PartialEq)]
27pub enum EmulationError {
28    /// The given module is already loaded
29    #[error("unable to load module: '{0}' is already loaded")]
30    AlreadyLoaded(Ident),
31    /// The given function is already loaded
32    #[error("unable to load function: '{0}' is already loaded")]
33    DuplicateFunction(FunctionIdent),
34    /// The given function cannot be found
35    #[error("unable to invoke function: '{0}' is not defined")]
36    UndefinedFunction(FunctionIdent),
37    /// The emulator ran out of available memory
38    #[error("system limit: out of memory")]
39    OutOfMemory,
40    /// The emulator was terminated due to a program failing to terminate in its budgeted time
41    #[error("execution terminated prematurely: maximum cycle count reached")]
42    CycleBudgetExceeded,
43    /// A breakpoint was reached, so execution was suspended and can be resumed
44    #[error("execution suspended by breakpoint")]
45    BreakpointHit(BreakpointEvent),
46    /// An attempt was made to run the emulator without specifying an entrypoint
47    #[error("unable to start the emulator without an entrypoint")]
48    NoEntrypoint,
49}
50
51/// The size/type of pointers in the emulator
52pub type Addr = u32;
53
54#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55pub struct InstructionPointer {
56    /// The block in which the instruction pointer is located
57    pub block: BlockId,
58    /// The index of the instruction pointed to
59    pub index: usize,
60}
61impl InstructionPointer {
62    pub const fn new(block: BlockId) -> Self {
63        Self { block, index: 0 }
64    }
65}
66
67/// This enum represents the state transitions for the emulator.
68///
69/// * The emulator starts in `Init`
70/// * Once some code is loaded, it becomes `Loaded`
71/// * Once the emulator has started executing some code, it becomes `Started`
72/// * If the emulator suspends due to a breakpoint or stepping, it becomes `Suspended`
73/// * Once the emulator finishes executing whatever entrypoint was invoked, it becomes `Stopped`
74/// * If an error occurs between `Started` and `Stopped`, it becomes `Faulted`
75///
76/// Once `Started`, it is not possible to `start` the emulator again until it reaches the
77/// `Stopped` state, or is explicitly reset to the `Init` or `Loaded` states using `reset`
78/// or `stop` respectively.
79#[derive(Debug, Default)]
80enum Status {
81    /// The emulator is in its initial state
82    ///
83    /// In this state, the emulator cannot execute any code because there
84    /// is no code loaded yet.
85    #[default]
86    Init,
87    /// A program has been loaded into the emulator, but not yet started
88    ///
89    /// This is the clean initial state from which a program or function can
90    /// start executing. Once the emulator leaves this status, the state of
91    /// the emulator is "dirty", i.e. it is no longer a clean slate.
92    Loaded,
93    /// The emulator has started running the current program, or a specified function.
94    Started,
95    /// The emulator is suspended, and awaiting resumption
96    Suspended,
97    /// The emulator finished running the current program, or a specified function,
98    /// and the state of the emulator has not yet been reset.
99    Stopped,
100    /// The emulator has stopped due to an error, and cannot proceed further
101    Faulted(EmulationError),
102}
103
104/// [Emulator] provides us with a means to execute our MASM IR directly
105/// without having to emit "real" MASM and run it via the Miden VM.
106/// In other words, it's a convenient way to run tests to verify the
107/// expected behavior of a program without all of the baggage of the
108/// Miden VM.
109///
110/// [Emulator] is necessarily a more limited execution environment:
111///
112/// * It only handles instructions which are defined in the [Op] enum
113/// * Anything related to proving, calling contracts, etc. is not supported
114/// * The default environment is empty, i.e. there are no Miden VM standard
115///   library functions available. Users must emit Miden IR for all functions
116///   they wish to call, or alternatively, provide native stubs.
117pub struct Emulator {
118    status: Status,
119    functions: FxHashMap<FunctionIdent, Stub>,
120    locals: FxHashMap<FunctionIdent, Addr>,
121    modules_loaded: FxHashMap<Ident, Arc<Module>>,
122    modules_pending: FxHashSet<Ident>,
123    memory: Memory,
124    stack: OperandStack<Felt>,
125    advice_stack: OperandStack<Felt>,
126    callstack: Vec<Activation>,
127    hp_start: u32,
128    hp: u32,
129    lp_start: u32,
130    lp: u32,
131    breakpoints: BreakpointManager,
132    step_over: Option<InstructionPointer>,
133    clk: usize,
134    clk_limit: usize,
135    entrypoint: Option<FunctionIdent>,
136    print_trace: bool,
137}
138impl Default for Emulator {
139    fn default() -> Self {
140        Self::new(
141            Self::DEFAULT_HEAP_SIZE,
142            Self::DEFAULT_HEAP_START,
143            Self::DEFAULT_LOCALS_START,
144            false,
145        )
146    }
147}
148impl Emulator {
149    pub const DEFAULT_HEAP_SIZE: u32 = (4 * Self::PAGE_SIZE) / 16;
150    pub const DEFAULT_HEAP_START: u32 = (2 * Self::PAGE_SIZE) / 16;
151    pub const DEFAULT_LOCALS_START: u32 = (3 * Self::PAGE_SIZE) / 16;
152    const PAGE_SIZE: u32 = 64 * 1024;
153
154    /// Construct a new, empty emulator with:
155    ///
156    /// * A linear memory heap of `memory_size` words
157    /// * The start of the usable heap set to `hp` (an address in words)
158    /// * The start of the reserved heap used for locals set to `lp` (an address in words)
159    pub fn new(memory_size: u32, hp: u32, lp: u32, print_stack: bool) -> Self {
160        let memory = Memory::new(memory_size as usize);
161        Self {
162            status: Status::Init,
163            functions: Default::default(),
164            locals: Default::default(),
165            modules_loaded: Default::default(),
166            modules_pending: Default::default(),
167            memory,
168            stack: Default::default(),
169            advice_stack: Default::default(),
170            callstack: vec![],
171            hp_start: hp,
172            hp,
173            lp_start: lp,
174            lp,
175            breakpoints: Default::default(),
176            step_over: None,
177            clk: 0,
178            clk_limit: usize::MAX,
179            entrypoint: None,
180            print_trace: print_stack,
181        }
182    }
183
184    /// Place a cap on the number of cycles the emulator will execute before failing with an error
185    pub fn set_max_cycles(&mut self, max: usize) {
186        self.clk_limit = max;
187    }
188
189    /// Returns all watchpoints that are currently managed by this [BreakpointManager]
190    pub fn watchpoints(&self) -> impl Iterator<Item = Watchpoint> + '_ {
191        self.breakpoints.watchpoints()
192    }
193
194    /// Returns all breakpoints that are currently managed by this [BreakpointManager]
195    pub fn breakpoints(&self) -> impl Iterator<Item = Breakpoint> {
196        self.breakpoints.breakpoints()
197    }
198
199    /// Sets a breakpoint for the emulator
200    pub fn set_breakpoint(&mut self, bp: Breakpoint) {
201        self.breakpoints.set(bp);
202    }
203
204    /// Removes the given breakpoint from the emulator
205    pub fn clear_breakpoint(&mut self, bp: Breakpoint) {
206        self.breakpoints.unset(bp);
207    }
208
209    /// Removes the all breakpoints from the emulator
210    pub fn clear_breakpoints(&mut self) {
211        self.breakpoints.unset_all();
212    }
213
214    /// Sets a watchpoint in the emulator
215    pub fn set_watchpoint(&mut self, addr: Addr, size: u32, mode: WatchMode) -> WatchpointId {
216        self.breakpoints.watch(addr, size, mode)
217    }
218
219    /// Sets a watchpoint in the emulator
220    pub fn clear_watchpoint(&mut self, id: WatchpointId) {
221        self.breakpoints.unwatch(id);
222    }
223
224    /// Set the watch mode for a [Watchpoint] using the identifier returned by [watch]
225    pub fn watchpoint_mode(&mut self, id: WatchpointId, mode: WatchMode) {
226        self.breakpoints.watch_mode(id, mode);
227    }
228
229    /// Clears all watchpoints
230    pub fn clear_watchpoints(&mut self) {
231        self.breakpoints.unwatch_all();
232    }
233
234    /// Clear all breakpoints and watchpoints
235    pub fn clear_break_and_watchpoints(&mut self) {
236        self.breakpoints.clear();
237    }
238
239    /// Get's debug information about the current emulator state
240    pub fn info(&self) -> Option<DebugInfo<'_>> {
241        let current = self.callstack.last()?;
242        // This returns the pending activation state for the current function,
243        // i.e. the next instruction to be executed, what control flow effects
244        // will occur to reach that instruction, and the actual instruction pointer
245        let ip = current.peek_with_op();
246        Some(DebugInfo {
247            cycle: self.clk,
248            function: current.function().name,
249            fp: current.fp(),
250            ip,
251            stack: &self.stack,
252        })
253    }
254
255    /// Get a stacktrace for the code running in the emulator
256    pub fn stacktrace(&self) -> Vec<CallFrame> {
257        let mut frames = Vec::with_capacity(self.callstack.len());
258        for frame in self.callstack.iter() {
259            frames.push(CallFrame {
260                function: frame.function().name,
261                fp: frame.fp(),
262                ip: Some(frame.ip()),
263            })
264        }
265        frames
266    }
267
268    /// Get the instruction pointer that will be next executed by the emulator
269    pub fn current_ip(&self) -> Option<Instruction> {
270        self.callstack.last().and_then(|activation| activation.peek())
271    }
272
273    /// Get the name of the function that is currently executing
274    pub fn current_function(&self) -> Option<FunctionIdent> {
275        self.callstack.last().map(|activation| activation.function().name)
276    }
277
278    /// Get access to the current state of the operand stack
279    pub fn stack(&mut self) -> &OperandStack<Felt> {
280        &self.stack
281    }
282
283    /// Get mutable access to the current state of the operand stack
284    pub fn stack_mut(&mut self) -> &mut OperandStack<Felt> {
285        &mut self.stack
286    }
287
288    /// Load `program` into this emulator
289    ///
290    /// This resets the emulator state, as only one program may be loaded at a time.
291    pub fn load_program(&mut self, program: Arc<Program>) -> Result<(), EmulationError> {
292        // Ensure the emulator state is reset
293        if !matches!(self.status, Status::Init) {
294            self.reset();
295        }
296
297        let modules = program.unwrap_frozen_modules();
298        let mut cursor = modules.front();
299        while let Some(module) = cursor.clone_pointer() {
300            self.load_module(module)?;
301            cursor.move_next();
302        }
303        self.entrypoint = Some(program.entrypoint());
304
305        // TODO: Load data segments
306
307        self.status = Status::Loaded;
308
309        Ok(())
310    }
311
312    /// Load `module` into this emulator
313    ///
314    /// An error is returned if a module with the same name is already loaded.
315    pub fn load_module(&mut self, module: Arc<Module>) -> Result<(), EmulationError> {
316        use std::collections::hash_map::Entry;
317
318        assert_matches!(
319            self.status,
320            Status::Init | Status::Loaded,
321            "cannot load modules once execution has started without calling stop() or reset() \
322             first"
323        );
324
325        match self.modules_loaded.entry(module.id) {
326            Entry::Occupied(_) => return Err(EmulationError::AlreadyLoaded(module.id)),
327            Entry::Vacant(entry) => {
328                entry.insert(module.clone());
329            }
330        }
331
332        // Register module dependencies
333        for import in module.imports.iter() {
334            let name = Ident::with_empty_span(import.name);
335            if self.modules_loaded.contains_key(&name) {
336                continue;
337            }
338            self.modules_pending.insert(name);
339        }
340        self.modules_pending.remove(&module.id);
341
342        // Load functions from this module
343        let functions = module.unwrap_frozen_functions();
344        let mut cursor = functions.front();
345        while let Some(function) = cursor.clone_pointer() {
346            self.load_function(function)?;
347            cursor.move_next();
348        }
349
350        self.status = Status::Loaded;
351
352        Ok(())
353    }
354
355    /// Reloads a loaded module, `name`.
356    ///
357    /// This function will panic if the named module is not currently loaded.
358    pub fn reload_module(&mut self, module: Arc<Module>) -> Result<(), EmulationError> {
359        self.unload_module(module.id);
360        self.load_module(module)
361    }
362
363    /// Unloads a loaded module, `name`.
364    ///
365    /// This function will panic if the named module is not currently loaded.
366    pub fn unload_module(&mut self, name: Ident) {
367        assert_matches!(
368            self.status,
369            Status::Loaded,
370            "cannot unload modules once execution has started without calling stop() or reset() \
371             first"
372        );
373
374        let prev = self
375            .modules_loaded
376            .remove(&name)
377            .expect("cannot reload a module that was not previously loaded");
378
379        // Unload all functions associated with the previous load
380        for f in prev.functions() {
381            self.functions.remove(&f.name);
382            self.locals.remove(&f.name);
383        }
384
385        // Determine if we need to add `name` to `modules_pending` if there are dependents still
386        // loaded
387        for module in self.modules_loaded.values() {
388            if module.imports.is_import(&name) {
389                self.modules_pending.insert(name);
390                break;
391            }
392        }
393    }
394
395    /// Load `function` into this emulator
396    fn load_function(&mut self, function: Arc<Function>) -> Result<(), EmulationError> {
397        let id = function.name;
398        if self.functions.contains_key(&id) {
399            return Err(EmulationError::DuplicateFunction(id));
400        }
401        let fp = self.lp;
402        self.lp += function.locals().len() as u32;
403        self.functions.insert(id, Stub::Asm(function));
404        self.locals.insert(id, fp);
405
406        Ok(())
407    }
408
409    /// Load `function` into this emulator, with the given identifier
410    ///
411    /// Because we don't know the set of [FuncId] that have already been allocated,
412    /// we leave the choice up to the caller. We assert that functions do
413    /// not get defined twice to catch conflicts, just in case.
414    pub fn load_nif(
415        &mut self,
416        id: FunctionIdent,
417        function: Box<NativeFn>,
418    ) -> Result<(), EmulationError> {
419        assert_matches!(
420            self.status,
421            Status::Init | Status::Loaded,
422            "cannot load nifs once execution has started without calling stop() or reset() first"
423        );
424
425        if self.functions.contains_key(&id) {
426            return Err(EmulationError::DuplicateFunction(id));
427        }
428        self.functions.insert(id, Stub::Native(Rc::new(RefCell::new(function))));
429
430        Ok(())
431    }
432
433    /// Allocate space for `value` on the emulator heap, and copy it's contents there.
434    ///
435    /// NOTE: The smallest unit of addressable memory is 4 bytes (32 bits). If you provide
436    /// a value that is smaller than this, or is not a multiple of 4, the data will be padded
437    /// with zeroes to ensure that it is.
438    pub fn write_bytes_to_memory(&mut self, value: &[u8]) -> u32 {
439        let addr = self.hp;
440        if value.is_empty() {
441            return addr;
442        }
443
444        let mut elem_idx = 0;
445        for chunk in value.chunks(4) {
446            let elem = match chunk.len() {
447                4 => u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]),
448                3 => u32::from_le_bytes([chunk[0], chunk[1], chunk[2], 0]),
449                2 => u32::from_le_bytes([chunk[0], chunk[1], 0, 0]),
450                1 => u32::from_le_bytes([chunk[0], 0, 0, 0]),
451                0 => 0,
452                _ => unreachable!(),
453            };
454            if elem_idx == 4 {
455                elem_idx = 0;
456                assert!(self.hp + 1 < self.lp, "heap has overflowed into reserved region");
457                self.hp += 1;
458            }
459            self.memory[self.hp as usize][elem_idx] = Felt::new(elem as u64);
460            elem_idx += 1;
461        }
462
463        addr
464    }
465
466    /// Allocate enough words to hold `size` bytes of memory
467    ///
468    /// Returns the pointer as a byte-addressable address
469    pub fn malloc(&mut self, size: usize) -> u32 {
470        let addr = self.hp;
471
472        if size == 0 {
473            return addr;
474        }
475
476        let size = size as u32;
477        let extra = size % 16;
478        let words = (size / 16) + (extra > 0) as u32;
479        assert!(self.hp + words < self.lp, "heap has overflowed into reserved region");
480        self.hp += words;
481
482        addr * 16
483    }
484
485    /// Write `value` to the word at `addr`, and element `index`
486    pub fn store(&mut self, addr: usize, value: Felt) {
487        use crate::NativePtr;
488
489        let ptr = NativePtr::from_ptr(addr.try_into().expect("invalid address"));
490        let addr = ptr.waddr as usize;
491        assert_eq!(ptr.offset, 0, "invalid store: unaligned address {addr:#?}");
492        assert!(addr < self.memory.len(), "invalid address");
493
494        self.memory[addr][ptr.index as usize] = value;
495    }
496
497    /// Start executing the current program by `invoke`ing the top-level initialization block (the
498    /// entrypoint).
499    ///
500    /// This function will run the program to completion, and return the state of the operand stack
501    /// on exit.
502    ///
503    /// NOTE: If no entrypoint has been loaded, an error is returned.
504    ///
505    /// The emulator is automatically reset when it exits successfully.
506    pub fn start(&mut self) -> Result<OperandStack<Felt>, EmulationError> {
507        match self.status {
508            Status::Init => return Err(EmulationError::NoEntrypoint),
509            Status::Loaded => (),
510            Status::Stopped => {
511                self.stop();
512            }
513            Status::Started | Status::Suspended => panic!(
514                "cannot start the emulator when it is already started without calling stop() or \
515                 reset() first"
516            ),
517            Status::Faulted(ref err) => return Err(err.clone()),
518        }
519
520        let main_fn = self.entrypoint.unwrap_or_else(|| FunctionIdent {
521            module: LibraryNamespace::EXEC_PATH.into(),
522            function: ProcedureName::MAIN_PROC_NAME.into(),
523        });
524
525        // Run to completion
526        let stack = self.invoke(main_fn, &[]).map_err(|err| match err {
527            EmulationError::UndefinedFunction(f) if f == main_fn => EmulationError::NoEntrypoint,
528            err => err,
529        })?;
530
531        // Reset the emulator on exit
532        self.stop();
533
534        // Return the output contained on the operand stack
535        Ok(stack)
536    }
537
538    /// Start emulation by `enter`ing the top-level initialization block (the entrypoint).
539    ///
540    /// This should be called instead of `start` when stepping through a program rather than
541    /// executing it to completion in one call.
542    ///
543    /// NOTE: If no entrypoint has been loaded, an error is returned.
544    ///
545    /// It is up to the caller to reset the emulator when the program exits, unlike `start`.
546    pub fn init(&mut self) -> Result<EmulatorEvent, EmulationError> {
547        match self.status {
548            Status::Init => return Err(EmulationError::NoEntrypoint),
549            Status::Loaded => (),
550            Status::Stopped => {
551                self.stop();
552            }
553            Status::Started | Status::Suspended => panic!(
554                "cannot start the emulator when it is already started without calling stop() or \
555                 reset() first"
556            ),
557            Status::Faulted(ref err) => return Err(err.clone()),
558        }
559
560        let main_fn = FunctionIdent {
561            module: LibraryNamespace::EXEC_PATH.into(),
562            function: ProcedureName::MAIN_PROC_NAME.into(),
563        };
564
565        // Step into the entrypoint
566        self.enter(main_fn, &[]).map_err(|err| match err {
567            EmulationError::UndefinedFunction(f) if f == main_fn => EmulationError::NoEntrypoint,
568            err => err,
569        })
570    }
571
572    /// Stop running the currently executing function, and reset the cycle counter, operand stack,
573    /// and linear memory.
574    ///
575    /// This function preserves loaded code, breakpoints, and other configuration items.
576    ///
577    /// If an attempt is made to run the emulator in the stopped state, a panic will occur
578    pub fn stop(&mut self) {
579        self.callstack.clear();
580        self.stack.clear();
581        self.memory.reset();
582        self.hp = self.hp_start;
583        self.lp = self.lp_start;
584        self.step_over = None;
585        self.clk = 0;
586        self.status = Status::Loaded;
587    }
588
589    /// Reset the emulator state to its initial state at creation.
590    ///
591    /// In addition to resetting the cycle counter, operand stack, and linear memory,
592    /// this function also unloads all code, and clears all breakpoints. Only the
593    /// configuration used to initialize the emulator is preserved.
594    ///
595    /// To use the emulator after calling this function, you must load a program or module again.
596    pub fn reset(&mut self) {
597        self.stop();
598        self.functions.clear();
599        self.locals.clear();
600        self.modules_loaded.clear();
601        self.modules_pending.clear();
602        self.breakpoints.clear();
603        self.status = Status::Init;
604    }
605
606    /// Run the emulator by invoking `callee` with `args` placed on the
607    /// operand stack in FIFO order.
608    ///
609    /// If a fatal error occurs during emulation, `Err` is returned,
610    /// e.g. if `callee` has not been loaded.
611    ///
612    /// When `callee` returns, it's result will be returned wrapped in `Ok`.
613    /// For functions with no return value, this will be `Ok(None)`, or all
614    /// others it will be `Ok(Some(value))`.
615    pub fn invoke(
616        &mut self,
617        callee: FunctionIdent,
618        args: &[Felt],
619    ) -> Result<OperandStack<Felt>, EmulationError> {
620        assert_matches!(
621            self.status,
622            Status::Loaded,
623            "cannot start executing a function when the emulator is already started without \
624             calling stop() or reset() first"
625        );
626        let fun = self
627            .functions
628            .get(&callee)
629            .cloned()
630            .ok_or(EmulationError::UndefinedFunction(callee))?;
631        self.status = Status::Started;
632        match fun {
633            Stub::Asm(ref function) => match self.invoke_function(function.clone(), args) {
634                done @ Ok(_) => {
635                    self.status = Status::Stopped;
636                    done
637                }
638                Err(err @ EmulationError::BreakpointHit(_)) => {
639                    self.status = Status::Suspended;
640                    Err(err)
641                }
642                Err(err) => {
643                    self.status = Status::Faulted(err.clone());
644                    Err(err)
645                }
646            },
647            Stub::Native(function) => {
648                let mut function = function.borrow_mut();
649                function(self, args)?;
650                Ok(self.stack.clone())
651            }
652        }
653    }
654
655    /// Invoke a function defined in MASM IR, placing the given arguments on the
656    /// operand stack in FIFO order, and suspending immediately if any breakpoints
657    /// would have been triggered by the invocation.
658    #[inline]
659    fn invoke_function(
660        &mut self,
661        function: Arc<Function>,
662        args: &[Felt],
663    ) -> Result<OperandStack<Felt>, EmulationError> {
664        // Place the arguments on the operand stack
665        //assert_eq!(args.len(), function.arity());
666        for arg in args.iter().copied().rev() {
667            self.stack.push(arg);
668        }
669
670        // Schedule `function`
671        let name = function.name;
672        let fp = self.locals[&name];
673        let state = Activation::new(function, fp);
674        self.callstack.push(state);
675
676        match self
677            .breakpoints
678            .handle_event(EmulatorEvent::EnterFunction(name), self.current_ip())
679        {
680            Some(bp) => Err(EmulationError::BreakpointHit(bp)),
681            None => {
682                self.run()?;
683
684                Ok(self.stack.clone())
685            }
686        }
687    }
688
689    /// Run the emulator by invoking `callee` with `args` placed on the
690    /// operand stack in FIFO order.
691    ///
692    /// If a fatal error occurs during emulation, `Err` is returned,
693    /// e.g. if `callee` has not been loaded.
694    ///
695    /// When `callee` returns, it's result will be returned wrapped in `Ok`.
696    /// For functions with no return value, this will be `Ok(None)`, or all
697    /// others it will be `Ok(Some(value))`.
698    pub fn enter(
699        &mut self,
700        callee: FunctionIdent,
701        args: &[Felt],
702    ) -> Result<EmulatorEvent, EmulationError> {
703        assert_matches!(
704            self.status,
705            Status::Loaded,
706            "cannot start executing a function when the emulator is already started without \
707             calling stop() or reset() first"
708        );
709
710        let fun = self
711            .functions
712            .get(&callee)
713            .cloned()
714            .ok_or(EmulationError::UndefinedFunction(callee))?;
715        self.status = Status::Started;
716        match fun {
717            Stub::Asm(ref function) => self.enter_function(function.clone(), args),
718            Stub::Native(function) => {
719                let mut function = function.borrow_mut();
720                function(self, args)?;
721                Ok(EmulatorEvent::ExitFunction(callee))
722            }
723        }
724    }
725
726    /// Stage a MASM IR function for execution by the emulator, placing the given arguments on the
727    /// operand stack in FIFO order, then immediately suspending execution until the next
728    /// resumption.
729    #[inline]
730    fn enter_function(
731        &mut self,
732        function: Arc<Function>,
733        args: &[Felt],
734    ) -> Result<EmulatorEvent, EmulationError> {
735        // Place the arguments on the operand stack
736        //assert_eq!(args.len(), function.arity());
737        for arg in args.iter().copied().rev() {
738            self.stack.push(arg);
739        }
740
741        // Schedule `function`
742        let name = function.name;
743        let fp = self.locals[&name];
744        let state = Activation::new(function, fp);
745        self.callstack.push(state);
746
747        self.status = Status::Suspended;
748
749        Ok(EmulatorEvent::Suspended)
750    }
751
752    /// Resume execution when the emulator suspended due to a breakpoint
753    #[inline]
754    pub fn resume(&mut self) -> Result<EmulatorEvent, EmulationError> {
755        assert_matches!(
756            self.status,
757            Status::Suspended,
758            "cannot resume the emulator from any state other than suspended"
759        );
760        self.run()
761    }
762}
763
764/// Pops the top element off the advice stack
765macro_rules! adv_pop {
766    ($emu:ident) => {
767        $emu.advice_stack.pop().expect("advice stack is empty")
768    };
769
770    ($emu:ident, $msg:literal) => {
771        $emu.advice_stack.pop().expect($msg)
772    };
773
774    ($emu:ident, $msg:literal, $($arg:expr),+) => {
775        match $emu.advice_stack.pop() {
776            Some(value) => value,
777            None => panic!($msg, $($arg),*),
778        }
779    }
780}
781
782/// Pops the top word off the advice stack
783macro_rules! adv_popw {
784    ($emu:ident) => {
785        $emu.advice_stack.popw().expect("advice stack does not contain a full word")
786    };
787
788    ($emu:ident, $msg:literal) => {
789        $emu.advice_stack.popw().expect($msg)
790    };
791
792    ($emu:ident, $msg:literal, $($arg:expr),+) => {{
793        match $emu.advice_stack.popw() {
794            Some(value) => value,
795            None => panic!($msg, $($arg),*),
796        }
797    }}
798}
799
800/// Pops the top element off the stack
801macro_rules! pop {
802    ($emu:ident) => {
803        $emu.stack.pop().expect("operand stack is empty")
804    };
805
806    ($emu:ident, $msg:literal) => {
807        $emu.stack.pop().expect($msg)
808    };
809
810    ($emu:ident, $msg:literal, $($arg:expr),+) => {
811        match $emu.stack.pop() {
812            Some(value) => value,
813            None => panic!($msg, $($arg),*),
814        }
815    }
816}
817
818/// Peeks the top element of the stack
819macro_rules! peek {
820    ($emu:ident) => {
821        $emu.stack.peek().expect("operand stack is empty")
822    };
823
824    ($emu:ident, $msg:literal) => {
825        $emu.stack.peek().expect($msg)
826    };
827
828    ($emu:ident, $msg:literal, $($arg:expr),+) => {
829        match $emu.stack.peek() {
830            Some(value) => value,
831            None => panic!($msg, $($arg),*),
832        }
833    }
834}
835
836/// Pops the top word off the stack
837macro_rules! popw {
838    ($emu:ident) => {
839        $emu.stack.popw().expect("operand stack does not contain a full word")
840    };
841
842    ($emu:ident, $msg:literal) => {
843        $emu.stack.popw().expect($msg)
844    };
845
846    ($emu:ident, $msg:literal, $($arg:expr),+) => {{
847        match $emu.stack.popw() {
848            Some(value) => value,
849            None => panic!($msg, $($arg),*),
850        }
851    }}
852}
853
854/// Pops the top two elements off the stack, returning them in order of appearance
855macro_rules! pop2 {
856    ($emu:ident) => {{
857        let b = pop!($emu);
858        let a = pop!($emu);
859        (b, a)
860    }};
861}
862
863/// Pops a u32 value from the top of the stack, and asserts if it is out of range
864macro_rules! pop_u32 {
865    ($emu:ident) => {{
866        let value = pop!($emu).as_int();
867        assert!(value < 2u64.pow(32), "assertion failed: {value} is not a valid u32, value is out of range");
868        value as u32
869    }};
870
871    ($emu:ident, $format:literal $(, $args:expr)*) => {{
872        let value = pop!($emu).as_int();
873        assert!(value < 2u64.pow(32), $format, value, $($args),*);
874        value as u32
875    }}
876}
877
878/// Peeks a u32 value from the top of the stack, and asserts if it is out of range
879#[allow(unused)]
880macro_rules! peek_u32 {
881    ($emu:ident) => {{
882        let value = peek!($emu).as_int();
883        assert!(value < 2u64.pow(32), "assertion failed: {value} is not a valid u32, value is out of range");
884        value as u32
885    }};
886
887    ($emu:ident, $format:literal $(, $args:expr)*) => {{
888        let value = peek!($emu).as_int();
889        assert!(value < 2u64.pow(32), $format, value, $($args),*);
890        value as u32
891    }}
892}
893
894/// Pops a pointer value from the top of the stack, and asserts if it is not a valid boolean
895macro_rules! pop_addr {
896    ($emu:ident) => {{
897        let addr = pop_u32!($emu, "expected valid 32-bit address, got {}") as usize;
898        assert!(
899            addr < $emu.memory.len(),
900            "out of bounds memory access, addr: {}, available memory: {}",
901            addr,
902            $emu.memory.len()
903        );
904        addr
905    }};
906}
907
908/// Pops a boolean value from the top of the stack, and asserts if it is not a valid boolean
909macro_rules! pop_bool {
910    ($emu:ident) => {{
911        let value = pop!($emu).as_int();
912        assert!(
913            value < 2,
914            "assertion failed: {value} is not a valid boolean, value must be either 1 or 0"
915        );
916        value == 1
917    }};
918}
919
920/// Applies a binary operator that produces a result of the same input type:
921///
922/// 1. The top two elements of the stack
923/// 2. The top element of the stack and an immediate.
924macro_rules! binop {
925    ($emu:ident, $op:ident) => {{
926        use core::ops::*;
927        let b = pop!($emu);
928        let a = pop!($emu);
929        $emu.stack.push(a.$op(b));
930    }};
931
932    ($emu:ident, $op:ident, $imm:expr) => {{
933        use core::ops::*;
934        let a = pop!($emu);
935        $emu.stack.push(a.$op($imm));
936    }};
937}
938
939/// Applies a binary operator to two u32 values, either:
940///
941/// 1. The top two elements of the stack
942/// 2. The top element of the stack and an immediate.
943macro_rules! binop32 {
944    ($emu:ident, $op:ident) => {{
945        #[allow(unused)]
946        use core::ops::*;
947        let b = pop_u32!($emu);
948        let a = pop_u32!($emu);
949        $emu.stack.push_u32(a.$op(b));
950    }};
951
952    ($emu:ident, $op:ident, $imm:expr) => {{
953        #[allow(unused)]
954        use core::ops::*;
955        let a = pop_u32!($emu);
956        $emu.stack.push_u32(a.$op($imm));
957    }};
958}
959
960/// Applies a checked binary operator to two u32 values, either:
961///
962/// 1. The top two elements of the stack
963/// 2. The top element of the stack and an immediate.
964macro_rules! binop_unchecked_u32 {
965    ($emu:ident, $op:ident) => {{
966        #[allow(unused)]
967        use core::ops::*;
968        let b = pop!($emu);
969        let a = pop!($emu);
970        $emu.stack.push(Felt::new(a.as_int().$op(b.as_int())));
971    }};
972
973    ($emu:ident, $op:ident, $imm:expr) => {{
974        #[allow(unused)]
975        use core::ops::*;
976        let a = pop!($emu);
977        $emu.stack.push(Felt::new(a.as_int().$op($imm)));
978    }};
979}
980
981/// Applies an overflowing binary operator to two u32 values, either:
982///
983/// 1. The top two elements of the stack
984/// 2. The top element of the stack and an immediate.
985macro_rules! binop_overflowing_u32 {
986    ($emu:ident, $op:ident) => {{
987        paste::paste! {
988            binop_overflowing_u32_impl!($emu, [<overflowing_ $op>]);
989        }
990    }};
991
992    ($emu:ident, $op:ident, $imm:expr) => {{
993        paste::paste! {
994            binop_overflowing_u32_impl!($emu, [<overflowing_ $op>], $imm);
995        }
996    }};
997}
998
999#[doc(hidden)]
1000macro_rules! binop_overflowing_u32_impl {
1001    ($emu:ident, $op:ident) => {{
1002        #[allow(unused)]
1003        use core::ops::*;
1004        let b = pop_u32!($emu);
1005        let a = pop_u32!($emu);
1006        let (result, overflowed) = a.$op(b);
1007        $emu.stack.push_u32(result);
1008        $emu.stack.push_u8(overflowed as u8);
1009    }};
1010
1011    ($emu:ident, $op:ident, $imm:expr) => {{
1012        #[allow(unused)]
1013        use core::ops::*;
1014        let a = pop_u32!($emu);
1015        let (result, overflowed) = a.$op($imm);
1016        $emu.stack.push_u32(result);
1017        $emu.stack.push_u8(overflowed as u8);
1018    }};
1019}
1020
1021/// Applies a wrapping binary operator to two u32 values, either:
1022///
1023/// 1. The top two elements of the stack
1024/// 2. The top element of the stack and an immediate.
1025macro_rules! binop_wrapping_u32 {
1026    ($emu:ident, $op:ident) => {{
1027        paste::paste! {
1028            binop_wrapping_u32_impl!($emu, [<wrapping_ $op>]);
1029        }
1030    }};
1031
1032    ($emu:ident, $op:ident, $imm:expr) => {{
1033        paste::paste! {
1034            binop_wrapping_u32_impl!($emu, [<wrapping_ $op>], $imm);
1035        }
1036    }};
1037}
1038
1039#[doc(hidden)]
1040macro_rules! binop_wrapping_u32_impl {
1041    ($emu:ident, $op:ident) => {{
1042        #[allow(unused)]
1043        use core::ops::*;
1044        let b = pop_u32!($emu);
1045        let a = pop_u32!($emu);
1046        $emu.stack.push_u32(a.$op(b));
1047    }};
1048
1049    ($emu:ident, $op:ident, $imm:expr) => {{
1050        #[allow(unused)]
1051        use core::ops::*;
1052        let a = pop_u32!($emu);
1053        $emu.stack.push_u32(a.$op($imm));
1054    }};
1055}
1056
1057/// Applies a binary comparison operator, to either:
1058///
1059/// 1. The top two elements of the stack
1060/// 2. The top element of the stack and an immediate.
1061macro_rules! comparison {
1062    ($emu:ident, $op:ident) => {{
1063        let b = pop!($emu).as_int();
1064        let a = pop!($emu).as_int();
1065        let result: bool = a.$op(&b);
1066        $emu.stack.push_u8(result as u8);
1067    }};
1068
1069    ($emu:ident, $op:ident, $imm:expr) => {{
1070        let a = pop!($emu).as_int();
1071        let result: bool = a.$op(&$imm);
1072        $emu.stack.push_u8(result as u8);
1073    }};
1074}
1075
1076impl Emulator {
1077    /// Step the emulator forward one cycle, returning the type of event produced
1078    /// during that cycle, or an error.
1079    pub fn step(&mut self) -> Result<EmulatorEvent, EmulationError> {
1080        match self
1081            .breakpoints
1082            .handle_event(EmulatorEvent::CycleStart(self.clk), self.current_ip())
1083        {
1084            Some(bp) => {
1085                self.status = Status::Suspended;
1086                Ok(EmulatorEvent::Breakpoint(bp))
1087            }
1088            None => match self.run_once() {
1089                Ok(EmulatorEvent::Stopped) => {
1090                    self.status = Status::Stopped;
1091                    Ok(EmulatorEvent::Stopped)
1092                }
1093                suspended @ Ok(_) => {
1094                    self.status = Status::Suspended;
1095                    suspended
1096                }
1097                Err(err) => {
1098                    self.status = Status::Faulted(err.clone());
1099                    Err(err)
1100                }
1101            },
1102        }
1103    }
1104
1105    /// Step the emulator forward one step, but stepping past any nested blocks or function calls,
1106    /// returning the type of event produced during that cycle, or an error.
1107    pub fn step_over(&mut self) -> Result<EmulatorEvent, EmulationError> {
1108        match self.step_over.take() {
1109            None => self.step(),
1110            Some(ip) => {
1111                self.breakpoints.set(Breakpoint::At(ip));
1112                match self.run() {
1113                    Ok(EmulatorEvent::Stopped) => {
1114                        self.status = Status::Stopped;
1115                        Ok(EmulatorEvent::Stopped)
1116                    }
1117                    Ok(EmulatorEvent::Breakpoint(bp)) | Err(EmulationError::BreakpointHit(bp)) => {
1118                        self.status = Status::Suspended;
1119                        if self.current_ip().map(|ix| ix.ip) == Some(ip) {
1120                            return Ok(EmulatorEvent::Suspended);
1121                        }
1122                        Ok(EmulatorEvent::Breakpoint(bp))
1123                    }
1124                    Ok(event) => panic!(
1125                        "unexpected event produced by emulator loop when stepping over: {event:?}"
1126                    ),
1127                    Err(err) => {
1128                        self.status = Status::Faulted(err.clone());
1129                        Err(err)
1130                    }
1131                }
1132            }
1133        }
1134    }
1135
1136    /// Step the emulator forward until control returns from the current function.
1137    pub fn step_out(&mut self) -> Result<EmulatorEvent, EmulationError> {
1138        let current_function = self.current_function();
1139        self.breakpoints.break_on_return(true);
1140        match self.run() {
1141            Ok(EmulatorEvent::Stopped) => {
1142                self.status = Status::Stopped;
1143                Ok(EmulatorEvent::Stopped)
1144            }
1145            Ok(EmulatorEvent::Breakpoint(bp)) | Err(EmulationError::BreakpointHit(bp)) => {
1146                self.status = Status::Suspended;
1147                if self.current_function() == current_function {
1148                    return Ok(EmulatorEvent::Suspended);
1149                }
1150                Ok(EmulatorEvent::Breakpoint(bp))
1151            }
1152            Ok(event) => {
1153                panic!("unexpected event produced by emulator loop when stepping over: {event:?}")
1154            }
1155            Err(err) => {
1156                self.status = Status::Faulted(err.clone());
1157                Err(err)
1158            }
1159        }
1160    }
1161
1162    /// Run the emulator until all calls are completed, the cycle budget is exhausted,
1163    /// or a breakpoint is hit.
1164    ///
1165    /// It is expected that the caller has set up the operand stack with the correct
1166    /// number of arguments. If not, undefined behavior (from the perspective of the
1167    /// MASM program) will result.
1168    #[inline(never)]
1169    fn run(&mut self) -> Result<EmulatorEvent, EmulationError> {
1170        // This is the core interpreter loop for MASM IR, it runs until one of the
1171        // following occurs:
1172        //
1173        // * We run out of code to execute, i.e. the function is returning normally
1174        // * Execution was explicitly aborted from within the function
1175        // * Execution traps due to a MASM invariant being violated, indicating the
1176        // code is malformed.
1177        // * Execution traps due to a runtime system error, e.g. out of memory
1178        // * Execution traps due to exceeding the predefined execution budget
1179        // * Execution breaks due to a breakpoint
1180        let mut event = self.step()?;
1181        loop {
1182            match event {
1183                // We should suspend when encountering these events
1184                event @ EmulatorEvent::Breakpoint(_) => break Ok(event),
1185                event @ EmulatorEvent::Stopped => break Ok(event),
1186                ev => {
1187                    // We must handle catching certain breakpoints when using this event loop
1188                    match self.breakpoints.handle_event(ev, self.current_ip()) {
1189                        Some(bp) => break Ok(EmulatorEvent::Breakpoint(bp)),
1190                        None => match ev {
1191                            // There was no code remaining in the current function, effectively
1192                            // returning from it. Since no instructions were dispatched, we don't
1193                            // count the cycle, and resume immediately at the continuation point
1194                            // in the caller
1195                            EmulatorEvent::ExitFunction(_) => {
1196                                if self.callstack.is_empty() {
1197                                    break Ok(EmulatorEvent::Stopped);
1198                                }
1199                                event = self.run_once()?;
1200                                continue;
1201                            }
1202                            _ => {
1203                                event = self.step()?;
1204                            }
1205                        },
1206                    }
1207                }
1208            }
1209        }
1210    }
1211
1212    #[inline(never)]
1213    fn run_once(&mut self) -> Result<EmulatorEvent, EmulationError> {
1214        const U32_P: u64 = 2u64.pow(32);
1215
1216        // If there are no more activation records, we're done
1217        if self.callstack.is_empty() {
1218            return Ok(EmulatorEvent::Stopped);
1219        }
1220
1221        // Terminate execution early if we reach a predetermined number of cycles
1222        self.clk += 1;
1223        if self.clk > self.clk_limit {
1224            return Err(EmulationError::CycleBudgetExceeded);
1225        }
1226
1227        let mut state = self.callstack.pop().unwrap();
1228        let current_function = state.function().name;
1229
1230        // Reset the next instruction to break at when stepping over instructions
1231        self.step_over = None;
1232
1233        // If we have breakpoints set that require it, we may need to
1234        // break execution before executing the instruction that is pending
1235        if self.breakpoints.break_on_return || self.breakpoints.has_break_on_reached() {
1236            match state.peek() {
1237                Some(Instruction { ip, .. })
1238                    if self.breakpoints.should_break_at(ip.block, ip.index) =>
1239                {
1240                    self.callstack.push(state);
1241                    return Ok(EmulatorEvent::Breakpoint(BreakpointEvent::Reached(ip)));
1242                }
1243                None if self.breakpoints.break_on_return => {
1244                    self.callstack.push(state);
1245                    self.breakpoints.break_on_return(false);
1246                    return Ok(EmulatorEvent::Breakpoint(BreakpointEvent::StepOut));
1247                }
1248                _ => (),
1249            }
1250        }
1251
1252        // Advance the instruction pointer, returning the instruction
1253        // that it previously pointed to, along with what, if any,
1254        // control flow effect occurred to reach it
1255        let ix_with_op = state.next();
1256        if let Some(ix_with_op) = ix_with_op {
1257            if self.print_trace {
1258                eprintln!("mem: {:?}", self.memory);
1259                eprintln!("stk: {}", self.stack.debug());
1260                eprintln!("op>: {:?}", ix_with_op.op);
1261            }
1262            match ix_with_op.op {
1263                Op::Padw => {
1264                    self.stack.padw();
1265                }
1266                Op::Push(v) => {
1267                    self.stack.push(v);
1268                }
1269                Op::Push2([a, b]) => {
1270                    self.stack.push(a);
1271                    self.stack.push(b);
1272                }
1273                Op::Pushw(word) => {
1274                    self.stack.pushw(word);
1275                }
1276                Op::PushU8(i) => {
1277                    self.stack.push_u8(i);
1278                }
1279                Op::PushU16(i) => {
1280                    self.stack.push_u16(i);
1281                }
1282                Op::PushU32(i) => {
1283                    self.stack.push_u32(i);
1284                }
1285                Op::Drop => {
1286                    self.stack.drop();
1287                }
1288                Op::Dropw => {
1289                    self.stack.dropw();
1290                }
1291                Op::Dup(pos) => {
1292                    self.stack.dup(pos as usize);
1293                }
1294                Op::Dupw(pos) => {
1295                    self.stack.dupw(pos as usize);
1296                }
1297                Op::Swap(pos) => {
1298                    self.stack.swap(pos as usize);
1299                }
1300                Op::Swapw(pos) => {
1301                    self.stack.swapw(pos as usize);
1302                }
1303                Op::Swapdw => {
1304                    self.stack.swapdw();
1305                }
1306                Op::Movup(pos) => {
1307                    self.stack.movup(pos as usize);
1308                }
1309                Op::Movupw(pos) => {
1310                    self.stack.movupw(pos as usize);
1311                }
1312                Op::Movdn(pos) => {
1313                    self.stack.movdn(pos as usize);
1314                }
1315                Op::Movdnw(pos) => {
1316                    self.stack.movdnw(pos as usize);
1317                }
1318                Op::Cswap => {
1319                    let cond = pop_bool!(self);
1320                    if cond {
1321                        self.stack.swap(1);
1322                    }
1323                }
1324                Op::Cswapw => {
1325                    let cond = pop_bool!(self);
1326                    if cond {
1327                        self.stack.swapw(1);
1328                    }
1329                }
1330                Op::Cdrop => {
1331                    let cond = pop_bool!(self);
1332                    let (b, a) = pop2!(self);
1333                    if cond {
1334                        self.stack.push(b);
1335                    } else {
1336                        self.stack.push(a);
1337                    }
1338                }
1339                Op::Cdropw => {
1340                    let cond = pop_bool!(self);
1341                    let b = popw!(self);
1342                    let a = popw!(self);
1343                    if cond {
1344                        self.stack.pushw(b);
1345                    } else {
1346                        self.stack.pushw(a);
1347                    }
1348                }
1349                Op::AdvPush(n) => {
1350                    assert!(
1351                        n > 0 && n <= 16,
1352                        "invalid adv_push operand: must be a value in the range 1..=16, got {n}"
1353                    );
1354                    for _ in 0..n {
1355                        let value = adv_pop!(self);
1356                        self.stack.push(value);
1357                    }
1358                }
1359                Op::AdvLoadw => {
1360                    let word = adv_popw!(self);
1361                    self.stack.dropw();
1362                    self.stack.pushw(word);
1363                }
1364                Op::AdvPipe => {
1365                    // We're overwriting the first two words, C and B, so drop them
1366                    self.stack.dropw();
1367                    self.stack.dropw();
1368                    // The third word, A, is saved, but unused
1369                    let a = popw!(self);
1370                    // The memory address to write to is the first element of the fourth word
1371                    let addr = pop_addr!(self);
1372                    // We update the original address += 2, and restore A
1373                    self.stack.push_u32(addr as u32 + 2);
1374                    self.stack.pushw(a);
1375                    // We then move words D and E from the advice stack to the operand stack,
1376                    // while also writing those words to memory starting at `addr`
1377                    let d = adv_popw!(self);
1378                    self.stack.pushw(d);
1379                    self.memory[addr] = d;
1380                    let e = adv_popw!(self);
1381                    self.stack.pushw(e);
1382                    self.memory[addr + 1] = e;
1383                    // Lastly, since we performed a memory write here, suspend like we do for other
1384                    // memory-modifying ops
1385                    self.callstack.push(state);
1386                    return Ok(EmulatorEvent::MemoryWrite {
1387                        addr: addr as u32,
1388                        size: 16,
1389                    });
1390                }
1391                Op::AdvInjectPushU64Div => {
1392                    const HI_MASK: u64 = u64::MAX << 32;
1393                    const LO_MASK: u64 = u32::MAX as u64;
1394                    let b_hi = pop_u32!(self) as u64;
1395                    let b_lo = pop_u32!(self) as u64;
1396                    let b = (b_hi << 32) | b_lo;
1397                    assert!(b > 0, "assertion failed: division by zero");
1398                    let a_hi = pop_u32!(self) as u64;
1399                    let a_lo = pop_u32!(self) as u64;
1400                    let a = (a_hi << 32) | a_lo;
1401                    let q = a / b;
1402                    let q_hi = (q & HI_MASK) >> 32;
1403                    let q_lo = q & LO_MASK;
1404                    let r = a % b;
1405                    let r_hi = (r & HI_MASK) >> 32;
1406                    let r_lo = r & LO_MASK;
1407                    self.advice_stack.push_u32(r_hi as u32);
1408                    self.advice_stack.push_u32(r_lo as u32);
1409                    self.advice_stack.push_u32(q_hi as u32);
1410                    self.advice_stack.push_u32(q_lo as u32);
1411                    self.stack.push_u32(a_lo as u32);
1412                    self.stack.push_u32(a_hi as u32);
1413                    self.stack.push_u32(b_lo as u32);
1414                    self.stack.push_u32(b_hi as u32);
1415                }
1416                Op::AdvInjectInsertMem
1417                | Op::AdvInjectInsertHperm
1418                | Op::AdvInjectInsertHdword
1419                | Op::AdvInjectInsertHdwordImm(_)
1420                | Op::AdvInjectPushMTreeNode
1421                | Op::AdvInjectPushMapVal
1422                | Op::AdvInjectPushMapValImm(_)
1423                | Op::AdvInjectPushMapValN
1424                | Op::AdvInjectPushMapValNImm(_) => unimplemented!(),
1425                Op::Assert => {
1426                    let cond = pop_bool!(self);
1427                    assert!(cond, "assertion failed: expected true, got false");
1428                }
1429                Op::Assertz => {
1430                    let cond = pop_bool!(self);
1431                    assert!(!cond, "assertion failed: expected false, got true");
1432                }
1433                Op::AssertEq => {
1434                    let (b, a) = pop2!(self);
1435                    assert_eq!(a, b, "equality assertion failed");
1436                }
1437                Op::AssertEqw => {
1438                    let b = popw!(self);
1439                    let a = popw!(self);
1440                    assert_eq!(a, b, "equality assertion failed");
1441                }
1442                Op::LocAddr(id) => {
1443                    let addr = state.fp() + id.as_usize() as u32;
1444                    debug_assert!(addr < self.memory.len() as u32);
1445                    self.stack.push_u32(addr * 16);
1446                }
1447                Op::LocStore(id) => {
1448                    let addr = (state.fp() + id.as_usize() as u32) as usize;
1449                    debug_assert!(addr < self.memory.len());
1450                    let value = pop!(self);
1451                    self.memory[addr][0] = value;
1452                    return Ok(EmulatorEvent::MemoryWrite {
1453                        addr: addr as u32,
1454                        size: 4,
1455                    });
1456                }
1457                Op::LocStorew(id) => {
1458                    let addr = (state.fp() + id.as_usize() as u32) as usize;
1459                    assert!(addr < self.memory.len() - 4, "out of bounds memory access");
1460                    let word =
1461                        self.stack.peekw().expect("operand stack does not contain a full word");
1462                    self.memory[addr] = word;
1463                    return Ok(EmulatorEvent::MemoryWrite {
1464                        addr: addr as u32,
1465                        size: 16,
1466                    });
1467                }
1468                Op::LocLoad(id) => {
1469                    let addr = (state.fp() + id.as_usize() as u32) as usize;
1470                    debug_assert!(addr < self.memory.len());
1471                    self.stack.push(self.memory[addr][0]);
1472                }
1473                Op::LocLoadw(id) => {
1474                    let addr = (state.fp() + id.as_usize() as u32) as usize;
1475                    debug_assert!(addr < self.memory.len());
1476                    self.stack.dropw();
1477                    self.stack.pushw(self.memory[addr]);
1478                }
1479                Op::MemLoad => {
1480                    let addr = pop_addr!(self);
1481                    self.stack.push(self.memory[addr][0]);
1482                }
1483                Op::MemLoadImm(addr) => {
1484                    let addr = addr as usize;
1485                    assert!(addr < self.memory.len(), "out of bounds memory access");
1486                    self.stack.push(self.memory[addr][0]);
1487                }
1488                Op::MemLoadw => {
1489                    let addr = pop_addr!(self);
1490                    self.stack.dropw();
1491                    let mut word = self.memory[addr];
1492                    word.reverse();
1493                    self.stack.pushw(word);
1494                }
1495                Op::MemLoadwImm(addr) => {
1496                    let addr = addr as usize;
1497                    assert!(addr < self.memory.len() - 4, "out of bounds memory access");
1498                    self.stack.dropw();
1499                    let mut word = self.memory[addr];
1500                    word.reverse();
1501                    self.stack.pushw(word);
1502                }
1503                Op::MemStore => {
1504                    let addr = pop_addr!(self);
1505                    let value = pop!(self);
1506                    self.memory[addr][0] = value;
1507                    self.callstack.push(state);
1508                    return Ok(EmulatorEvent::MemoryWrite {
1509                        addr: addr as u32,
1510                        size: 4,
1511                    });
1512                }
1513                Op::MemStoreImm(addr) => {
1514                    let addr = addr as usize;
1515                    assert!(addr < self.memory.len(), "out of bounds memory access");
1516                    let value = self.stack.pop().expect("operand stack is empty");
1517                    self.memory[addr][0] = value;
1518                    self.callstack.push(state);
1519                    return Ok(EmulatorEvent::MemoryWrite {
1520                        addr: addr as u32,
1521                        size: 4,
1522                    });
1523                }
1524                Op::MemStorew => {
1525                    let addr = pop_addr!(self);
1526                    let mut word =
1527                        self.stack.peekw().expect("operand stack does not contain a full word");
1528                    word.reverse();
1529                    self.memory[addr] = word;
1530                    self.callstack.push(state);
1531                    return Ok(EmulatorEvent::MemoryWrite {
1532                        addr: addr as u32,
1533                        size: 16,
1534                    });
1535                }
1536                Op::MemStorewImm(addr) => {
1537                    let addr = addr as usize;
1538                    assert!(addr < self.memory.len() - 4, "out of bounds memory access");
1539                    let mut word =
1540                        self.stack.peekw().expect("operand stack does not contain a full word");
1541                    word.reverse();
1542                    self.memory[addr] = word;
1543                    self.callstack.push(state);
1544                    return Ok(EmulatorEvent::MemoryWrite {
1545                        addr: addr as u32,
1546                        size: 16,
1547                    });
1548                }
1549                Op::If(then_blk, else_blk) => {
1550                    self.step_over = Some(state.ip());
1551                    let cond = pop_bool!(self);
1552                    let dest = if cond {
1553                        state.enter_block(then_blk);
1554                        then_blk
1555                    } else {
1556                        state.enter_block(else_blk);
1557                        else_blk
1558                    };
1559                    self.callstack.push(state);
1560                    return Ok(EmulatorEvent::Jump(dest));
1561                }
1562                Op::While(body_blk) => {
1563                    self.step_over = Some(state.ip());
1564                    let cond = pop_bool!(self);
1565                    if cond {
1566                        state.enter_while_loop(body_blk);
1567                        self.callstack.push(state);
1568                        return Ok(EmulatorEvent::EnterLoop(body_blk));
1569                    }
1570                }
1571                Op::Repeat(n, body_blk) => {
1572                    self.step_over = Some(state.ip());
1573                    state.repeat_block(body_blk, n);
1574                    self.callstack.push(state);
1575                    return Ok(EmulatorEvent::EnterLoop(body_blk));
1576                }
1577                Op::Exec(callee) => {
1578                    let fun = self
1579                        .functions
1580                        .get(&callee)
1581                        .cloned()
1582                        .ok_or(EmulationError::UndefinedFunction(callee))?;
1583                    self.step_over = Some(state.ip());
1584                    match fun {
1585                        Stub::Asm(ref function) => {
1586                            let fp = self.locals[&function.name];
1587                            let callee_state = Activation::new(function.clone(), fp);
1588                            // Suspend caller and scheduled callee next
1589                            self.callstack.push(state);
1590                            self.callstack.push(callee_state);
1591                            return Ok(EmulatorEvent::EnterFunction(function.name));
1592                        }
1593                        Stub::Native(_function) => unimplemented!(),
1594                    }
1595                }
1596                Op::Call(_callee) | Op::Syscall(_callee) => unimplemented!(),
1597                Op::Add => binop!(self, add),
1598                Op::AddImm(imm) => binop!(self, add, imm),
1599                Op::Sub => binop!(self, sub),
1600                Op::SubImm(imm) => binop!(self, sub, imm),
1601                Op::Mul => binop!(self, mul),
1602                Op::MulImm(imm) => binop!(self, mul, imm),
1603                Op::Div => binop!(self, div),
1604                Op::DivImm(imm) => binop!(self, div, imm),
1605                Op::Neg => {
1606                    let a = self.stack.pop().expect("operand stack is empty");
1607                    self.stack.push(-a);
1608                }
1609                Op::Inv => {
1610                    let a = self.stack.pop().expect("operand stack is empty");
1611                    self.stack.push(a.inv());
1612                }
1613                Op::Incr => binop!(self, add, Felt::ONE),
1614                Op::Ilog2 => {
1615                    let a = peek!(self).as_int();
1616                    assert!(a > 0, "invalid ilog2 argument: expected {a} to be > 0");
1617                    self.advice_stack.push_u32(a.ilog2());
1618                }
1619                Op::Pow2 => {
1620                    let a = pop!(self).as_int();
1621                    assert!(
1622                        a < 64,
1623                        "invalid power of two: expected {a} to be a value less than 64"
1624                    );
1625                    let two = Felt::new(2);
1626                    self.stack.push(two.exp(a));
1627                }
1628                Op::Exp => {
1629                    let (b, a) = pop2!(self);
1630                    let b = b.as_int();
1631                    assert!(
1632                        b < 64,
1633                        "invalid power of two: expected {b} to be a value less than 64"
1634                    );
1635                    self.stack.push(a.exp(b));
1636                }
1637                Op::ExpImm(pow) | Op::ExpBitLength(pow) => {
1638                    let pow = pow as u64;
1639                    let a = pop!(self);
1640                    assert!(
1641                        pow < 64,
1642                        "invalid power of two: expected {pow} to be a value less than 64"
1643                    );
1644                    self.stack.push(a.exp(pow));
1645                }
1646                Op::Not => {
1647                    let a = pop_bool!(self);
1648                    self.stack.push_u8(!a as u8);
1649                }
1650                Op::And => {
1651                    let b = pop_bool!(self);
1652                    let a = pop_bool!(self);
1653                    self.stack.push_u8((b & a) as u8);
1654                }
1655                Op::AndImm(b) => {
1656                    let a = pop_bool!(self);
1657                    self.stack.push_u8((a & b) as u8);
1658                }
1659                Op::Or => {
1660                    let b = pop_bool!(self);
1661                    let a = pop_bool!(self);
1662                    self.stack.push_u8((b | a) as u8);
1663                }
1664                Op::OrImm(b) => {
1665                    let a = pop_bool!(self);
1666                    self.stack.push_u8((a | b) as u8);
1667                }
1668                Op::Xor => {
1669                    let b = pop_bool!(self);
1670                    let a = pop_bool!(self);
1671                    self.stack.push_u8((b ^ a) as u8);
1672                }
1673                Op::XorImm(b) => {
1674                    let a = pop_bool!(self);
1675                    self.stack.push_u8((a ^ b) as u8);
1676                }
1677                Op::Eq => comparison!(self, eq),
1678                Op::EqImm(imm) => comparison!(self, eq, imm.as_int()),
1679                Op::Neq => comparison!(self, ne),
1680                Op::NeqImm(imm) => comparison!(self, ne, imm.as_int()),
1681                Op::Gt => comparison!(self, gt),
1682                Op::GtImm(imm) => comparison!(self, gt, imm.as_int()),
1683                Op::Gte => comparison!(self, ge),
1684                Op::GteImm(imm) => comparison!(self, ge, imm.as_int()),
1685                Op::Lt => comparison!(self, lt),
1686                Op::LtImm(imm) => comparison!(self, lt, imm.as_int()),
1687                Op::Lte => comparison!(self, le),
1688                Op::LteImm(imm) => comparison!(self, le, imm.as_int()),
1689                Op::IsOdd => {
1690                    let a = pop!(self).as_int();
1691                    self.stack.push_u8((a % 2 == 0) as u8);
1692                }
1693                Op::Eqw => {
1694                    let b = popw!(self);
1695                    let a = popw!(self);
1696                    self.stack.push_u8((a == b) as u8);
1697                }
1698                Op::Clk => {
1699                    self.stack.push(Felt::new(self.clk as u64));
1700                }
1701                Op::Sdepth => {
1702                    self.stack.push(Felt::new(self.stack.len() as u64));
1703                }
1704                Op::U32Test => {
1705                    let top = self.stack.peek().expect("operand stack is empty").as_int();
1706                    self.stack.push_u8((top < U32_P) as u8);
1707                }
1708                Op::U32Testw => {
1709                    let word = self.stack.peekw().expect("operand stack is empty");
1710                    let is_true = word.iter().all(|elem| elem.as_int() < U32_P);
1711                    self.stack.push_u8(is_true as u8);
1712                }
1713                Op::U32Assert => {
1714                    let top = self.stack.peek().expect("operand stack is empty").as_int();
1715                    assert!(top < U32_P, "assertion failed: {top} is larger than 2^32");
1716                }
1717                Op::U32Assert2 => {
1718                    let a = self.stack.peek().expect("operand stack is empty").as_int();
1719                    let b = self.stack.peek().expect("operand stack is empty").as_int();
1720                    assert!(a < U32_P, "assertion failed: {a} is larger than 2^32");
1721                    assert!(b < U32_P, "assertion failed: {b} is larger than 2^32");
1722                }
1723                Op::U32Assertw => {
1724                    let word = self.stack.peekw().expect("operand stack is empty");
1725                    for elem in word.into_iter() {
1726                        assert!(
1727                            elem.as_int() < U32_P,
1728                            "assertion failed: {elem} is larger than 2^32"
1729                        );
1730                    }
1731                }
1732                Op::U32Cast => {
1733                    let a = pop!(self).as_int();
1734                    self.stack.push(Felt::new(a % U32_P));
1735                }
1736                Op::U32Split => {
1737                    let a = pop!(self).as_int();
1738                    let hi = a / U32_P;
1739                    let lo = a % U32_P;
1740                    self.stack.push(Felt::new(lo));
1741                    self.stack.push(Felt::new(hi));
1742                }
1743                Op::U32OverflowingAdd => binop_overflowing_u32!(self, add),
1744                Op::U32OverflowingAddImm(imm) => binop_overflowing_u32!(self, add, imm),
1745                Op::U32WrappingAdd => binop_wrapping_u32!(self, add),
1746                Op::U32WrappingAddImm(imm) => binop_wrapping_u32!(self, add, imm),
1747                Op::U32OverflowingAdd3 => todo!(),
1748                Op::U32WrappingAdd3 => todo!(),
1749                Op::U32OverflowingSub => binop_overflowing_u32!(self, sub),
1750                Op::U32OverflowingSubImm(imm) => binop_overflowing_u32!(self, sub, imm),
1751                Op::U32WrappingSub => binop_wrapping_u32!(self, sub),
1752                Op::U32WrappingSubImm(imm) => binop_wrapping_u32!(self, sub, imm),
1753                Op::U32OverflowingMul => binop_overflowing_u32!(self, mul),
1754                Op::U32OverflowingMulImm(imm) => binop_overflowing_u32!(self, mul, imm),
1755                Op::U32WrappingMul => binop_wrapping_u32!(self, mul),
1756                Op::U32WrappingMulImm(imm) => binop_wrapping_u32!(self, mul, imm),
1757                Op::U32OverflowingMadd => {
1758                    let b = pop_u32!(self) as u64;
1759                    let a = pop_u32!(self) as u64;
1760                    let c = pop_u32!(self) as u64;
1761                    let result = a * b + c;
1762                    let d = result % 2u64.pow(32);
1763                    let e = result / 2u64.pow(32);
1764                    self.stack.push(Felt::new(d));
1765                    self.stack.push(Felt::new(e));
1766                }
1767                Op::U32WrappingMadd => {
1768                    let b = pop_u32!(self) as u64;
1769                    let a = pop_u32!(self) as u64;
1770                    let c = pop_u32!(self) as u64;
1771                    let d = (a * b + c) % 2u64.pow(32);
1772                    self.stack.push(Felt::new(d));
1773                }
1774                Op::U32Div => binop_unchecked_u32!(self, div),
1775                Op::U32DivImm(imm) => binop_unchecked_u32!(self, div, imm as u64),
1776                Op::U32Mod => {
1777                    let b = pop!(self).as_int();
1778                    let a = pop!(self).as_int();
1779                    self.stack.push(Felt::new(a % b));
1780                }
1781                Op::U32ModImm(imm) => {
1782                    let a = pop!(self).as_int();
1783                    self.stack.push(Felt::new(a % imm as u64));
1784                }
1785                Op::U32DivMod => {
1786                    let b = pop!(self).as_int();
1787                    let a = pop!(self).as_int();
1788                    self.stack.push(Felt::new(a / b));
1789                    self.stack.push(Felt::new(a % b));
1790                }
1791                Op::U32DivModImm(b) => {
1792                    let b = b as u64;
1793                    let a = pop!(self).as_int();
1794                    self.stack.push(Felt::new(a / b));
1795                    self.stack.push(Felt::new(a % b));
1796                }
1797                Op::U32And => binop32!(self, bitand),
1798                Op::U32Or => binop32!(self, bitor),
1799                Op::U32Xor => binop32!(self, bitxor),
1800                Op::U32Not => {
1801                    let a = pop_u32!(self);
1802                    self.stack.push_u32(!a);
1803                }
1804                Op::U32Shl => binop_wrapping_u32!(self, shl),
1805                Op::U32ShlImm(imm) => binop_wrapping_u32!(self, shl, imm),
1806                Op::U32Shr => binop_wrapping_u32!(self, shr),
1807                Op::U32ShrImm(imm) => binop_wrapping_u32!(self, shr, imm),
1808                Op::U32Rotl => {
1809                    let b = pop_u32!(self);
1810                    let a = pop_u32!(self);
1811                    self.stack.push_u32(a.rotate_left(b));
1812                }
1813                Op::U32RotlImm(imm) => {
1814                    let a = pop_u32!(self);
1815                    self.stack.push_u32(a.rotate_left(imm));
1816                }
1817                Op::U32Rotr => {
1818                    let b = pop_u32!(self);
1819                    let a = pop_u32!(self);
1820                    self.stack.push_u32(a.rotate_right(b));
1821                }
1822                Op::U32RotrImm(imm) => {
1823                    let a = pop_u32!(self);
1824                    self.stack.push_u32(a.rotate_right(imm));
1825                }
1826                Op::U32Popcnt => {
1827                    let a = pop_u32!(self);
1828                    self.stack.push_u32(a.count_ones());
1829                }
1830                Op::U32Clz => {
1831                    let a = pop_u32!(self);
1832                    self.stack.push_u32(a.leading_zeros());
1833                }
1834                Op::U32Clo => {
1835                    let a = pop_u32!(self);
1836                    self.stack.push_u32(a.leading_ones());
1837                }
1838                Op::U32Ctz => {
1839                    let a = pop_u32!(self);
1840                    self.stack.push_u32(a.trailing_zeros());
1841                }
1842                Op::U32Cto => {
1843                    let a = pop_u32!(self);
1844                    self.stack.push_u32(a.trailing_ones());
1845                }
1846                Op::U32Gt => comparison!(self, gt),
1847                Op::U32Gte => comparison!(self, ge),
1848                Op::U32Lt => comparison!(self, lt),
1849                Op::U32Lte => comparison!(self, le),
1850                Op::U32Min => {
1851                    let b = pop!(self).as_int();
1852                    let a = pop!(self).as_int();
1853                    self.stack.push(Felt::new(cmp::min(a, b)));
1854                }
1855                Op::U32Max => {
1856                    let b = pop!(self).as_int();
1857                    let a = pop!(self).as_int();
1858                    self.stack.push(Felt::new(cmp::max(a, b)));
1859                }
1860                Op::Breakpoint => {
1861                    self.callstack.push(state);
1862                    return Ok(EmulatorEvent::Breakpoint(BreakpointEvent::Step));
1863                }
1864                Op::DebugStack => {
1865                    dbg!(self.stack.debug());
1866                }
1867                Op::DebugStackN(n) => {
1868                    dbg!(self.stack.debug().take(n as usize));
1869                }
1870                Op::DebugMemory
1871                | Op::DebugMemoryAt(_)
1872                | Op::DebugMemoryRange(..)
1873                | Op::DebugFrame
1874                | Op::DebugFrameAt(_)
1875                | Op::DebugFrameRange(..) => (),
1876                Op::Emit(_) | Op::Trace(_) => (),
1877                op => unimplemented!("missing opcode implementation for {op:?}"),
1878            }
1879
1880            match ix_with_op.effect {
1881                ControlEffect::Repeat(_) | ControlEffect::Loopback => {
1882                    self.callstack.push(state);
1883                    return Ok(EmulatorEvent::EnterLoop(ix_with_op.ip.block));
1884                }
1885                ControlEffect::Enter => {
1886                    self.callstack.push(state);
1887                    return Ok(EmulatorEvent::Jump(ix_with_op.ip.block));
1888                }
1889                ControlEffect::Exit => {
1890                    self.callstack.push(state);
1891                    return Ok(EmulatorEvent::Jump(ix_with_op.ip.block));
1892                }
1893                ControlEffect::None => (),
1894            }
1895
1896            // Suspend the current activation record
1897            self.callstack.push(state);
1898
1899            Ok(EmulatorEvent::Suspended)
1900        } else {
1901            // No more code left in the current function
1902            Ok(EmulatorEvent::ExitFunction(current_function))
1903        }
1904    }
1905}