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 8, 2023
1 parent 0ec30b5 commit 0170341
Show file tree
Hide file tree
Showing 16 changed files with 193 additions and 112 deletions.
14 changes: 13 additions & 1 deletion assembly/src/assembler/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,19 @@ 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_context_name(&self) -> &str {
self.current_proc_context()
.map(|p| p.name().as_ref())
.unwrap_or(ProcedureName::MAIN_PROC_NAME)
}
}

// MODULE CONTEXT
Expand Down Expand Up @@ -478,6 +486,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_context_name();
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
4 changes: 4 additions & 0 deletions miden/examples/nprime/nprime.masm
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ proc.is_not_prime
# [j, candidate, i, n, primes..]
push.0

breakpoint

exec.is_not_prime_should_continue
while.true
# [j, candidate, i, n, primes..]
Expand Down Expand Up @@ -161,6 +163,8 @@ begin
push.2
exec.append

breakpoint

# append second known prime
push.3
exec.append
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
33 changes: 18 additions & 15 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 => {
while let Some(new_vm_state) = self.prev_vm_state() {
DebugCommand::Rewind => {
while let Some(new_vm_state) = self.vm_state_iter.back() {
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() {
match self.vm_state_iter.back() {
Some(new_vm_state) => {
self.vm_state = new_vm_state;
if self.should_break() {
break;
}
}
None => break,
}
Expand Down Expand Up @@ -105,17 +114,6 @@ impl DebugExecutor {
}
}

/// iterates to the previous clock cycle.
fn prev_vm_state(&mut self) -> Option<VmState> {
match self.vm_state_iter.next_back() {
Some(prev_vm_state_result) => prev_vm_state_result.ok(),
None => {
println!("At start of program execution.");
None
}
}
}

// ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -213,4 +211,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 0170341

Please sign in to comment.