Skip to content

Commit

Permalink
feat: implement trace instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
Fumuran committed Jan 16, 2024
1 parent 2662639 commit 3c19637
Show file tree
Hide file tree
Showing 19 changed files with 205 additions and 60 deletions.
4 changes: 4 additions & 0 deletions air/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,15 @@ impl From<ProvingOptions> for WinterProofOptions {
pub struct ExecutionOptions {
max_cycles: u32,
expected_cycles: u32,
enable_tracing: bool,
}

impl Default for ExecutionOptions {
fn default() -> Self {
ExecutionOptions {
max_cycles: u32::MAX,
expected_cycles: MIN_TRACE_LEN as u32,
enable_tracing: false,
}
}
}
Expand All @@ -171,6 +173,7 @@ impl ExecutionOptions {
pub fn new(
max_cycles: Option<u32>,
expected_cycles: u32,
enable_tracing: bool,
) -> Result<Self, ExecutionOptionsError> {
let max_cycles = max_cycles.unwrap_or(u32::MAX);
if max_cycles < MIN_TRACE_LEN as u32 {
Expand All @@ -187,6 +190,7 @@ impl ExecutionOptions {
Ok(ExecutionOptions {
max_cycles,
expected_cycles,
enable_tracing,
})
}

Expand Down
6 changes: 6 additions & 0 deletions assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,12 @@ impl Assembler {
span.push_decorator(Decorator::Event(*event_id));
Ok(None)
}

// ----- trace instruction ------------------------------------------------------------
Instruction::Trace(trace_id) => {
span.push_decorator(Decorator::Trace(*trace_id));
Ok(None)
}
};

// compute and update the cycle count of the instruction which just finished executing
Expand Down
6 changes: 6 additions & 0 deletions assembly/src/ast/nodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ pub enum Instruction {

// ----- emit instruction ---------------------------------------------------------------------
Emit(u32),

// ----- trace instruction --------------------------------------------------------------------
Trace(u32),
}

impl Instruction {
Expand Down Expand Up @@ -547,6 +550,9 @@ impl fmt::Display for Instruction {

// ----- emit instruction -------------------------------------------------------------
Self::Emit(value) => write!(f, "emit.{value}"),

// ----- trace instruction ------------------------------------------------------------
Self::Trace(value) => write!(f, "trace.{value}"),
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion assembly/src/ast/nodes/serde/deserialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,12 @@ impl Deserializable for Instruction {
Ok(Instruction::Debug(options))
}

// ----- emit ------------------------------------------------------------------------
// ----- emit -------------------------------------------------------------------------
OpCode::Emit => Ok(Instruction::Emit(source.read_u32()?)),

// ----- trace ------------------------------------------------------------------------
OpCode::Trace => Ok(Instruction::Trace(source.read_u32()?)),

// ----- control flow -----------------------------------------------------------------
// control flow instructions should be parsed as a part of Node::read_from() and we
// should never get here
Expand Down
5 changes: 4 additions & 1 deletion assembly/src/ast/nodes/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,12 @@ pub enum OpCode {
// ----- debugging ----------------------------------------------------------------------------
Debug = 220,

// ----- emit --------------------------------------------------------------------------------
// ----- emit ---------------------------------------------------------------------------------
Emit = 221,

// ----- trace --------------------------------------------------------------------------------
Trace = 222,

// ----- control flow -------------------------------------------------------------------------
IfElse = 253,
Repeat = 254,
Expand Down
6 changes: 6 additions & 0 deletions assembly/src/ast/nodes/serde/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@ impl Serializable for Instruction {
OpCode::Emit.write_into(target);
target.write_u32(*event_id);
}

// ----- trace instruction ------------------------------------------------------------
Self::Trace(trace_id) => {
OpCode::Trace.write_into(target);
target.write_u32(*trace_id);
}
}
}
}
5 changes: 5 additions & 0 deletions assembly/src/ast/parsers/context.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::ast::parsers::trace;

use super::{
super::ProcReExport, adv_ops, debug, emit, field_ops, io_ops, stack_ops, sys_ops, u32_ops,
CodeBody, Instruction, InvocationTarget, LibraryPath, LocalConstMap, LocalProcMap,
Expand Down Expand Up @@ -629,6 +631,9 @@ impl ParserContext<'_> {
// ----- emit instruction -------------------------------------------------------------
"emit" => emit::parse_emit(op, &self.local_constants),

// ----- trace instruction ------------------------------------------------------------
"trace" => trace::parse_trace(op, &self.local_constants),

// ----- catch all --------------------------------------------------------------------
_ => Err(ParsingError::invalid_op(op)),
}
Expand Down
1 change: 1 addition & 0 deletions assembly/src/ast/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod field_ops;
mod io_ops;
mod stack_ops;
mod sys_ops;
mod trace;
mod u32_ops;

mod constants;
Expand Down
29 changes: 29 additions & 0 deletions assembly/src/ast/parsers/trace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use super::{
parse_param_with_constant_lookup,
Instruction::*,
LocalConstMap,
Node::{self, Instruction},
ParsingError, Token,
};

// EMIT PARSER
// ================================================================================================

/// Returns `Trace` instruction node with the parsed `trace_id`.
///
/// The `trace_id` can be provided as a constant label or as a u32 value.
///
/// # Errors
/// Returns an error if the constant does not exist or if the value is not a u32.
pub fn parse_trace(op: &Token, constants: &LocalConstMap) -> Result<Node, ParsingError> {
debug_assert_eq!(op.parts()[0], "trace");
match op.num_parts() {
0 => unreachable!(),
1 => Err(ParsingError::missing_param(op, "trace.<tace_id>")),
2 => {
let trace_id = parse_param_with_constant_lookup(op, 1, constants)?;
Ok(Instruction(Trace(trace_id)))
}
_ => Err(ParsingError::extra_param(op)),
}
}
3 changes: 3 additions & 0 deletions core/src/operations/decorators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub enum Decorator {
Debug(DebugOptions),
/// Emits an event to the host.
Event(u32),
/// Emmits a trace to the host.
Trace(u32),
}

impl fmt::Display for Decorator {
Expand All @@ -43,6 +45,7 @@ impl fmt::Display for Decorator {
}
Self::Debug(options) => write!(f, "debug({options})"),
Self::Event(event_id) => write!(f, "event({})", event_id),
Self::Trace(trace_id) => write!(f, "trace({})", trace_id),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion miden/src/cli/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ pub struct ProveCmd {
/// Security level for execution proofs generated by the VM
#[clap(short = 's', long = "security", default_value = "96bits")]
security: String,

/// Enable tracing to monitor execution of the VM
#[clap(short = 't', long = "tracing")]
tracing: bool,
}

impl ProveCmd {
pub fn get_proof_options(&self) -> Result<ProvingOptions, ExecutionOptionsError> {
let exec_options = ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles)?;
let exec_options =
ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles, self.tracing)?;
Ok(match self.security.as_str() {
"96bits" => ProvingOptions::with_96_bit_security(self.recursive),
"128bits" => ProvingOptions::with_128_bit_security(self.recursive),
Expand Down
9 changes: 7 additions & 2 deletions miden/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ pub struct RunCmd {
/// Path to output file
#[clap(short = 'o', long = "output", value_parser)]
output_file: Option<PathBuf>,

/// Enable tracing to monitor execution of the VM
#[clap(short = 't', long = "tracing")]
tracing: bool,
}

impl RunCmd {
Expand Down Expand Up @@ -106,8 +110,9 @@ fn run_program(params: &RunCmd) -> Result<(ExecutionTrace, [u8; 32]), String> {
let input_data = InputFile::read(&params.input_file, &params.assembly_file)?;

// get execution options
let execution_options = ExecutionOptions::new(Some(params.max_cycles), params.expected_cycles)
.map_err(|err| format!("{err}"))?;
let execution_options =
ExecutionOptions::new(Some(params.max_cycles), params.expected_cycles, params.tracing)
.map_err(|err| format!("{err}"))?;

// fetch the stack and program inputs from the arguments
let stack_inputs = input_data.parse_stack_inputs()?;
Expand Down
7 changes: 6 additions & 1 deletion miden/src/examples/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ pub struct ExampleOptions {
/// Security level for execution proofs generated by the VM
#[clap(short = 's', long = "security", default_value = "96bits")]
security: String,

/// Enable tracing to monitor execution of the VM
#[clap(short = 't', long = "tracing")]
tracing: bool,
}

#[derive(Debug, Clone, Parser)]
Expand All @@ -67,7 +71,8 @@ pub enum ExampleType {

impl ExampleOptions {
pub fn get_proof_options(&self) -> Result<ProvingOptions, ExecutionOptionsError> {
let exec_options = ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles)?;
let exec_options =
ExecutionOptions::new(Some(self.max_cycles), self.expected_cycles, self.tracing)?;
Ok(match self.security.as_str() {
"96bits" => ProvingOptions::with_96_bit_security(self.recursive),
"128bits" => ProvingOptions::with_128_bit_security(self.recursive),
Expand Down
54 changes: 2 additions & 52 deletions miden/tests/integration/operations/decorators/event.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,5 @@
use super::TestHost;
use assembly::Assembler;
use processor::{
AdviceExtractor, AdviceProvider, ExecutionError, Host, HostResponse, MemAdviceProvider,
ProcessState,
};
use vm_core::AdviceInjector;

// EVENT TEST HOST
// ================================================================================================
pub struct TestEventHost<A> {
pub adv_provider: A,
pub event_handler: Vec<u32>,
}

impl Default for TestEventHost<MemAdviceProvider> {
fn default() -> Self {
Self {
adv_provider: MemAdviceProvider::default(),
event_handler: Vec::new(),
}
}
}

impl<A: AdviceProvider> Host for TestEventHost<A> {
fn get_advice<S: ProcessState>(
&mut self,
process: &S,
extractor: AdviceExtractor,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.get_advice(process, &extractor)
}

fn set_advice<S: ProcessState>(
&mut self,
process: &S,
injector: AdviceInjector,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.set_advice(process, &injector)
}

fn on_event<S: ProcessState>(
&mut self,
_process: &S,
event_id: u32,
) -> Result<HostResponse, ExecutionError> {
self.event_handler.push(event_id);
Ok(HostResponse::None)
}
}

// TESTS
// ================================================================================================

#[test]
fn test_event_handling() {
Expand All @@ -63,7 +13,7 @@ fn test_event_handling() {

// compile and execute program
let program = Assembler::default().compile(source).unwrap();
let mut host = TestEventHost::default();
let mut host = TestHost::default();
processor::execute(&program, Default::default(), &mut host, Default::default()).unwrap();

// make sure events were handled correctly
Expand Down
61 changes: 61 additions & 0 deletions miden/tests/integration/operations/decorators/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,64 @@
use processor::{
AdviceExtractor, AdviceProvider, ExecutionError, Host, HostResponse, MemAdviceProvider,
ProcessState,
};
use vm_core::AdviceInjector;

mod advice;
mod asmop;
mod event;
mod trace;

// TEST HOST
// ================================================================================================
pub struct TestHost<A> {
pub adv_provider: A,
pub event_handler: Vec<u32>,
pub trace_handler: Vec<u32>,
}

impl Default for TestHost<MemAdviceProvider> {
fn default() -> Self {
Self {
adv_provider: MemAdviceProvider::default(),
event_handler: Vec::new(),
trace_handler: Vec::new(),
}
}
}

impl<A: AdviceProvider> Host for TestHost<A> {
fn get_advice<S: ProcessState>(
&mut self,
process: &S,
extractor: AdviceExtractor,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.get_advice(process, &extractor)
}

fn set_advice<S: ProcessState>(
&mut self,
process: &S,
injector: AdviceInjector,
) -> Result<HostResponse, ExecutionError> {
self.adv_provider.set_advice(process, &injector)
}

fn on_event<S: ProcessState>(
&mut self,
_process: &S,
event_id: u32,
) -> Result<HostResponse, ExecutionError> {
self.event_handler.push(event_id);
Ok(HostResponse::None)
}

fn on_trace<S: ProcessState>(
&mut self,
_process: &S,
trace_id: u32,
) -> Result<HostResponse, ExecutionError> {
self.trace_handler.push(trace_id);
Ok(HostResponse::None)
}
}
22 changes: 22 additions & 0 deletions miden/tests/integration/operations/decorators/trace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use super::TestHost;
use assembly::Assembler;

#[test]
fn test_trace_handling() {
let source = "\
begin
push.1
trace.1
push.2
trace.2
end";

// compile and execute program
let program = Assembler::default().compile(source).unwrap();
let mut host = TestHost::default();
processor::execute(&program, Default::default(), &mut host, Default::default()).unwrap();

// make sure traces were handled correctly
let expected = vec![1, 2];
assert_eq!(host.trace_handler, expected);
}
Loading

0 comments on commit 3c19637

Please sign in to comment.