1#![no_std]
2
3#[macro_use]
4extern crate alloc;
5
6#[cfg(feature = "std")]
7extern crate std;
8
9use alloc::vec::Vec;
10
11use miden_air::trace::{
12 CHIPLETS_WIDTH, DECODER_TRACE_WIDTH, MIN_TRACE_LEN, RANGE_CHECK_TRACE_WIDTH, STACK_TRACE_WIDTH,
13 SYS_TRACE_WIDTH,
14};
15pub use miden_air::{ExecutionOptions, ExecutionOptionsError, RowIndex};
16pub use vm_core::{
17 AssemblyOp, EMPTY_WORD, Felt, Kernel, ONE, Operation, Program, ProgramInfo, QuadExtension,
18 StackInputs, StackOutputs, Word, ZERO,
19 chiplets::hasher::Digest,
20 crypto::merkle::SMT_DEPTH,
21 errors::InputError,
22 mast::{MastForest, MastNode, MastNodeId},
23 sys_events::SystemEvent,
24 utils::{DeserializationError, collections::KvMap},
25};
26use vm_core::{
27 Decorator, DecoratorIterator, FieldElement,
28 mast::{
29 BasicBlockNode, CallNode, DynNode, JoinNode, LoopNode, OP_GROUP_SIZE, OpBatch, SplitNode,
30 },
31};
32pub use winter_prover::matrix::ColMatrix;
33
34mod operations;
35
36mod system;
37use system::System;
38pub use system::{ContextId, FMP_MIN, SYSCALL_FMP_MIN};
39
40mod decoder;
41use decoder::Decoder;
42
43mod stack;
44use stack::Stack;
45
46mod range;
47use range::RangeChecker;
48
49mod host;
50pub use host::{
51 DefaultHost, Host, MastForestStore, MemMastForestStore,
52 advice::{AdviceInputs, AdviceProvider, AdviceSource, MemAdviceProvider, RecAdviceProvider},
53};
54
55mod chiplets;
56use chiplets::Chiplets;
57
58mod trace;
59use trace::TraceFragment;
60pub use trace::{ChipletsLengths, ExecutionTrace, NUM_RAND_ROWS, TraceLenSummary};
61
62mod errors;
63pub use errors::{ExecutionError, Ext2InttError};
64
65pub mod utils;
66
67mod debug;
68pub use debug::{AsmOpInfo, VmState, VmStateIterator};
69
70pub mod math {
74 pub use vm_core::{Felt, FieldElement, StarkField};
75 pub use winter_prover::math::fft;
76}
77
78pub mod crypto {
79 pub use vm_core::crypto::{
80 hash::{
81 Blake3_192, Blake3_256, ElementHasher, Hasher, Rpo256, RpoDigest, Rpx256, RpxDigest,
82 },
83 merkle::{
84 MerkleError, MerklePath, MerkleStore, MerkleTree, NodeIndex, PartialMerkleTree,
85 SimpleSmt,
86 },
87 random::{RandomCoin, RpoRandomCoin, RpxRandomCoin, WinterRandomCoin},
88 };
89}
90
91type QuadFelt = QuadExtension<Felt>;
95
96type SysTrace = [Vec<Felt>; SYS_TRACE_WIDTH];
97
98pub struct DecoderTrace {
99 trace: [Vec<Felt>; DECODER_TRACE_WIDTH],
100 aux_builder: decoder::AuxTraceBuilder,
101}
102
103pub struct StackTrace {
104 trace: [Vec<Felt>; STACK_TRACE_WIDTH],
105}
106
107pub struct RangeCheckTrace {
108 trace: [Vec<Felt>; RANGE_CHECK_TRACE_WIDTH],
109 aux_builder: range::AuxTraceBuilder,
110}
111
112pub struct ChipletsTrace {
113 trace: [Vec<Felt>; CHIPLETS_WIDTH],
114 aux_builder: chiplets::AuxTraceBuilder,
115}
116
117#[tracing::instrument("execute_program", skip_all)]
126pub fn execute(
127 program: &Program,
128 stack_inputs: StackInputs,
129 host: &mut impl Host,
130 options: ExecutionOptions,
131) -> Result<ExecutionTrace, ExecutionError> {
132 let mut process = Process::new(program.kernel().clone(), stack_inputs, options);
133 let stack_outputs = process.execute(program, host)?;
134 let trace = ExecutionTrace::new(process, stack_outputs);
135 assert_eq!(&program.hash(), trace.program_hash(), "inconsistent program hash");
136 Ok(trace)
137}
138
139pub fn execute_iter(
142 program: &Program,
143 stack_inputs: StackInputs,
144 host: &mut impl Host,
145) -> VmStateIterator {
146 let mut process = Process::new_debug(program.kernel().clone(), stack_inputs);
147 let result = process.execute(program, host);
148 if result.is_ok() {
149 assert_eq!(
150 program.hash(),
151 process.decoder.program_hash().into(),
152 "inconsistent program hash"
153 );
154 }
155 VmStateIterator::new(process, result)
156}
157
158#[cfg(not(any(test, feature = "testing")))]
171pub struct Process {
172 system: System,
173 decoder: Decoder,
174 stack: Stack,
175 range: RangeChecker,
176 chiplets: Chiplets,
177 max_cycles: u32,
178 enable_tracing: bool,
179}
180
181#[cfg(any(test, feature = "testing"))]
182pub struct Process {
183 pub system: System,
184 pub decoder: Decoder,
185 pub stack: Stack,
186 pub range: RangeChecker,
187 pub chiplets: Chiplets,
188 pub max_cycles: u32,
189 pub enable_tracing: bool,
190}
191
192impl Process {
193 pub fn new(
197 kernel: Kernel,
198 stack_inputs: StackInputs,
199 execution_options: ExecutionOptions,
200 ) -> Self {
201 Self::initialize(kernel, stack_inputs, execution_options)
202 }
203
204 pub fn new_debug(kernel: Kernel, stack_inputs: StackInputs) -> Self {
206 Self::initialize(
207 kernel,
208 stack_inputs,
209 ExecutionOptions::default().with_tracing().with_debugging(),
210 )
211 }
212
213 fn initialize(kernel: Kernel, stack: StackInputs, execution_options: ExecutionOptions) -> Self {
214 let in_debug_mode = execution_options.enable_debugging();
215 Self {
216 system: System::new(execution_options.expected_cycles() as usize),
217 decoder: Decoder::new(in_debug_mode),
218 stack: Stack::new(&stack, execution_options.expected_cycles() as usize, in_debug_mode),
219 range: RangeChecker::new(),
220 chiplets: Chiplets::new(kernel),
221 max_cycles: execution_options.max_cycles(),
222 enable_tracing: execution_options.enable_tracing(),
223 }
224 }
225
226 pub fn execute(
231 &mut self,
232 program: &Program,
233 host: &mut impl Host,
234 ) -> Result<StackOutputs, ExecutionError> {
235 if self.system.clk() != 0 {
236 return Err(ExecutionError::ProgramAlreadyExecuted);
237 }
238
239 for (digest, values) in program.mast_forest().advice_map().iter() {
241 if let Some(stored_values) = host.advice_provider().get_mapped_values(digest) {
242 if stored_values != values {
243 return Err(ExecutionError::AdviceMapKeyAlreadyPresent(digest.into()));
244 }
245 } else {
246 host.advice_provider_mut().insert_into_map(digest.into(), values.clone());
247 }
248 }
249
250 self.execute_mast_node(program.entrypoint(), &program.mast_forest().clone(), host)?;
251
252 self.stack.build_stack_outputs()
253 }
254
255 fn execute_mast_node(
259 &mut self,
260 node_id: MastNodeId,
261 program: &MastForest,
262 host: &mut impl Host,
263 ) -> Result<(), ExecutionError> {
264 let node = program
265 .get_node_by_id(node_id)
266 .ok_or(ExecutionError::MastNodeNotFoundInForest { node_id })?;
267
268 for &decorator_id in node.before_enter() {
269 self.execute_decorator(&program[decorator_id], host)?;
270 }
271
272 match node {
273 MastNode::Block(node) => self.execute_basic_block_node(node, program, host)?,
274 MastNode::Join(node) => self.execute_join_node(node, program, host)?,
275 MastNode::Split(node) => self.execute_split_node(node, program, host)?,
276 MastNode::Loop(node) => self.execute_loop_node(node, program, host)?,
277 MastNode::Call(node) => self.execute_call_node(node, program, host)?,
278 MastNode::Dyn(node) => self.execute_dyn_node(node, program, host)?,
279 MastNode::External(external_node) => {
280 let node_digest = external_node.digest();
281 let mast_forest = host.get_mast_forest(&node_digest).ok_or(
282 ExecutionError::NoMastForestWithProcedure { root_digest: node_digest },
283 )?;
284
285 let root_id = mast_forest.find_procedure_root(node_digest).ok_or(
288 ExecutionError::MalformedMastForestInHost { root_digest: node_digest },
289 )?;
290
291 if mast_forest[root_id].is_external() {
294 return Err(ExecutionError::CircularExternalNode(node_digest));
295 }
296
297 self.execute_mast_node(root_id, &mast_forest, host)?;
298 },
299 }
300
301 for &decorator_id in node.after_exit() {
302 self.execute_decorator(&program[decorator_id], host)?;
303 }
304
305 Ok(())
306 }
307
308 #[inline(always)]
310 fn execute_join_node(
311 &mut self,
312 node: &JoinNode,
313 program: &MastForest,
314 host: &mut impl Host,
315 ) -> Result<(), ExecutionError> {
316 self.start_join_node(node, program, host)?;
317
318 self.execute_mast_node(node.first(), program, host)?;
320 self.execute_mast_node(node.second(), program, host)?;
321
322 self.end_join_node(node, host)
323 }
324
325 #[inline(always)]
327 fn execute_split_node(
328 &mut self,
329 node: &SplitNode,
330 program: &MastForest,
331 host: &mut impl Host,
332 ) -> Result<(), ExecutionError> {
333 let condition = self.start_split_node(node, program, host)?;
335
336 if condition == ONE {
338 self.execute_mast_node(node.on_true(), program, host)?;
339 } else if condition == ZERO {
340 self.execute_mast_node(node.on_false(), program, host)?;
341 } else {
342 return Err(ExecutionError::NotBinaryValue(condition));
343 }
344
345 self.end_split_node(node, host)
346 }
347
348 #[inline(always)]
350 fn execute_loop_node(
351 &mut self,
352 node: &LoopNode,
353 program: &MastForest,
354 host: &mut impl Host,
355 ) -> Result<(), ExecutionError> {
356 let condition = self.start_loop_node(node, program, host)?;
358
359 if condition == ONE {
361 self.execute_mast_node(node.body(), program, host)?;
363
364 while self.stack.peek() == ONE {
368 self.decoder.repeat();
369 self.execute_op(Operation::Drop, host)?;
370 self.execute_mast_node(node.body(), program, host)?;
371 }
372
373 if self.stack.peek() != ZERO {
374 return Err(ExecutionError::NotBinaryValue(self.stack.peek()));
375 }
376
377 self.end_loop_node(node, true, host)
379 } else if condition == ZERO {
380 self.end_loop_node(node, false, host)
383 } else {
384 Err(ExecutionError::NotBinaryValue(condition))
385 }
386 }
387
388 #[inline(always)]
390 fn execute_call_node(
391 &mut self,
392 call_node: &CallNode,
393 program: &MastForest,
394 host: &mut impl Host,
395 ) -> Result<(), ExecutionError> {
396 if self.system.in_syscall() {
398 let instruction = if call_node.is_syscall() { "syscall" } else { "call" };
399 return Err(ExecutionError::CallInSyscall(instruction));
400 }
401
402 if call_node.is_syscall() {
404 let callee = program.get_node_by_id(call_node.callee()).ok_or_else(|| {
405 ExecutionError::MastNodeNotFoundInForest { node_id: call_node.callee() }
406 })?;
407 self.chiplets.kernel_rom.access_proc(callee.digest())?;
408 }
409
410 self.start_call_node(call_node, program, host)?;
411 self.execute_mast_node(call_node.callee(), program, host)?;
412 self.end_call_node(call_node, host)
413 }
414
415 #[inline(always)]
420 fn execute_dyn_node(
421 &mut self,
422 node: &DynNode,
423 program: &MastForest,
424 host: &mut impl Host,
425 ) -> Result<(), ExecutionError> {
426 if node.is_dyncall() && self.system.in_syscall() {
428 return Err(ExecutionError::CallInSyscall("dyncall"));
429 }
430
431 let callee_hash = if node.is_dyncall() {
432 self.start_dyncall_node(node)?
433 } else {
434 self.start_dyn_node(node, host)?
435 };
436
437 match program.find_procedure_root(callee_hash.into()) {
441 Some(callee_id) => self.execute_mast_node(callee_id, program, host)?,
442 None => {
443 let mast_forest = host
444 .get_mast_forest(&callee_hash.into())
445 .ok_or_else(|| ExecutionError::DynamicNodeNotFound(callee_hash.into()))?;
446
447 let root_id = mast_forest.find_procedure_root(callee_hash.into()).ok_or(
450 ExecutionError::MalformedMastForestInHost { root_digest: callee_hash.into() },
451 )?;
452
453 self.execute_mast_node(root_id, &mast_forest, host)?
454 },
455 }
456
457 if node.is_dyncall() {
458 self.end_dyncall_node(node, host)
459 } else {
460 self.end_dyn_node(node, host)
461 }
462 }
463
464 #[inline(always)]
466 fn execute_basic_block_node(
467 &mut self,
468 basic_block: &BasicBlockNode,
469 program: &MastForest,
470 host: &mut impl Host,
471 ) -> Result<(), ExecutionError> {
472 self.start_basic_block_node(basic_block, host)?;
473
474 let mut op_offset = 0;
475 let mut decorator_ids = basic_block.decorator_iter();
476
477 self.execute_op_batch(
479 &basic_block.op_batches()[0],
480 &mut decorator_ids,
481 op_offset,
482 program,
483 host,
484 )?;
485 op_offset += basic_block.op_batches()[0].ops().len();
486
487 for op_batch in basic_block.op_batches().iter().skip(1) {
491 self.respan(op_batch);
492 self.execute_op(Operation::Noop, host)?;
493 self.execute_op_batch(op_batch, &mut decorator_ids, op_offset, program, host)?;
494 op_offset += op_batch.ops().len();
495 }
496
497 self.end_basic_block_node(basic_block, host)?;
498
499 for &decorator_id in decorator_ids {
504 let decorator = program
505 .get_decorator_by_id(decorator_id)
506 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
507 self.execute_decorator(decorator, host)?;
508 }
509
510 Ok(())
511 }
512
513 #[inline(always)]
520 fn execute_op_batch(
521 &mut self,
522 batch: &OpBatch,
523 decorators: &mut DecoratorIterator,
524 op_offset: usize,
525 program: &MastForest,
526 host: &mut impl Host,
527 ) -> Result<(), ExecutionError> {
528 let op_counts = batch.op_counts();
529 let mut op_idx = 0;
530 let mut group_idx = 0;
531 let mut next_group_idx = 1;
532
533 let num_batch_groups = batch.num_groups().next_power_of_two();
537
538 for (i, &op) in batch.ops().iter().enumerate() {
540 while let Some(&decorator_id) = decorators.next_filtered(i + op_offset) {
541 let decorator = program
542 .get_decorator_by_id(decorator_id)
543 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
544 self.execute_decorator(decorator, host)?;
545 }
546
547 self.decoder.execute_user_op(op, op_idx);
549 self.execute_op(op, host)?;
550
551 let has_imm = op.imm_value().is_some();
554 if has_imm {
555 next_group_idx += 1;
556 }
557
558 if op_idx == op_counts[group_idx] - 1 {
560 if has_imm {
563 debug_assert!(op_idx < OP_GROUP_SIZE - 1, "invalid op index");
568 self.decoder.execute_user_op(Operation::Noop, op_idx + 1);
569 self.execute_op(Operation::Noop, host)?;
570 }
571
572 group_idx = next_group_idx;
574 next_group_idx += 1;
575 op_idx = 0;
576
577 if group_idx < num_batch_groups {
580 self.decoder.start_op_group(batch.groups()[group_idx]);
581 }
582 } else {
583 op_idx += 1;
585 }
586 }
587
588 for group_idx in group_idx..num_batch_groups {
591 self.decoder.execute_user_op(Operation::Noop, 0);
592 self.execute_op(Operation::Noop, host)?;
593
594 if group_idx < num_batch_groups - 1 {
598 self.decoder.start_op_group(ZERO);
599 }
600 }
601
602 Ok(())
603 }
604
605 fn execute_decorator(
607 &mut self,
608 decorator: &Decorator,
609 host: &mut impl Host,
610 ) -> Result<(), ExecutionError> {
611 match decorator {
612 Decorator::Debug(options) => {
613 if self.decoder.in_debug_mode() {
614 host.on_debug(self.into(), options)?;
615 }
616 },
617 Decorator::AsmOp(assembly_op) => {
618 if self.decoder.in_debug_mode() {
619 self.decoder.append_asmop(self.system.clk(), assembly_op.clone());
620 }
621 },
622 Decorator::Trace(id) => {
623 if self.enable_tracing {
624 host.on_trace(self.into(), *id)?;
625 }
626 },
627 };
628 Ok(())
629 }
630
631 pub const fn kernel(&self) -> &Kernel {
635 self.chiplets.kernel_rom.kernel()
636 }
637
638 pub fn into_parts(self) -> (System, Decoder, Stack, RangeChecker, Chiplets) {
639 (self.system, self.decoder, self.stack, self.range, self.chiplets)
640 }
641}
642
643#[derive(Debug, Clone, Copy)]
647pub struct ProcessState<'a> {
648 system: &'a System,
649 stack: &'a Stack,
650 chiplets: &'a Chiplets,
651}
652
653impl ProcessState<'_> {
654 pub fn clk(&self) -> RowIndex {
656 self.system.clk()
657 }
658
659 pub fn ctx(&self) -> ContextId {
661 self.system.ctx()
662 }
663
664 pub fn fmp(&self) -> u64 {
666 self.system.fmp().as_int()
667 }
668
669 pub fn get_stack_item(&self, pos: usize) -> Felt {
671 self.stack.get(pos)
672 }
673
674 pub fn get_stack_word(&self, word_idx: usize) -> Word {
685 self.stack.get_word(word_idx)
686 }
687
688 pub fn get_stack_state(&self) -> Vec<Felt> {
691 self.stack.get_state_at(self.system.clk())
692 }
693
694 pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
697 self.chiplets.memory.get_value(ctx, addr)
698 }
699
700 pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, ExecutionError> {
705 self.chiplets.memory.get_word(ctx, addr)
706 }
707
708 pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(u64, Felt)> {
714 self.chiplets.memory.get_state_at(ctx, self.system.clk())
715 }
716}
717
718impl<'a> From<&'a Process> for ProcessState<'a> {
719 fn from(process: &'a Process) -> Self {
720 Self {
721 system: &process.system,
722 stack: &process.stack,
723 chiplets: &process.chiplets,
724 }
725 }
726}
727
728impl<'a> From<&'a mut Process> for ProcessState<'a> {
729 fn from(process: &'a mut Process) -> Self {
730 Self {
731 system: &process.system,
732 stack: &process.stack,
733 chiplets: &process.chiplets,
734 }
735 }
736}