From 9a29498249021a001ff9a6e1f9f22ec69afdd944 Mon Sep 17 00:00:00 2001 From: Victor Lopez Date: Mon, 6 Mar 2023 20:25:45 +0100 Subject: [PATCH] feat: add breakpoint instruction This commit introduces `breakpoint`, a transparent instruction that will cause a debug execution to break when reached. This instruction will not be serialized into libraries, and will not have an opcode or be part of the code block tree. For debug executions, it will produce a `NOOP`, so the internal clock of the VM will be affected. The decision of not including this as part of the code block is to avoid further consuming variants of opcodes as they are already scarce. related issue: #580 --- assembly/src/assembler/context.rs | 12 +++- assembly/src/assembler/instruction/mod.rs | 11 +++- assembly/src/assembler/span_builder.rs | 11 ++-- assembly/src/lib.rs | 2 +- assembly/src/parsers/context.rs | 3 + assembly/src/parsers/mod.rs | 3 +- assembly/src/parsers/nodes.rs | 6 ++ assembly/src/parsers/serde/serialization.rs | 5 ++ miden/src/cli/debug/command.rs | 10 ++-- miden/src/cli/debug/executor.rs | 18 +++++- miden/tests/integration/exec_iters.rs | 24 ++++---- .../operations/decorators/asmop.rs | 58 +++++++++---------- processor/Cargo.toml | 2 +- processor/src/debug.rs | 44 ++++++++++++-- processor/src/lib.rs | 1 + 15 files changed, 147 insertions(+), 63 deletions(-) diff --git a/assembly/src/assembler/context.rs b/assembly/src/assembler/context.rs index 786d688ffb..681e95d535 100644 --- a/assembly/src/assembler/context.rs +++ b/assembly/src/assembler/context.rs @@ -250,11 +250,17 @@ impl AssemblyContext { // HELPER METHODS // -------------------------------------------------------------------------------------------- - /// Returns the context of the procedure currently being complied, or None if module or + /// Returns the context of the procedure currently being compiled, or None if module or /// procedure stacks are empty. fn current_proc_context(&self) -> Option<&ProcedureContext> { self.module_stack.last().and_then(|m| m.proc_stack.last()) } + + /// Returns the name of the procedure currently being compilied, or None if module or + /// procedure stacks are empty. + pub(crate) fn current_proc_context_name(&self) -> Option<&str> { + self.current_proc_context().map(|p| p.name().as_ref()) + } } // MODULE CONTEXT @@ -478,6 +484,10 @@ impl ProcedureContext { self.name.is_main() } + pub const fn name(&self) -> &ProcedureName { + &self.name + } + pub fn into_procedure(self, id: ProcedureId, code_root: CodeBlock) -> Procedure { let Self { name, diff --git a/assembly/src/assembler/instruction/mod.rs b/assembly/src/assembler/instruction/mod.rs index 25eadb7b64..6a874f79d2 100644 --- a/assembly/src/assembler/instruction/mod.rs +++ b/assembly/src/assembler/instruction/mod.rs @@ -34,7 +34,7 @@ impl Assembler { // this will allow us to map the instruction to the sequence of operations which were // executed as a part of this instruction. if self.in_debug_mode() { - span.track_instruction(instruction); + span.track_instruction(instruction, ctx); } let result = match instruction { @@ -306,6 +306,15 @@ impl Assembler { Instruction::CallLocal(idx) => self.call_local(*idx, ctx), Instruction::CallImported(id) => self.call_imported(id, ctx), Instruction::SysCall(id) => self.syscall(id, ctx), + + // ----- debug decorators ------------------------------------------------------------- + Instruction::Breakpoint => { + if self.in_debug_mode() { + span.add_op(Noop)?; + span.track_instruction(instruction, ctx); + } + Ok(None) + } }; // compute and update the cycle count of the instruction which just finished executing diff --git a/assembly/src/assembler/span_builder.rs b/assembly/src/assembler/span_builder.rs index bcc8ac0029..fd51b748de 100644 --- a/assembly/src/assembler/span_builder.rs +++ b/assembly/src/assembler/span_builder.rs @@ -1,6 +1,6 @@ use super::{ - AssemblyError, BodyWrapper, Borrow, CodeBlock, Decorator, DecoratorList, Instruction, - Operation, ToString, Vec, + AssemblyContext, AssemblyError, BodyWrapper, Borrow, CodeBlock, Decorator, DecoratorList, + Instruction, Operation, ToString, Vec, }; use vm_core::AssemblyOp; @@ -102,8 +102,11 @@ impl SpanBuilder { /// /// This indicates that the provided instruction should be tracked and the cycle count for /// this instruction will be computed when the call to set_instruction_cycle_count() is made. - pub fn track_instruction(&mut self, instruction: &Instruction) { - let op = AssemblyOp::new(instruction.to_string(), 0); + pub fn track_instruction(&mut self, instruction: &Instruction, ctx: &mut AssemblyContext) { + let proc = ctx.current_proc_context_name().unwrap_or("#main"); + let op = instruction.to_string(); + let op = format!("{proc}:{op}"); + let op = AssemblyOp::new(op, 0); self.push_decorator(Decorator::AsmOp(op)); self.last_asmop_pos = self.decorators.len() - 1; } diff --git a/assembly/src/lib.rs b/assembly/src/lib.rs index a3d1f8e6b4..eaa14f60a5 100644 --- a/assembly/src/lib.rs +++ b/assembly/src/lib.rs @@ -24,7 +24,7 @@ pub use procedures::{ProcedureId, ProcedureName}; mod parsers; use parsers::PROCEDURE_LABEL_PARSER; -pub use parsers::{parse_module, parse_program, ModuleAst, ProcedureAst, ProgramAst}; +pub use parsers::{parse_module, parse_program, Instruction, ModuleAst, ProcedureAst, ProgramAst}; mod serde; pub use serde::{ByteReader, ByteWriter, Deserializable, Serializable}; diff --git a/assembly/src/parsers/context.rs b/assembly/src/parsers/context.rs index ca5972709f..3cb2e9e0ac 100644 --- a/assembly/src/parsers/context.rs +++ b/assembly/src/parsers/context.rs @@ -511,6 +511,9 @@ impl ParserContext { // ----- constant statements ---------------------------------------------------------- "const" => Err(ParsingError::const_invalid_scope(op)), + // ----- debug decorators ------------------------------------------------------------- + "breakpoint" => simple_instruction(op, Breakpoint), + // ----- catch all -------------------------------------------------------------------- _ => Err(ParsingError::invalid_op(op)), } diff --git a/assembly/src/parsers/mod.rs b/assembly/src/parsers/mod.rs index f58704cd02..b66a07422e 100644 --- a/assembly/src/parsers/mod.rs +++ b/assembly/src/parsers/mod.rs @@ -7,7 +7,8 @@ use core::{fmt::Display, ops::RangeBounds}; mod nodes; use crate::utils::bound_into_included_u64; -pub(crate) use nodes::{Instruction, Node}; +pub use nodes::Instruction; +pub(crate) use nodes::Node; mod context; use context::ParserContext; mod labels; diff --git a/assembly/src/parsers/nodes.rs b/assembly/src/parsers/nodes.rs index 9884e1d750..99caf12b91 100644 --- a/assembly/src/parsers/nodes.rs +++ b/assembly/src/parsers/nodes.rs @@ -277,6 +277,9 @@ pub enum Instruction { CallLocal(u16), CallImported(ProcedureId), SysCall(ProcedureId), + + // ----- debug decorators --------------------------------------------------------------------- + Breakpoint, } impl fmt::Display for Instruction { @@ -543,6 +546,9 @@ impl fmt::Display for Instruction { Self::CallLocal(index) => write!(f, "call.{index}"), Self::CallImported(proc_id) => write!(f, "call.{proc_id}"), Self::SysCall(proc_id) => write!(f, "syscall.{proc_id}"), + + // ----- debug decorators ------------------------------------------------------------- + Self::Breakpoint => write!(f, "breakpoint"), } } } diff --git a/assembly/src/parsers/serde/serialization.rs b/assembly/src/parsers/serde/serialization.rs index 3a6eaed5b2..e0e9b413b3 100644 --- a/assembly/src/parsers/serde/serialization.rs +++ b/assembly/src/parsers/serde/serialization.rs @@ -467,6 +467,11 @@ impl Serializable for Instruction { OpCode::SysCall.write_into(target)?; imported.write_into(target)? } + + // ----- debug decorators ------------------------------------------------------------- + Self::Breakpoint => { + // this is a transparent instruction and will not be encoded into the library + } } Ok(()) } diff --git a/miden/src/cli/debug/command.rs b/miden/src/cli/debug/command.rs index 70465a3687..71447f1f2a 100644 --- a/miden/src/cli/debug/command.rs +++ b/miden/src/cli/debug/command.rs @@ -3,8 +3,8 @@ pub enum DebugCommand { Continue, Next(usize), - RewindAll, - Rewind(usize), + Rewind, + Back(usize), PrintState, PrintStack, PrintStackItem(usize), @@ -36,7 +36,7 @@ impl DebugCommand { "n" | "next" => Self::parse_next(tokens.by_ref())?, "c" | "continue" => Self::Continue, "b" | "back" => Self::parse_back(tokens.by_ref())?, - "r" | "rewind" => Self::RewindAll, + "r" | "rewind" => Self::Rewind, "p" | "print" => Self::parse_print(tokens.by_ref())?, "l" | "clock" => Self::Clock, "h" | "?" | "help" => Self::Help, @@ -89,9 +89,9 @@ impl DebugCommand { n, err ) })?, - None => return Ok(Self::Rewind(1)), + None => return Ok(Self::Back(1)), }; - Ok(Self::Rewind(num_cycles)) + Ok(Self::Back(num_cycles)) } /// parse print command - p [m|s] [addr] diff --git a/miden/src/cli/debug/executor.rs b/miden/src/cli/debug/executor.rs index b081aff807..f9b7ca5e26 100644 --- a/miden/src/cli/debug/executor.rs +++ b/miden/src/cli/debug/executor.rs @@ -45,6 +45,9 @@ impl DebugExecutor { DebugCommand::Continue => { while let Some(new_vm_state) = self.next_vm_state() { self.vm_state = new_vm_state; + if self.should_break() { + break; + } } self.print_vm_state(); } @@ -53,23 +56,29 @@ impl DebugExecutor { match self.next_vm_state() { Some(next_vm_state) => { self.vm_state = next_vm_state; + if self.should_break() { + break; + } } None => break, } } self.print_vm_state(); } - DebugCommand::RewindAll => { + DebugCommand::Rewind => { while let Some(new_vm_state) = self.prev_vm_state() { self.vm_state = new_vm_state; } self.print_vm_state(); } - DebugCommand::Rewind(cycles) => { + DebugCommand::Back(cycles) => { for _cycle in 0..cycles { match self.prev_vm_state() { Some(new_vm_state) => { self.vm_state = new_vm_state; + if self.should_break() { + break; + } } None => break, } @@ -213,4 +222,9 @@ impl DebugExecutor { println!("{}", message); } + + /// Returns `true` if the current state should break. + fn should_break(&self) -> bool { + self.vm_state.asmop.as_ref().map(|asm| asm.should_break()).unwrap_or(false) + } } diff --git a/miden/tests/integration/exec_iters.rs b/miden/tests/integration/exec_iters.rs index ba45b6c47f..601a7eb8d2 100644 --- a/miden/tests/integration/exec_iters.rs +++ b/miden/tests/integration/exec_iters.rs @@ -39,7 +39,7 @@ fn test_exec_iter() { clk: 2, ctx: 0, op: Some(Operation::Pad), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 1)), + asmop: Some(AsmOpInfo::new("#main:mem_storew.1".to_string(), 3, 1)), stack: [0, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].to_elements(), fmp, memory: Vec::new(), @@ -48,7 +48,7 @@ fn test_exec_iter() { clk: 3, ctx: 0, op: Some(Operation::Incr), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 2)), + asmop: Some(AsmOpInfo::new("#main:mem_storew.1".to_string(), 3, 2)), stack: [1, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2].to_elements(), fmp, memory: Vec::new(), @@ -57,7 +57,7 @@ fn test_exec_iter() { clk: 4, ctx: 0, op: Some(Operation::MStoreW), - asmop: Some(AsmOpInfo::new("mem_storew.1".to_string(), 3, 3)), + asmop: Some(AsmOpInfo::new("#main:mem_storew.1".to_string(), 3, 3)), stack: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].to_elements(), fmp, memory: mem.clone(), @@ -66,7 +66,7 @@ fn test_exec_iter() { clk: 5, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 1)), + asmop: Some(AsmOpInfo::new("#main:dropw".to_string(), 4, 1)), stack: [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0].to_elements(), fmp, memory: mem.clone(), @@ -75,7 +75,7 @@ fn test_exec_iter() { clk: 6, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 2)), + asmop: Some(AsmOpInfo::new("#main:dropw".to_string(), 4, 2)), stack: [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -84,7 +84,7 @@ fn test_exec_iter() { clk: 7, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 3)), + asmop: Some(AsmOpInfo::new("#main:dropw".to_string(), 4, 3)), stack: [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -93,7 +93,7 @@ fn test_exec_iter() { clk: 8, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("dropw".to_string(), 4, 4)), + asmop: Some(AsmOpInfo::new("#main:dropw".to_string(), 4, 4)), stack: [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -102,7 +102,7 @@ fn test_exec_iter() { clk: 9, ctx: 0, op: Some(Operation::Push(Felt::new(17))), - asmop: Some(AsmOpInfo::new("push.17".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.17".to_string(), 1, 1)), stack: [17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0].to_elements(), fmp, memory: mem.clone(), @@ -138,7 +138,7 @@ fn test_exec_iter() { clk: 13, ctx: 0, op: Some(Operation::Pad), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 1)), + asmop: Some(AsmOpInfo::new("foo:loc_store.0".to_string(), 4, 1)), stack: [0, 17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0].to_elements(), fmp: next_fmp, memory: mem.clone(), @@ -147,7 +147,7 @@ fn test_exec_iter() { clk: 14, ctx: 0, op: Some(Operation::FmpAdd), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 2)), + asmop: Some(AsmOpInfo::new("foo:loc_store.0".to_string(), 4, 2)), stack: [2u64.pow(30) + 1, 17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0] .to_elements(), fmp: next_fmp, @@ -157,7 +157,7 @@ fn test_exec_iter() { clk: 15, ctx: 0, op: Some(Operation::MStore), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 3)), + asmop: Some(AsmOpInfo::new("foo:loc_store.0".to_string(), 4, 3)), stack: [17, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0].to_elements(), fmp: next_fmp, memory: vec![ @@ -169,7 +169,7 @@ fn test_exec_iter() { clk: 16, ctx: 0, op: Some(Operation::Drop), - asmop: Some(AsmOpInfo::new("loc_store.0".to_string(), 4, 4)), + asmop: Some(AsmOpInfo::new("foo:loc_store.0".to_string(), 4, 4)), stack: [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0].to_elements(), fmp: next_fmp, memory: vec![ diff --git a/miden/tests/integration/operations/decorators/asmop.rs b/miden/tests/integration/operations/decorators/asmop.rs index 93e22fc37b..e7511dc672 100644 --- a/miden/tests/integration/operations/decorators/asmop.rs +++ b/miden/tests/integration/operations/decorators/asmop.rs @@ -20,22 +20,22 @@ fn asmop_one_span_block_test() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { @@ -66,22 +66,22 @@ fn asmop_with_one_procedure() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("foo:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("foo:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("foo:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("foo:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { @@ -116,62 +116,62 @@ fn asmop_repeat_test() { }, VmStatePartial { clk: 2, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 4, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 5, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { clk: 6, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { clk: 10, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 11, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 12, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 13, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { @@ -231,7 +231,7 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("eq".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:eq".to_string(), 1, 1)), op: Some(Operation::Eq), }, VmStatePartial { @@ -251,22 +251,22 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 1)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 1)), op: Some(Operation::Pad), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.1".to_string(), 2, 2)), + asmop: Some(AsmOpInfo::new("#main:push.1".to_string(), 2, 2)), op: Some(Operation::Incr), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("push.2".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.2".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(2))), }, VmStatePartial { clk: 10, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { @@ -309,7 +309,7 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 3, - asmop: Some(AsmOpInfo::new("eq".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:eq".to_string(), 1, 1)), op: Some(Operation::Eq), }, VmStatePartial { @@ -329,17 +329,17 @@ fn asmop_conditional_execution_test() { }, VmStatePartial { clk: 7, - asmop: Some(AsmOpInfo::new("push.3".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.3".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(3))), }, VmStatePartial { clk: 8, - asmop: Some(AsmOpInfo::new("push.4".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:push.4".to_string(), 1, 1)), op: Some(Operation::Push(Felt::new(4))), }, VmStatePartial { clk: 9, - asmop: Some(AsmOpInfo::new("add".to_string(), 1, 1)), + asmop: Some(AsmOpInfo::new("#main:add".to_string(), 1, 1)), op: Some(Operation::Add), }, VmStatePartial { diff --git a/processor/Cargo.toml b/processor/Cargo.toml index 490d340516..aabb18b8b7 100644 --- a/processor/Cargo.toml +++ b/processor/Cargo.toml @@ -23,12 +23,12 @@ std = ["vm-core/std", "winter-prover/std", "log/std"] [dependencies] log = "0.4.14" +assembly = { package = "miden-assembly", path = "../assembly", version = "0.5", default-features = false } vm-core = { package = "miden-core", path = "../core", version = "0.5", default-features = false } winter-prover = { package = "winter-prover", version = "0.5", default-features = false } [dev-dependencies] logtest = { version = "2.0", default-features = false } -miden-assembly = { package = "miden-assembly", path = "../assembly", version = "0.5", default-features = false } rand-utils = { package = "winter-rand-utils", version = "0.5" } winter-fri = { package = "winter-fri", version = "0.5" } winter-utils = { package = "winter-utils", version = "0.5" } diff --git a/processor/src/debug.rs b/processor/src/debug.rs index a6475eb821..d740fba638 100644 --- a/processor/src/debug.rs +++ b/processor/src/debug.rs @@ -1,9 +1,12 @@ use crate::{ - advice::AdviceProvider, Chiplets, Decoder, ExecutionError, Felt, Process, Stack, StarkField, - System, Vec, + advice::AdviceProvider, Chiplets, Decoder, ExecutionError, Felt, Instruction, Process, Stack, + StarkField, System, Vec, }; use core::fmt; -use vm_core::{utils::string::String, Operation, StackOutputs, Word}; +use vm_core::{ + utils::string::{String, ToString}, + Operation, StackOutputs, Word, +}; /// VmState holds a current process state information at a specific clock cycle. #[derive(Clone, Debug, Eq, PartialEq)] @@ -24,8 +27,17 @@ impl fmt::Display for VmState { self.memory.iter().map(|x| (x.0, word_to_ints(&x.1))).collect(); write!( f, - "clk={}, op={:?}, asmop={:?}, fmp={}, stack={stack:?}, memory={memory:?}", - self.clk, self.op, self.asmop, self.fmp + "clk={}{}{}, fmp={}, stack={stack:?}, memory={memory:?}", + self.clk, + match self.op { + Some(op) => format!(", op={op}"), + None => "".to_string(), + }, + match &self.asmop { + Some(op) => format!(", {op}"), + None => "".to_string(), + }, + self.fmp ) } } @@ -106,7 +118,7 @@ impl VmStateIterator { // if this is not the first asmop in the list and if this op is part of current asmop, // returns a new instance of [AsmOp] instantiated with current asmop, num_cycles and // cycle_idx of current op. - else if self.asmop_idx > 0 && cycle_idx <= curr_asmop.1.num_cycles() { + else if self.asmop_idx > 0 && cycle_idx <= curr_asmop.1.num_cycles() as u8 { let asmop = Some(AsmOpInfo::new( curr_asmop.1.op().clone(), curr_asmop.1.num_cycles(), @@ -223,6 +235,7 @@ pub struct AsmOpInfo { op: String, num_cycles: u8, cycle_idx: u8, + should_break: bool, } // ASMOP STATE @@ -233,10 +246,18 @@ impl AsmOpInfo { /// cycles it takes to execute the assembly instruction and op index in sequence of operations /// corresponding to the current assembly instruction. The first index is 1 instead of 0. pub fn new(op: String, num_cycles: u8, cycle_idx: u8) -> Self { + // breakpoint doesn't exist as operation. we instead assume the properly formatted + // decorator will carry this data + let should_break = op + .split(':') + .last() + .filter(|l| l == &Instruction::Breakpoint.to_string().as_str()) + .is_some(); Self { op, num_cycles, cycle_idx, + should_break, } } @@ -266,4 +287,15 @@ impl AsmOpInfo { pub fn cycle_idx(&self) -> u8 { self.cycle_idx } + + /// Returns `true` if the debug should break for this line. + pub const fn should_break(&self) -> bool { + self.should_break + } +} + +impl fmt::Display for AsmOpInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "decorator={}, cost={}, cycles={}", self.op, self.num_cycles, self.cycle_idx) + } } diff --git a/processor/src/lib.rs b/processor/src/lib.rs index 21b95bb879..894d7fe30f 100644 --- a/processor/src/lib.rs +++ b/processor/src/lib.rs @@ -4,6 +4,7 @@ #[macro_use] extern crate alloc; +use assembly::Instruction; pub use vm_core::{ chiplets::hasher::Digest, crypto::{