1use alloc::{sync::Arc, vec::Vec};
2use core::cmp::min;
3
4use memory::Memory;
5use miden_air::RowIndex;
6use miden_core::{
7 Decorator, DecoratorIterator, EMPTY_WORD, Felt, ONE, Operation, Program, StackOutputs,
8 WORD_SIZE, Word, ZERO,
9 mast::{
10 BasicBlockNode, CallNode, ExternalNode, JoinNode, LoopNode, MastForest, MastNode,
11 MastNodeId, OP_GROUP_SIZE, OpBatch, SplitNode,
12 },
13 stack::MIN_STACK_DEPTH,
14 utils::range,
15};
16
17use crate::{
18 AdviceInputs, AdviceProvider, AsyncHost, ContextId, ErrorContext, ExecutionError, FMP_MIN,
19 ProcessState, SYSCALL_FMP_MIN,
20 chiplets::Ace,
21 continuation_stack::{Continuation, ContinuationStack},
22 err_ctx,
23};
24
25mod memory;
26
27mod circuit_eval;
29mod crypto_ops;
30mod field_ops;
31mod fri_ops;
32mod horner_ops;
33mod io_ops;
34mod stack_ops;
35mod sys_ops;
36mod u32_ops;
37
38#[cfg(test)]
39mod tests;
40
41const STACK_BUFFER_SIZE: usize = 6850;
50
51const INITIAL_STACK_TOP_IDX: usize = 250;
58
59const WORD_SIZE_FELT: Felt = Felt::new(4);
61
62const DOUBLE_WORD_SIZE: Felt = Felt::new(8);
64
65#[derive(Debug)]
97pub struct FastProcessor {
98 pub(super) stack: [Felt; STACK_BUFFER_SIZE],
100 stack_top_idx: usize,
102 stack_bot_idx: usize,
104
105 pub(super) clk: RowIndex,
107
108 pub(super) ctx: ContextId,
110
111 pub(super) fmp: Felt,
113
114 in_syscall: bool,
116
117 pub(super) caller_hash: Word,
120
121 pub(super) advice: AdviceProvider,
123
124 pub(super) memory: Memory,
126
127 pub(super) ace: Ace,
129
130 call_stack: Vec<ExecutionContextInfo>,
134
135 in_debug_mode: bool,
137}
138
139impl FastProcessor {
140 pub fn new(stack_inputs: &[Felt]) -> Self {
148 Self::initialize(stack_inputs, AdviceInputs::default(), false)
149 }
150
151 pub fn new_with_advice_inputs(stack_inputs: &[Felt], advice_inputs: AdviceInputs) -> Self {
156 Self::initialize(stack_inputs, advice_inputs, false)
157 }
158
159 pub fn new_debug(stack_inputs: &[Felt], advice_inputs: AdviceInputs) -> Self {
165 Self::initialize(stack_inputs, advice_inputs, true)
166 }
167
168 fn initialize(stack_inputs: &[Felt], advice_inputs: AdviceInputs, in_debug_mode: bool) -> Self {
174 assert!(stack_inputs.len() <= MIN_STACK_DEPTH);
175
176 let stack_top_idx = INITIAL_STACK_TOP_IDX;
177 let stack = {
178 let mut stack = [ZERO; STACK_BUFFER_SIZE];
179 let bottom_idx = stack_top_idx - stack_inputs.len();
180
181 stack[bottom_idx..stack_top_idx].copy_from_slice(stack_inputs);
182 stack
183 };
184
185 Self {
186 advice: advice_inputs.into(),
187 stack,
188 stack_top_idx,
189 stack_bot_idx: stack_top_idx - MIN_STACK_DEPTH,
190 clk: 0_u32.into(),
191 ctx: 0_u32.into(),
192 fmp: Felt::new(FMP_MIN),
193 in_syscall: false,
194 caller_hash: EMPTY_WORD,
195 memory: Memory::new(),
196 call_stack: Vec::new(),
197 ace: Ace::default(),
198 in_debug_mode,
199 }
200 }
201
202 pub fn stack(&self) -> &[Felt] {
208 &self.stack[self.stack_bot_idx..self.stack_top_idx]
209 }
210
211 #[inline(always)]
213 pub fn stack_get(&self, idx: usize) -> Felt {
214 self.stack[self.stack_top_idx - idx - 1]
215 }
216
217 #[inline(always)]
219 pub fn stack_get_mut(&mut self, idx: usize) -> &mut Felt {
220 &mut self.stack[self.stack_top_idx - idx - 1]
221 }
222
223 #[inline(always)]
239 pub fn stack_get_word(&self, start_idx: usize) -> Word {
240 debug_assert!(start_idx < MIN_STACK_DEPTH);
241
242 let word_start_idx = self.stack_top_idx - start_idx - 4;
243 let result: [Felt; WORD_SIZE] =
244 self.stack[range(word_start_idx, WORD_SIZE)].try_into().unwrap();
245 result.into()
246 }
247
248 #[inline(always)]
250 pub fn stack_depth(&self) -> u32 {
251 (self.stack_top_idx - self.stack_bot_idx) as u32
252 }
253
254 #[inline(always)]
259 pub fn stack_write(&mut self, idx: usize, element: Felt) {
260 self.stack[self.stack_top_idx - idx - 1] = element
261 }
262
263 #[inline(always)]
268 pub fn stack_write_word(&mut self, start_idx: usize, word: &Word) {
269 debug_assert!(start_idx < MIN_STACK_DEPTH);
270
271 let word_start_idx = self.stack_top_idx - start_idx - 4;
272 let source: [Felt; WORD_SIZE] = (*word).into();
273 self.stack[range(word_start_idx, WORD_SIZE)].copy_from_slice(&source)
274 }
275
276 #[inline(always)]
278 pub fn stack_swap(&mut self, idx1: usize, idx2: usize) {
279 let a = self.stack_get(idx1);
280 let b = self.stack_get(idx2);
281 self.stack_write(idx1, b);
282 self.stack_write(idx2, a);
283 }
284
285 pub async fn execute(
290 mut self,
291 program: &Program,
292 host: &mut impl AsyncHost,
293 ) -> Result<(StackOutputs, AdviceProvider), ExecutionError> {
294 let stack_outputs = self.execute_impl(program, host).await?;
295
296 Ok((stack_outputs, self.advice))
297 }
298
299 async fn execute_impl(
300 &mut self,
301 program: &Program,
302 host: &mut impl AsyncHost,
303 ) -> Result<StackOutputs, ExecutionError> {
304 let mut continuation_stack = ContinuationStack::new(program);
305 let mut current_forest = program.mast_forest().clone();
306
307 while let Some(processing_step) = continuation_stack.pop_continuation() {
308 match processing_step {
309 Continuation::StartNode(node_id) => {
310 let node = current_forest.get_node_by_id(node_id).unwrap();
311 match node {
312 MastNode::Block(basic_block_node) => {
313 self.execute_basic_block_node(
314 basic_block_node,
315 node_id,
316 ¤t_forest,
317 host,
318 )
319 .await?
320 },
321 MastNode::Join(join_node) => self.start_join_node(
322 join_node,
323 node_id,
324 ¤t_forest,
325 &mut continuation_stack,
326 host,
327 )?,
328 MastNode::Split(split_node) => self.start_split_node(
329 split_node,
330 node_id,
331 ¤t_forest,
332 &mut continuation_stack,
333 host,
334 )?,
335 MastNode::Loop(loop_node) => self.start_loop_node(
336 loop_node,
337 node_id,
338 ¤t_forest,
339 &mut continuation_stack,
340 host,
341 )?,
342 MastNode::Call(call_node) => self.start_call_node(
343 call_node,
344 node_id,
345 program,
346 ¤t_forest,
347 &mut continuation_stack,
348 host,
349 )?,
350 MastNode::Dyn(_dyn_node) => {
351 self.start_dyn_node(
352 node_id,
353 &mut current_forest,
354 &mut continuation_stack,
355 host,
356 )
357 .await?
358 },
359 MastNode::External(_external_node) => {
360 self.execute_external_node(
361 node_id,
362 &mut current_forest,
363 &mut continuation_stack,
364 host,
365 )
366 .await?
367 },
368 }
369 },
370 Continuation::FinishJoin(node_id) => {
371 self.finish_join_node(node_id, ¤t_forest, host)?
372 },
373 Continuation::FinishSplit(node_id) => {
374 self.finish_split_node(node_id, ¤t_forest, host)?
375 },
376 Continuation::FinishLoop(node_id) => {
377 self.finish_loop_node(node_id, ¤t_forest, &mut continuation_stack, host)?
378 },
379 Continuation::FinishCall(node_id) => {
380 self.finish_call_node(node_id, ¤t_forest, host)?
381 },
382 Continuation::FinishDyn(node_id) => {
383 self.finish_dyn_node(node_id, ¤t_forest, host)?
384 },
385 Continuation::EnterForest(previous_forest) => {
386 current_forest = previous_forest;
388 },
389 }
390 }
391
392 StackOutputs::new(
393 self.stack[self.stack_bot_idx..self.stack_top_idx]
394 .iter()
395 .rev()
396 .copied()
397 .collect(),
398 )
399 .map_err(|_| {
400 ExecutionError::OutputStackOverflow(
401 self.stack_top_idx - self.stack_bot_idx - MIN_STACK_DEPTH,
402 )
403 })
404 }
405
406 #[inline(always)]
411 fn start_join_node(
412 &mut self,
413 join_node: &JoinNode,
414 node_id: MastNodeId,
415 current_forest: &MastForest,
416 continuation_stack: &mut ContinuationStack,
417 host: &mut impl AsyncHost,
418 ) -> Result<(), ExecutionError> {
419 self.execute_before_enter_decorators(node_id, current_forest, host)?;
421
422 self.clk += 1_u32;
425
426 continuation_stack.push_finish_join(node_id);
427 continuation_stack.push_start_node(join_node.second());
428 continuation_stack.push_start_node(join_node.first());
429 Ok(())
430 }
431
432 #[inline(always)]
434 fn finish_join_node(
435 &mut self,
436 node_id: MastNodeId,
437 current_forest: &MastForest,
438 host: &mut impl AsyncHost,
439 ) -> Result<(), ExecutionError> {
440 self.clk += 1_u32;
443
444 self.execute_after_exit_decorators(node_id, current_forest, host)
445 }
446
447 #[inline(always)]
449 fn start_split_node(
450 &mut self,
451 split_node: &SplitNode,
452 node_id: MastNodeId,
453 current_forest: &MastForest,
454 continuation_stack: &mut ContinuationStack,
455 host: &mut impl AsyncHost,
456 ) -> Result<(), ExecutionError> {
457 self.execute_before_enter_decorators(node_id, current_forest, host)?;
459
460 self.clk += 1_u32;
463
464 let condition = self.stack_get(0);
465
466 self.decrement_stack_size();
468
469 continuation_stack.push_finish_split(node_id);
471 if condition == ONE {
472 continuation_stack.push_start_node(split_node.on_true());
473 } else if condition == ZERO {
474 continuation_stack.push_start_node(split_node.on_false());
475 } else {
476 let err_ctx = err_ctx!(current_forest, split_node, host);
477 return Err(ExecutionError::not_binary_value_if(condition, &err_ctx));
478 };
479 Ok(())
480 }
481
482 #[inline(always)]
484 fn finish_split_node(
485 &mut self,
486 node_id: MastNodeId,
487 current_forest: &MastForest,
488 host: &mut impl AsyncHost,
489 ) -> Result<(), ExecutionError> {
490 self.clk += 1_u32;
493
494 self.execute_after_exit_decorators(node_id, current_forest, host)
495 }
496
497 #[inline(always)]
499 fn start_loop_node(
500 &mut self,
501 loop_node: &LoopNode,
502 current_node_id: MastNodeId,
503 current_forest: &MastForest,
504 continuation_stack: &mut ContinuationStack,
505 host: &mut impl AsyncHost,
506 ) -> Result<(), ExecutionError> {
507 self.execute_before_enter_decorators(current_node_id, current_forest, host)?;
509
510 self.clk += 1_u32;
513
514 let condition = self.stack_get(0);
515
516 self.decrement_stack_size();
518
519 if condition == ONE {
521 continuation_stack.push_finish_loop(current_node_id);
524 continuation_stack.push_start_node(loop_node.body());
525 } else if condition == ZERO {
526 self.clk += 1_u32;
529 } else {
530 let err_ctx = err_ctx!(current_forest, loop_node, host);
531 return Err(ExecutionError::not_binary_value_loop(condition, &err_ctx));
532 }
533 Ok(())
534 }
535
536 #[inline(always)]
538 fn finish_loop_node(
539 &mut self,
540 current_node_id: MastNodeId,
541 current_forest: &MastForest,
542 continuation_stack: &mut ContinuationStack,
543 host: &mut impl AsyncHost,
544 ) -> Result<(), ExecutionError> {
545 let condition = self.stack_get(0);
548 self.decrement_stack_size();
549
550 let loop_node = current_forest[current_node_id].unwrap_loop();
551 if condition == ONE {
552 self.clk += 1_u32;
554 continuation_stack.push_finish_loop(current_node_id);
555 continuation_stack.push_start_node(loop_node.body());
556 } else if condition == ZERO {
557 self.clk += 1_u32;
559
560 self.execute_after_exit_decorators(current_node_id, current_forest, host)?;
561 } else {
562 let err_ctx = err_ctx!(current_forest, loop_node, host);
563 return Err(ExecutionError::not_binary_value_loop(condition, &err_ctx));
564 }
565 Ok(())
566 }
567
568 #[inline(always)]
570 fn start_call_node(
571 &mut self,
572 call_node: &CallNode,
573 current_node_id: MastNodeId,
574 program: &Program,
575 current_forest: &MastForest,
576 continuation_stack: &mut ContinuationStack,
577 host: &mut impl AsyncHost,
578 ) -> Result<(), ExecutionError> {
579 self.execute_before_enter_decorators(current_node_id, current_forest, host)?;
581
582 let err_ctx = err_ctx!(current_forest, call_node, host);
583
584 self.clk += 1_u32;
587
588 if self.in_syscall {
590 let instruction = if call_node.is_syscall() { "syscall" } else { "call" };
591 return Err(ExecutionError::CallInSyscall(instruction));
592 }
593
594 let callee_hash = current_forest
595 .get_node_by_id(call_node.callee())
596 .ok_or(ExecutionError::MastNodeNotFoundInForest { node_id: call_node.callee() })?
597 .digest();
598
599 self.save_context_and_truncate_stack();
600
601 if call_node.is_syscall() {
602 if !program.kernel().contains_proc(callee_hash) {
604 return Err(ExecutionError::syscall_target_not_in_kernel(callee_hash, &err_ctx));
605 }
606
607 self.ctx = ContextId::root();
609 self.fmp = SYSCALL_FMP_MIN.into();
610 self.in_syscall = true;
611 } else {
612 self.ctx = self.clk.into();
614 self.fmp = Felt::new(FMP_MIN);
615 self.caller_hash = callee_hash;
616 }
617
618 continuation_stack.push_finish_call(current_node_id);
620 continuation_stack.push_start_node(call_node.callee());
621 Ok(())
622 }
623
624 #[inline(always)]
626 fn finish_call_node(
627 &mut self,
628 node_id: MastNodeId,
629 current_forest: &MastForest,
630 host: &mut impl AsyncHost,
631 ) -> Result<(), ExecutionError> {
632 let call_node = current_forest[node_id].unwrap_call();
633 let err_ctx = err_ctx!(current_forest, call_node, host);
634 self.restore_context(&err_ctx)?;
639
640 self.clk += 1_u32;
643
644 self.execute_after_exit_decorators(node_id, current_forest, host)
645 }
646
647 #[inline(always)]
649 async fn start_dyn_node(
650 &mut self,
651 current_node_id: MastNodeId,
652 current_forest: &mut Arc<MastForest>,
653 continuation_stack: &mut ContinuationStack,
654 host: &mut impl AsyncHost,
655 ) -> Result<(), ExecutionError> {
656 self.execute_before_enter_decorators(current_node_id, current_forest, host)?;
658
659 self.clk += 1_u32;
662
663 let dyn_node = current_forest[current_node_id].unwrap_dyn();
664
665 if dyn_node.is_dyncall() && self.in_syscall {
667 return Err(ExecutionError::CallInSyscall("dyncall"));
668 }
669
670 let err_ctx = err_ctx!(¤t_forest, dyn_node, host);
671
672 let callee_hash = {
675 let mem_addr = self.stack_get(0);
676 self.memory
677 .read_word(self.ctx, mem_addr, self.clk, &err_ctx)
678 .map_err(ExecutionError::MemoryError)?
679 };
680
681 self.decrement_stack_size();
683
684 if dyn_node.is_dyncall() {
686 self.save_context_and_truncate_stack();
687 self.ctx = self.clk.into();
688 self.fmp = Felt::new(FMP_MIN);
689 self.caller_hash = callee_hash;
690 };
691
692 continuation_stack.push_finish_dyn(current_node_id);
695
696 match current_forest.find_procedure_root(callee_hash) {
700 Some(callee_id) => {
701 continuation_stack.push_start_node(callee_id);
702 },
703 None => {
704 let (root_id, new_forest) = self
705 .load_mast_forest(
706 callee_hash,
707 host,
708 ExecutionError::dynamic_node_not_found,
709 &err_ctx,
710 )
711 .await?;
712
713 continuation_stack.push_enter_forest(current_forest.clone());
715
716 continuation_stack.push_start_node(root_id);
718
719 *current_forest = new_forest;
721 },
722 }
723 Ok(())
724 }
725
726 #[inline(always)]
728 fn finish_dyn_node(
729 &mut self,
730 node_id: MastNodeId,
731 current_forest: &MastForest,
732 host: &mut impl AsyncHost,
733 ) -> Result<(), ExecutionError> {
734 let dyn_node = current_forest[node_id].unwrap_dyn();
735 let err_ctx = err_ctx!(current_forest, dyn_node, host);
736 if dyn_node.is_dyncall() {
738 self.restore_context(&err_ctx)?;
739 }
740
741 self.clk += 1_u32;
744
745 self.execute_after_exit_decorators(node_id, current_forest, host)
746 }
747
748 #[inline(always)]
750 async fn execute_external_node(
751 &mut self,
752 node_id: MastNodeId,
753 current_forest: &mut Arc<MastForest>,
754 continuation_stack: &mut ContinuationStack,
755 host: &mut impl AsyncHost,
756 ) -> Result<(), ExecutionError> {
757 self.execute_before_enter_decorators(node_id, current_forest, host)?;
759
760 let external_node = current_forest[node_id].unwrap_external();
761 let (root_id, new_mast_forest) = self.resolve_external_node(external_node, host).await?;
762
763 continuation_stack.push_enter_forest(current_forest.clone());
765
766 continuation_stack.push_start_node(root_id);
768
769 self.execute_after_exit_decorators(node_id, current_forest, host)?;
770
771 *current_forest = new_mast_forest;
773
774 Ok(())
775 }
776
777 #[inline(always)]
781 async fn execute_basic_block_node(
782 &mut self,
783 basic_block_node: &BasicBlockNode,
784 node_id: MastNodeId,
785 program: &MastForest,
786 host: &mut impl AsyncHost,
787 ) -> Result<(), ExecutionError> {
788 self.execute_before_enter_decorators(node_id, program, host)?;
790
791 self.clk += 1_u32;
793
794 let mut batch_offset_in_block = 0;
795 let mut op_batches = basic_block_node.op_batches().iter();
796 let mut decorator_ids = basic_block_node.decorator_iter();
797
798 if let Some(first_op_batch) = op_batches.next() {
800 self.execute_op_batch(
801 basic_block_node,
802 first_op_batch,
803 &mut decorator_ids,
804 batch_offset_in_block,
805 program,
806 host,
807 )
808 .await?;
809 batch_offset_in_block += first_op_batch.ops().len();
810 }
811
812 for op_batch in op_batches {
814 self.clk += 1_u32;
816
817 self.execute_op_batch(
818 basic_block_node,
819 op_batch,
820 &mut decorator_ids,
821 batch_offset_in_block,
822 program,
823 host,
824 )
825 .await?;
826 batch_offset_in_block += op_batch.ops().len();
827 }
828
829 self.clk += 1_u32;
831
832 for &decorator_id in decorator_ids {
837 let decorator = program
838 .get_decorator_by_id(decorator_id)
839 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
840 self.execute_decorator(decorator, host)?;
841 }
842
843 self.execute_after_exit_decorators(node_id, program, host)
844 }
845
846 #[inline(always)]
847 async fn execute_op_batch(
848 &mut self,
849 basic_block: &BasicBlockNode,
850 batch: &OpBatch,
851 decorators: &mut DecoratorIterator<'_>,
852 batch_offset_in_block: usize,
853 program: &MastForest,
854 host: &mut impl AsyncHost,
855 ) -> Result<(), ExecutionError> {
856 let op_counts = batch.op_counts();
857 let mut op_idx_in_group = 0;
858 let mut group_idx = 0;
859 let mut next_group_idx = 1;
860
861 let num_batch_groups = batch.num_groups().next_power_of_two();
865
866 for (op_idx_in_batch, op) in batch.ops().iter().enumerate() {
868 while let Some(&decorator_id) =
869 decorators.next_filtered(batch_offset_in_block + op_idx_in_batch)
870 {
871 let decorator = program
872 .get_decorator_by_id(decorator_id)
873 .ok_or(ExecutionError::DecoratorNotFoundInForest { decorator_id })?;
874 self.execute_decorator(decorator, host)?;
875 }
876
877 let op_idx_in_block = batch_offset_in_block + op_idx_in_batch;
879 let err_ctx = err_ctx!(program, basic_block, host, op_idx_in_block);
880
881 match op {
887 Operation::Emit(event_id) => self.op_emit(*event_id, host, &err_ctx).await?,
888 _ => {
889 self.execute_op(op, op_idx_in_block, program, host, &err_ctx)?;
891 },
892 }
893
894 let has_imm = op.imm_value().is_some();
897 if has_imm {
898 next_group_idx += 1;
899 }
900
901 if op_idx_in_group == op_counts[group_idx] - 1 {
903 if has_imm {
906 debug_assert!(op_idx_in_group < OP_GROUP_SIZE - 1, "invalid op index");
910 self.clk += 1_u32;
911 }
912
913 group_idx = next_group_idx;
915 next_group_idx += 1;
916 op_idx_in_group = 0;
917 } else {
918 op_idx_in_group += 1;
919 }
920
921 self.clk += 1_u32;
922 }
923
924 self.clk += (num_batch_groups - group_idx) as u32;
930
931 Ok(())
932 }
933
934 fn execute_before_enter_decorators(
936 &mut self,
937 node_id: MastNodeId,
938 current_forest: &MastForest,
939 host: &mut impl AsyncHost,
940 ) -> Result<(), ExecutionError> {
941 let node = current_forest
942 .get_node_by_id(node_id)
943 .expect("internal error: node id {node_id} not found in current forest");
944
945 for &decorator_id in node.before_enter() {
946 self.execute_decorator(¤t_forest[decorator_id], host)?;
947 }
948
949 Ok(())
950 }
951
952 fn execute_after_exit_decorators(
954 &mut self,
955 node_id: MastNodeId,
956 current_forest: &MastForest,
957 host: &mut impl AsyncHost,
958 ) -> Result<(), ExecutionError> {
959 let node = current_forest
960 .get_node_by_id(node_id)
961 .expect("internal error: node id {node_id} not found in current forest");
962
963 for &decorator_id in node.after_exit() {
964 self.execute_decorator(¤t_forest[decorator_id], host)?;
965 }
966
967 Ok(())
968 }
969
970 fn execute_decorator(
972 &mut self,
973 decorator: &Decorator,
974 host: &mut impl AsyncHost,
975 ) -> Result<(), ExecutionError> {
976 match decorator {
977 Decorator::Debug(options) => {
978 if self.in_debug_mode {
979 let process = &mut self.state();
980 host.on_debug(process, options)?;
981 }
982 },
983 Decorator::AsmOp(_assembly_op) => {
984 },
986 Decorator::Trace(id) => {
987 let process = &mut self.state();
988 host.on_trace(process, *id)?;
989 },
990 };
991 Ok(())
992 }
993
994 #[inline(always)]
1000 fn execute_op(
1001 &mut self,
1002 operation: &Operation,
1003 op_idx: usize,
1004 program: &MastForest,
1005 host: &mut impl AsyncHost,
1006 err_ctx: &impl ErrorContext,
1007 ) -> Result<(), ExecutionError> {
1008 match operation {
1009 Operation::Noop => {
1011 },
1013 Operation::Assert(err_code) => self.op_assert(*err_code, host, program, err_ctx)?,
1014 Operation::FmpAdd => self.op_fmpadd(),
1015 Operation::FmpUpdate => self.op_fmpupdate()?,
1016 Operation::SDepth => self.op_sdepth()?,
1017 Operation::Caller => self.op_caller()?,
1018 Operation::Clk => self.op_clk()?,
1019 Operation::Emit(_event_id) => {
1020 panic!("emit instruction requires async, so is not supported by execute_op()")
1021 },
1022
1023 Operation::Join => unreachable!("control flow operation"),
1026 Operation::Split => unreachable!("control flow operation"),
1027 Operation::Loop => unreachable!("control flow operation"),
1028 Operation::Call => unreachable!("control flow operation"),
1029 Operation::SysCall => unreachable!("control flow operation"),
1030 Operation::Dyn => unreachable!("control flow operation"),
1031 Operation::Dyncall => unreachable!("control flow operation"),
1032 Operation::Span => unreachable!("control flow operation"),
1033 Operation::Repeat => unreachable!("control flow operation"),
1034 Operation::Respan => unreachable!("control flow operation"),
1035 Operation::End => unreachable!("control flow operation"),
1036 Operation::Halt => unreachable!("control flow operation"),
1037
1038 Operation::Add => self.op_add()?,
1040 Operation::Neg => self.op_neg()?,
1041 Operation::Mul => self.op_mul()?,
1042 Operation::Inv => self.op_inv(err_ctx)?,
1043 Operation::Incr => self.op_incr()?,
1044 Operation::And => self.op_and(err_ctx)?,
1045 Operation::Or => self.op_or(err_ctx)?,
1046 Operation::Not => self.op_not(err_ctx)?,
1047 Operation::Eq => self.op_eq()?,
1048 Operation::Eqz => self.op_eqz()?,
1049 Operation::Expacc => self.op_expacc(),
1050 Operation::Ext2Mul => self.op_ext2mul(),
1051
1052 Operation::U32split => self.op_u32split()?,
1054 Operation::U32add => self.op_u32add(err_ctx)?,
1055 Operation::U32add3 => self.op_u32add3(err_ctx)?,
1056 Operation::U32sub => self.op_u32sub(op_idx, err_ctx)?,
1057 Operation::U32mul => self.op_u32mul(err_ctx)?,
1058 Operation::U32madd => self.op_u32madd(err_ctx)?,
1059 Operation::U32div => self.op_u32div(err_ctx)?,
1060 Operation::U32and => self.op_u32and(err_ctx)?,
1061 Operation::U32xor => self.op_u32xor(err_ctx)?,
1062 Operation::U32assert2(err_code) => self.op_u32assert2(*err_code, err_ctx)?,
1063
1064 Operation::Pad => self.op_pad()?,
1066 Operation::Drop => self.decrement_stack_size(),
1067 Operation::Dup0 => self.dup_nth(0)?,
1068 Operation::Dup1 => self.dup_nth(1)?,
1069 Operation::Dup2 => self.dup_nth(2)?,
1070 Operation::Dup3 => self.dup_nth(3)?,
1071 Operation::Dup4 => self.dup_nth(4)?,
1072 Operation::Dup5 => self.dup_nth(5)?,
1073 Operation::Dup6 => self.dup_nth(6)?,
1074 Operation::Dup7 => self.dup_nth(7)?,
1075 Operation::Dup9 => self.dup_nth(9)?,
1076 Operation::Dup11 => self.dup_nth(11)?,
1077 Operation::Dup13 => self.dup_nth(13)?,
1078 Operation::Dup15 => self.dup_nth(15)?,
1079 Operation::Swap => self.op_swap(),
1080 Operation::SwapW => self.swapw_nth(1),
1081 Operation::SwapW2 => self.swapw_nth(2),
1082 Operation::SwapW3 => self.swapw_nth(3),
1083 Operation::SwapDW => self.op_swap_double_word(),
1084 Operation::MovUp2 => self.rotate_left(3),
1085 Operation::MovUp3 => self.rotate_left(4),
1086 Operation::MovUp4 => self.rotate_left(5),
1087 Operation::MovUp5 => self.rotate_left(6),
1088 Operation::MovUp6 => self.rotate_left(7),
1089 Operation::MovUp7 => self.rotate_left(8),
1090 Operation::MovUp8 => self.rotate_left(9),
1091 Operation::MovDn2 => self.rotate_right(3),
1092 Operation::MovDn3 => self.rotate_right(4),
1093 Operation::MovDn4 => self.rotate_right(5),
1094 Operation::MovDn5 => self.rotate_right(6),
1095 Operation::MovDn6 => self.rotate_right(7),
1096 Operation::MovDn7 => self.rotate_right(8),
1097 Operation::MovDn8 => self.rotate_right(9),
1098 Operation::CSwap => self.op_cswap(err_ctx)?,
1099 Operation::CSwapW => self.op_cswapw(err_ctx)?,
1100
1101 Operation::Push(element) => self.op_push(*element)?,
1103 Operation::AdvPop => self.op_advpop(err_ctx)?,
1104 Operation::AdvPopW => self.op_advpopw(err_ctx)?,
1105 Operation::MLoadW => self.op_mloadw(err_ctx)?,
1106 Operation::MStoreW => self.op_mstorew(err_ctx)?,
1107 Operation::MLoad => self.op_mload(err_ctx)?,
1108 Operation::MStore => self.op_mstore(err_ctx)?,
1109 Operation::MStream => self.op_mstream(err_ctx)?,
1110 Operation::Pipe => self.op_pipe(err_ctx)?,
1111
1112 Operation::HPerm => self.op_hperm(),
1114 Operation::MpVerify(err_code) => self.op_mpverify(*err_code, program, err_ctx)?,
1115 Operation::MrUpdate => self.op_mrupdate(err_ctx)?,
1116 Operation::FriE2F4 => self.op_fri_ext2fold4()?,
1117 Operation::HornerBase => self.op_horner_eval_base(err_ctx)?,
1118 Operation::HornerExt => self.op_horner_eval_ext(err_ctx)?,
1119 Operation::EvalCircuit => self.op_eval_circuit(err_ctx)?,
1120 }
1121
1122 Ok(())
1123 }
1124
1125 async fn load_mast_forest<E>(
1129 &mut self,
1130 node_digest: Word,
1131 host: &mut impl AsyncHost,
1132 get_mast_forest_failed: impl Fn(Word, &E) -> ExecutionError,
1133 err_ctx: &E,
1134 ) -> Result<(MastNodeId, Arc<MastForest>), ExecutionError>
1135 where
1136 E: ErrorContext,
1137 {
1138 let mast_forest = host
1139 .get_mast_forest(&node_digest)
1140 .ok_or_else(|| get_mast_forest_failed(node_digest, err_ctx))?;
1141
1142 let root_id = mast_forest
1145 .find_procedure_root(node_digest)
1146 .ok_or(ExecutionError::malfored_mast_forest_in_host(node_digest, err_ctx))?;
1147
1148 self.advice
1154 .extend_map(mast_forest.advice_map())
1155 .map_err(|err| ExecutionError::advice_error(err, self.clk, err_ctx))?;
1156
1157 Ok((root_id, mast_forest))
1158 }
1159
1160 async fn resolve_external_node(
1163 &mut self,
1164 external_node: &ExternalNode,
1165 host: &mut impl AsyncHost,
1166 ) -> Result<(MastNodeId, Arc<MastForest>), ExecutionError> {
1167 let (root_id, mast_forest) = self
1168 .load_mast_forest(
1169 external_node.digest(),
1170 host,
1171 ExecutionError::no_mast_forest_with_procedure,
1172 &(),
1173 )
1174 .await?;
1175
1176 if mast_forest[root_id].is_external() {
1179 return Err(ExecutionError::CircularExternalNode(external_node.digest()));
1180 }
1181
1182 Ok((root_id, mast_forest))
1183 }
1184
1185 #[inline(always)]
1189 fn increment_stack_size(&mut self) -> Result<(), ExecutionError> {
1190 if self.stack_top_idx < STACK_BUFFER_SIZE - 1 {
1191 self.stack_top_idx += 1;
1192 Ok(())
1193 } else {
1194 Err(ExecutionError::FailedToExecuteProgram("stack overflow"))
1195 }
1196 }
1197
1198 #[inline(always)]
1203 fn decrement_stack_size(&mut self) {
1204 if self.stack_top_idx == MIN_STACK_DEPTH {
1205 self.reset_stack_in_buffer(INITIAL_STACK_TOP_IDX);
1209 }
1210
1211 self.stack_top_idx -= 1;
1212 self.stack_bot_idx = min(self.stack_bot_idx, self.stack_top_idx - MIN_STACK_DEPTH);
1213 }
1214
1215 #[inline(always)]
1217 fn stack_size(&self) -> usize {
1218 self.stack_top_idx - self.stack_bot_idx
1219 }
1220
1221 fn save_context_and_truncate_stack(&mut self) {
1224 let overflow_stack = if self.stack_size() > MIN_STACK_DEPTH {
1225 let overflow_stack =
1230 self.stack[self.stack_bot_idx..self.stack_top_idx - MIN_STACK_DEPTH].to_vec();
1231 self.stack[self.stack_bot_idx..self.stack_top_idx - MIN_STACK_DEPTH].fill(ZERO);
1232
1233 overflow_stack
1234 } else {
1235 Vec::new()
1236 };
1237
1238 self.stack_bot_idx = self.stack_top_idx - MIN_STACK_DEPTH;
1239
1240 self.call_stack.push(ExecutionContextInfo {
1241 overflow_stack,
1242 ctx: self.ctx,
1243 fn_hash: self.caller_hash,
1244 fmp: self.fmp,
1245 });
1246 }
1247
1248 fn restore_context(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
1257 if self.stack_size() > MIN_STACK_DEPTH {
1259 return Err(ExecutionError::invalid_stack_depth_on_return(self.stack_size(), err_ctx));
1260 }
1261
1262 let ctx_info = self
1263 .call_stack
1264 .pop()
1265 .expect("execution context stack should never be empty when restoring context");
1266
1267 self.restore_overflow_stack(&ctx_info);
1269
1270 self.ctx = ctx_info.ctx;
1272 self.fmp = ctx_info.fmp;
1273 self.in_syscall = false;
1274 self.caller_hash = ctx_info.fn_hash;
1275
1276 Ok(())
1277 }
1278
1279 #[inline(always)]
1287 fn restore_overflow_stack(&mut self, ctx_info: &ExecutionContextInfo) {
1288 let target_overflow_len = ctx_info.overflow_stack.len();
1289
1290 if target_overflow_len > self.stack_bot_idx {
1292 let new_stack_top_idx =
1297 core::cmp::min(INITIAL_STACK_TOP_IDX + target_overflow_len, STACK_BUFFER_SIZE - 1);
1298
1299 self.reset_stack_in_buffer(new_stack_top_idx);
1300 }
1301
1302 self.stack[range(self.stack_bot_idx - target_overflow_len, target_overflow_len)]
1304 .copy_from_slice(&ctx_info.overflow_stack);
1305 self.stack_bot_idx -= target_overflow_len;
1306 }
1307
1308 fn reset_stack_in_buffer(&mut self, new_stack_top_idx: usize) {
1314 debug_assert_eq!(self.stack_depth(), MIN_STACK_DEPTH as u32);
1315
1316 let new_stack_bot_idx = new_stack_top_idx - MIN_STACK_DEPTH;
1317
1318 self.stack
1320 .copy_within(self.stack_bot_idx..self.stack_top_idx, new_stack_bot_idx);
1321
1322 self.stack[0..new_stack_bot_idx].fill(ZERO);
1326
1327 self.stack_bot_idx = new_stack_bot_idx;
1329 self.stack_top_idx = new_stack_top_idx;
1330 }
1331
1332 #[cfg(any(test, feature = "testing"))]
1337 pub fn execute_sync(
1338 self,
1339 program: &Program,
1340 host: &mut impl AsyncHost,
1341 ) -> Result<StackOutputs, ExecutionError> {
1342 let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
1344
1345 let (stack_outputs, _advice) = rt.block_on(self.execute(program, host))?;
1346
1347 Ok(stack_outputs)
1348 }
1349
1350 #[cfg(any(test, feature = "testing"))]
1352 pub fn execute_sync_mut(
1353 &mut self,
1354 program: &Program,
1355 host: &mut impl AsyncHost,
1356 ) -> Result<StackOutputs, ExecutionError> {
1357 let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();
1359
1360 rt.block_on(self.execute_impl(program, host))
1361 }
1362}
1363
1364#[derive(Debug)]
1365pub struct FastProcessState<'a> {
1366 pub(super) processor: &'a mut FastProcessor,
1367}
1368
1369impl FastProcessor {
1370 #[inline(always)]
1371 pub fn state(&mut self) -> ProcessState<'_> {
1372 ProcessState::Fast(FastProcessState { processor: self })
1373 }
1374}
1375
1376#[derive(Debug)]
1384struct ExecutionContextInfo {
1385 overflow_stack: Vec<Felt>,
1388 ctx: ContextId,
1389 fn_hash: Word,
1390 fmp: Felt,
1391}