use alloc::sync::Arc;
use core::ops::ControlFlow;
use miden_air::{Felt, trace::RowIndex};
use miden_core::{
WORD_SIZE, Word, ZERO,
field::PrimeField64,
mast::{BasicBlockNode, MastForest, MastNodeId},
precompile::PrecompileTranscriptState,
program::{Kernel, MIN_STACK_DEPTH},
utils::range,
};
use super::super::trace_state::{
AdviceReplay, ExecutionContextReplay, HasherResponseReplay, MastForestResolutionReplay,
MemoryReadsReplay, StackOverflowReplay, StackState, SystemState,
};
use crate::{
BreakReason, ContextId, ExecutionError, Host, Stopper,
continuation_stack::{Continuation, ContinuationStack},
errors::OperationError,
execution::{
InternalBreakReason, execute_impl, finish_emit_op_execution,
finish_load_mast_forest_from_dyn_start, finish_load_mast_forest_from_external,
},
host::default::NoopHost,
processor::{Processor, StackInterface, SystemInterface},
tracer::Tracer,
};
#[derive(Debug)]
pub(crate) struct ReplayProcessor {
pub system: SystemState,
pub stack: StackState,
pub stack_overflow_replay: StackOverflowReplay,
pub execution_context_replay: ExecutionContextReplay,
pub advice_replay: AdviceReplay,
pub memory_reads_replay: MemoryReadsReplay,
pub hasher_response_replay: HasherResponseReplay,
pub mast_forest_resolution_replay: MastForestResolutionReplay,
pub maximum_clock: RowIndex,
}
impl ReplayProcessor {
pub fn new(
initial_system: SystemState,
initial_stack: StackState,
stack_overflow_replay: StackOverflowReplay,
execution_context_replay: ExecutionContextReplay,
advice_replay: AdviceReplay,
memory_reads_replay: MemoryReadsReplay,
hasher_response_replay: HasherResponseReplay,
mast_forest_resolution_replay: MastForestResolutionReplay,
num_clocks_to_execute: RowIndex,
) -> Self {
let maximum_clock = initial_system.clk + num_clocks_to_execute.as_usize();
Self {
system: initial_system,
stack: initial_stack,
stack_overflow_replay,
execution_context_replay,
advice_replay,
memory_reads_replay,
hasher_response_replay,
mast_forest_resolution_replay,
maximum_clock,
}
}
pub fn execute<T>(
&mut self,
continuation_stack: &mut ContinuationStack,
current_forest: &mut Arc<MastForest>,
kernel: &Kernel,
tracer: &mut T,
) -> Result<(), ExecutionError>
where
T: Tracer<Processor = Self>,
{
match self.execute_impl(continuation_stack, current_forest, kernel, tracer) {
ControlFlow::Continue(_) => {
Ok(())
},
ControlFlow::Break(break_reason) => match break_reason {
BreakReason::Err(err) => Err(err),
BreakReason::Stopped(_continuation) => {
Ok(())
},
},
}
}
fn execute_impl<T>(
&mut self,
continuation_stack: &mut ContinuationStack,
current_forest: &mut Arc<MastForest>,
kernel: &Kernel,
tracer: &mut T,
) -> ControlFlow<BreakReason>
where
T: Tracer<Processor = Self>,
{
let host = &mut NoopHost;
let stopper = &ReplayStopper;
while let ControlFlow::Break(internal_break_reason) =
execute_impl(self, continuation_stack, current_forest, kernel, host, tracer, stopper)
{
match internal_break_reason {
InternalBreakReason::User(break_reason) => return ControlFlow::Break(break_reason),
InternalBreakReason::Emit {
basic_block_node_id: _,
op_idx: _,
continuation,
} => {
finish_emit_op_execution(
continuation,
self,
continuation_stack,
current_forest,
tracer,
stopper,
)?;
},
InternalBreakReason::LoadMastForestFromDyn { .. } => {
let (root_id, new_forest) =
match self.mast_forest_resolution_replay.replay_resolution() {
Ok(v) => v,
Err(err) => {
return ControlFlow::Break(BreakReason::Err(err));
},
};
finish_load_mast_forest_from_dyn_start(
root_id,
new_forest,
self,
current_forest,
continuation_stack,
tracer,
stopper,
)?;
},
InternalBreakReason::LoadMastForestFromExternal {
external_node_id,
procedure_hash: _,
} => {
let (root_id, new_forest) =
match self.mast_forest_resolution_replay.replay_resolution() {
Ok(v) => v,
Err(err) => {
return ControlFlow::Break(BreakReason::Err(err));
},
};
finish_load_mast_forest_from_external(
root_id,
new_forest,
external_node_id,
current_forest,
continuation_stack,
host,
tracer,
)?;
},
}
}
ControlFlow::Continue(())
}
}
impl SystemInterface for ReplayProcessor {
fn caller_hash(&self) -> Word {
self.system.fn_hash
}
fn clock(&self) -> RowIndex {
self.system.clk
}
fn ctx(&self) -> ContextId {
self.system.ctx
}
fn set_caller_hash(&mut self, caller_hash: Word) {
self.system.fn_hash = caller_hash;
}
fn set_ctx(&mut self, ctx: ContextId) {
self.system.ctx = ctx;
}
fn increment_clock(&mut self) {
self.system.clk += 1_u32;
}
}
impl StackInterface for ReplayProcessor {
fn top(&self) -> &[Felt] {
&self.stack.stack_top
}
fn get(&self, idx: usize) -> Felt {
debug_assert!(idx < MIN_STACK_DEPTH);
self.stack.stack_top[MIN_STACK_DEPTH - idx - 1]
}
fn get_mut(&mut self, idx: usize) -> &mut Felt {
debug_assert!(idx < MIN_STACK_DEPTH);
&mut self.stack.stack_top[MIN_STACK_DEPTH - idx - 1]
}
fn get_word(&self, start_idx: usize) -> Word {
debug_assert!(start_idx < MIN_STACK_DEPTH - 4);
let word_start_idx = MIN_STACK_DEPTH - start_idx - 4;
let mut result: [Felt; WORD_SIZE] =
self.top()[range(word_start_idx, WORD_SIZE)].try_into().unwrap();
result.reverse();
result.into()
}
fn depth(&self) -> u32 {
(MIN_STACK_DEPTH + self.stack.num_overflow_elements_in_current_ctx()) as u32
}
fn set(&mut self, idx: usize, element: Felt) {
*self.get_mut(idx) = element;
}
fn set_word(&mut self, start_idx: usize, word: &Word) {
debug_assert!(start_idx < MIN_STACK_DEPTH - 4);
let word_start_idx = MIN_STACK_DEPTH - start_idx - 4;
let mut source: [Felt; WORD_SIZE] = (*word).into();
source.reverse();
let word_on_stack = &mut self.stack.stack_top[range(word_start_idx, WORD_SIZE)];
word_on_stack.copy_from_slice(&source);
}
fn swap(&mut self, idx1: usize, idx2: usize) {
let a = self.get(idx1);
let b = self.get(idx2);
self.set(idx1, b);
self.set(idx2, a);
}
fn swapw_nth(&mut self, n: usize) {
let (rest_of_stack, top_word) =
self.stack.stack_top.split_at_mut(MIN_STACK_DEPTH - WORD_SIZE);
let (_, nth_word) = rest_of_stack.split_at_mut(rest_of_stack.len() - n * WORD_SIZE);
nth_word[0..WORD_SIZE].swap_with_slice(&mut top_word[0..WORD_SIZE]);
}
fn rotate_left(&mut self, n: usize) {
let rotation_bot_index = MIN_STACK_DEPTH - n;
let new_stack_top_element = self.stack.stack_top[rotation_bot_index];
for i in 0..n - 1 {
self.stack.stack_top[rotation_bot_index + i] =
self.stack.stack_top[rotation_bot_index + i + 1];
}
self.set(0, new_stack_top_element);
}
fn rotate_right(&mut self, n: usize) {
let rotation_bot_index = MIN_STACK_DEPTH - n;
let new_stack_bot_element = self.stack.stack_top[MIN_STACK_DEPTH - 1];
for i in 1..n {
self.stack.stack_top[MIN_STACK_DEPTH - i] =
self.stack.stack_top[MIN_STACK_DEPTH - i - 1];
}
self.stack.stack_top[rotation_bot_index] = new_stack_bot_element;
}
fn increment_size(&mut self) -> Result<(), ExecutionError> {
const SENTINEL_VALUE: Felt = Felt::new(Felt::ORDER_U64 - 1);
{
let last_element = self.get(MIN_STACK_DEPTH - 1);
self.stack.push_overflow(last_element, self.clock());
}
for write_idx in (1..MIN_STACK_DEPTH).rev() {
let read_idx = write_idx - 1;
self.set(write_idx, self.get(read_idx));
}
self.set(0, SENTINEL_VALUE);
Ok(())
}
fn decrement_size(&mut self) -> Result<(), OperationError> {
for write_idx in 0..(MIN_STACK_DEPTH - 1) {
let read_idx = write_idx + 1;
self.set(write_idx, self.get(read_idx));
}
if let Some(last_element) = self.stack.pop_overflow(&mut self.stack_overflow_replay)? {
self.set(MIN_STACK_DEPTH - 1, last_element);
} else {
self.set(MIN_STACK_DEPTH - 1, ZERO);
}
Ok(())
}
}
impl Processor for ReplayProcessor {
type System = Self;
type Stack = Self;
type AdviceProvider = AdviceReplay;
type Memory = MemoryReadsReplay;
type Hasher = HasherResponseReplay;
fn stack(&self) -> &Self::Stack {
self
}
fn stack_mut(&mut self) -> &mut Self::Stack {
self
}
fn system(&self) -> &Self::System {
self
}
fn system_mut(&mut self) -> &mut Self::System {
self
}
fn advice_provider(&self) -> &Self::AdviceProvider {
&self.advice_replay
}
fn advice_provider_mut(&mut self) -> &mut Self::AdviceProvider {
&mut self.advice_replay
}
fn memory_mut(&mut self) -> &mut Self::Memory {
&mut self.memory_reads_replay
}
fn hasher(&mut self) -> &mut Self::Hasher {
&mut self.hasher_response_replay
}
fn save_context_and_truncate_stack(&mut self) {
self.stack.start_context();
}
fn restore_context(&mut self) -> Result<(), OperationError> {
let ctx_info = self.execution_context_replay.replay_execution_context()?;
self.system_mut().set_ctx(ctx_info.parent_ctx);
self.system_mut().set_caller_hash(ctx_info.parent_fn_hash);
self.stack.restore_context(&mut self.stack_overflow_replay)?;
Ok(())
}
fn precompile_transcript_state(&self) -> PrecompileTranscriptState {
self.system.pc_transcript_state
}
fn set_precompile_transcript_state(&mut self, state: PrecompileTranscriptState) {
self.system.pc_transcript_state = state;
}
fn execute_before_enter_decorators(
&self,
_node_id: MastNodeId,
_current_forest: &MastForest,
_host: &mut impl Host,
) -> ControlFlow<BreakReason> {
ControlFlow::Continue(())
}
fn execute_after_exit_decorators(
&self,
_node_id: MastNodeId,
_current_forest: &MastForest,
_host: &mut impl Host,
) -> ControlFlow<BreakReason> {
ControlFlow::Continue(())
}
fn execute_decorators_for_op(
&self,
_node_id: MastNodeId,
_op_idx_in_block: usize,
_current_forest: &MastForest,
_host: &mut impl Host,
) -> ControlFlow<BreakReason> {
ControlFlow::Continue(())
}
fn execute_end_of_block_decorators(
&self,
_basic_block_node: &BasicBlockNode,
_node_id: MastNodeId,
_current_forest: &Arc<MastForest>,
_host: &mut impl Host,
) -> ControlFlow<BreakReason> {
ControlFlow::Continue(())
}
}
#[derive(Debug)]
pub(crate) struct ReplayStopper;
impl Stopper for ReplayStopper {
type Processor = ReplayProcessor;
fn should_stop(
&self,
processor: &ReplayProcessor,
_continuation_stack: &ContinuationStack,
continuation_after_stop: impl FnOnce() -> Option<Continuation>,
) -> ControlFlow<BreakReason> {
if processor.system().clock() >= processor.maximum_clock {
ControlFlow::Break(BreakReason::Stopped(continuation_after_stop()))
} else {
ControlFlow::Continue(())
}
}
}