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 chiplets::hasher::Digest,
18 crypto::merkle::SMT_DEPTH,
19 errors::InputError,
20 mast::{MastForest, MastNode, MastNodeId},
21 sys_events::SystemEvent,
22 utils::{collections::KvMap, DeserializationError},
23 AssemblyOp, Felt, Kernel, Operation, Program, ProgramInfo, QuadExtension, StackInputs,
24 StackOutputs, Word, EMPTY_WORD, ONE, ZERO,
25};
26use vm_core::{
27 mast::{
28 BasicBlockNode, CallNode, DynNode, JoinNode, LoopNode, OpBatch, SplitNode, OP_GROUP_SIZE,
29 },
30 Decorator, DecoratorIterator, FieldElement,
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 advice::{AdviceInputs, AdviceProvider, AdviceSource, MemAdviceProvider, RecAdviceProvider},
52 DefaultHost, Host, MastForestStore, MemMastForestStore,
53};
54
55mod chiplets;
56use chiplets::Chiplets;
57
58mod trace;
59use trace::TraceFragment;
60pub use trace::{ChipletsLengths, ExecutionTrace, TraceLenSummary, NUM_RAND_ROWS};
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 self.end_loop_node(node, true, host)
375 } else if condition == ZERO {
376 self.end_loop_node(node, false, host)
379 } else {
380 Err(ExecutionError::NotBinaryValue(condition))
381 }
382 }
383
384 #[inline(always)]
386 fn execute_call_node(
387 &mut self,
388 call_node: &CallNode,
389 program: &MastForest,
390 host: &mut impl Host,
391 ) -> Result<(), ExecutionError> {
392 if call_node.is_syscall() {
394 let callee = program.get_node_by_id(call_node.callee()).ok_or_else(|| {
395 ExecutionError::MastNodeNotFoundInForest { node_id: call_node.callee() }
396 })?;
397 self.chiplets.access_kernel_proc(callee.digest())?;
398 }
399
400 self.start_call_node(call_node, program, host)?;
401 self.execute_mast_node(call_node.callee(), program, host)?;
402 self.end_call_node(call_node, host)
403 }
404
405 #[inline(always)]
410 fn execute_dyn_node(
411 &mut self,
412 node: &DynNode,
413 program: &MastForest,
414 host: &mut impl Host,
415 ) -> Result<(), ExecutionError> {
416 let callee_hash = if node.is_dyncall() {
417 self.start_dyncall_node(node)?
418 } else {
419 self.start_dyn_node(node, host)?
420 };
421
422 match program.find_procedure_root(callee_hash.into()) {
426 Some(callee_id) => self.execute_mast_node(callee_id, program, host)?,
427 None => {
428 let mast_forest = host
429 .get_mast_forest(&callee_hash.into())
430 .ok_or_else(|| ExecutionError::DynamicNodeNotFound(callee_hash.into()))?;
431
432 let root_id = mast_forest.find_procedure_root(callee_hash.into()).ok_or(
435 ExecutionError::MalformedMastForestInHost { root_digest: callee_hash.into() },
436 )?;
437
438 self.execute_mast_node(root_id, &mast_forest, host)?
439 },
440 }
441
442 if node.is_dyncall() {
443 self.end_dyncall_node(node, host)
444 } else {
445 self.end_dyn_node(node, host)
446 }
447 }
448
449 #[inline(always)]
451 fn execute_basic_block_node(
452 &mut self,
453 basic_block: &BasicBlockNode,
454 program: &MastForest,
455 host: &mut impl Host,
456 ) -> Result<(), ExecutionError> {
457 self.start_basic_block_node(basic_block, host)?;
458
459 let mut op_offset = 0;
460 let mut decorator_ids = basic_block.decorator_iter();
461
462 self.execute_op_batch(
464 &basic_block.op_batches()[0],
465 &mut decorator_ids,
466 op_offset,
467 program,
468 host,
469 )?;
470 op_offset += basic_block.op_batches()[0].ops().len();
471
472 for op_batch in basic_block.op_batches().iter().skip(1) {
476 self.respan(op_batch);
477 self.execute_op(Operation::Noop, host)?;
478 self.execute_op_batch(op_batch, &mut decorator_ids, op_offset, program, host)?;
479 op_offset += op_batch.ops().len();
480 }
481
482 self.end_basic_block_node(basic_block, host)?;
483
484 for &decorator_id in decorator_ids {
489 let decorator = program
490 .get_decorator_by_id(decorator_id)
491 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
492 self.execute_decorator(decorator, host)?;
493 }
494
495 Ok(())
496 }
497
498 #[inline(always)]
505 fn execute_op_batch(
506 &mut self,
507 batch: &OpBatch,
508 decorators: &mut DecoratorIterator,
509 op_offset: usize,
510 program: &MastForest,
511 host: &mut impl Host,
512 ) -> Result<(), ExecutionError> {
513 let op_counts = batch.op_counts();
514 let mut op_idx = 0;
515 let mut group_idx = 0;
516 let mut next_group_idx = 1;
517
518 let num_batch_groups = batch.num_groups().next_power_of_two();
522
523 for (i, &op) in batch.ops().iter().enumerate() {
525 while let Some(&decorator_id) = decorators.next_filtered(i + op_offset) {
526 let decorator = program
527 .get_decorator_by_id(decorator_id)
528 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
529 self.execute_decorator(decorator, host)?;
530 }
531
532 self.decoder.execute_user_op(op, op_idx);
534 self.execute_op(op, host)?;
535
536 let has_imm = op.imm_value().is_some();
539 if has_imm {
540 next_group_idx += 1;
541 }
542
543 if op_idx == op_counts[group_idx] - 1 {
545 if has_imm {
548 debug_assert!(op_idx < OP_GROUP_SIZE - 1, "invalid op index");
553 self.decoder.execute_user_op(Operation::Noop, op_idx + 1);
554 self.execute_op(Operation::Noop, host)?;
555 }
556
557 group_idx = next_group_idx;
559 next_group_idx += 1;
560 op_idx = 0;
561
562 if group_idx < num_batch_groups {
565 self.decoder.start_op_group(batch.groups()[group_idx]);
566 }
567 } else {
568 op_idx += 1;
570 }
571 }
572
573 for group_idx in group_idx..num_batch_groups {
576 self.decoder.execute_user_op(Operation::Noop, 0);
577 self.execute_op(Operation::Noop, host)?;
578
579 if group_idx < num_batch_groups - 1 {
583 self.decoder.start_op_group(ZERO);
584 }
585 }
586
587 Ok(())
588 }
589
590 fn execute_decorator(
592 &mut self,
593 decorator: &Decorator,
594 host: &mut impl Host,
595 ) -> Result<(), ExecutionError> {
596 match decorator {
597 Decorator::Debug(options) => {
598 if self.decoder.in_debug_mode() {
599 host.on_debug(self.into(), options)?;
600 }
601 },
602 Decorator::AsmOp(assembly_op) => {
603 if self.decoder.in_debug_mode() {
604 self.decoder.append_asmop(self.system.clk(), assembly_op.clone());
605 }
606 },
607 Decorator::Trace(id) => {
608 if self.enable_tracing {
609 host.on_trace(self.into(), *id)?;
610 }
611 },
612 };
613 Ok(())
614 }
615
616 pub const fn kernel(&self) -> &Kernel {
620 self.chiplets.kernel()
621 }
622
623 pub fn into_parts(self) -> (System, Decoder, Stack, RangeChecker, Chiplets) {
624 (self.system, self.decoder, self.stack, self.range, self.chiplets)
625 }
626}
627
628#[derive(Debug, Clone, Copy)]
632pub struct ProcessState<'a> {
633 system: &'a System,
634 stack: &'a Stack,
635 chiplets: &'a Chiplets,
636}
637
638impl ProcessState<'_> {
639 pub fn clk(&self) -> RowIndex {
641 self.system.clk()
642 }
643
644 pub fn ctx(&self) -> ContextId {
646 self.system.ctx()
647 }
648
649 pub fn fmp(&self) -> u64 {
651 self.system.fmp().as_int()
652 }
653
654 pub fn get_stack_item(&self, pos: usize) -> Felt {
656 self.stack.get(pos)
657 }
658
659 pub fn get_stack_word(&self, word_idx: usize) -> Word {
670 self.stack.get_word(word_idx)
671 }
672
673 pub fn get_stack_state(&self) -> Vec<Felt> {
676 self.stack.get_state_at(self.system.clk())
677 }
678
679 pub fn get_mem_value(&self, ctx: ContextId, addr: u32) -> Option<Felt> {
682 self.chiplets.memory().get_value(ctx, addr)
683 }
684
685 pub fn get_mem_word(&self, ctx: ContextId, addr: u32) -> Result<Option<Word>, ExecutionError> {
690 self.chiplets.memory().get_word(ctx, addr)
691 }
692
693 pub fn get_mem_state(&self, ctx: ContextId) -> Vec<(u64, Felt)> {
699 self.chiplets.memory().get_state_at(ctx, self.system.clk())
700 }
701}
702
703impl<'a> From<&'a Process> for ProcessState<'a> {
704 fn from(process: &'a Process) -> Self {
705 Self {
706 system: &process.system,
707 stack: &process.stack,
708 chiplets: &process.chiplets,
709 }
710 }
711}
712
713impl<'a> From<&'a mut Process> for ProcessState<'a> {
714 fn from(process: &'a mut Process) -> Self {
715 Self {
716 system: &process.system,
717 stack: &process.stack,
718 chiplets: &process.chiplets,
719 }
720 }
721}