Skip to content

Commit

Permalink
feat: add breakpoint instruction
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vlopes11 committed Mar 6, 2023
1 parent 0ec30b5 commit 9a29498
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 63 deletions.
12 changes: 11 additions & 1 deletion assembly/src/assembler/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 10 additions & 1 deletion assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions assembly/src/assembler/span_builder.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion assembly/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
3 changes: 3 additions & 0 deletions assembly/src/parsers/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
}
Expand Down
3 changes: 2 additions & 1 deletion assembly/src/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions assembly/src/parsers/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ pub enum Instruction {
CallLocal(u16),
CallImported(ProcedureId),
SysCall(ProcedureId),

// ----- debug decorators ---------------------------------------------------------------------
Breakpoint,
}

impl fmt::Display for Instruction {
Expand Down Expand Up @@ -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"),
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions assembly/src/parsers/serde/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
10 changes: 5 additions & 5 deletions miden/src/cli/debug/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
pub enum DebugCommand {
Continue,
Next(usize),
RewindAll,
Rewind(usize),
Rewind,
Back(usize),
PrintState,
PrintStack,
PrintStackItem(usize),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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]
Expand Down
18 changes: 16 additions & 2 deletions miden/src/cli/debug/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -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,
}
Expand Down Expand Up @@ -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)
}
}
24 changes: 12 additions & 12 deletions miden/tests/integration/exec_iters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand All @@ -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(),
Expand Down Expand Up @@ -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(),
Expand All @@ -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,
Expand All @@ -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![
Expand All @@ -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![
Expand Down
Loading

0 comments on commit 9a29498

Please sign in to comment.