use alloc::sync::Arc;
use core::ops::ControlFlow;
use miden_core::{FMP_ADDR, FMP_INIT_VALUE};
use crate::{
BreakReason, ContextId, Host, MapExecErr, Stopper,
continuation_stack::{Continuation, ContinuationStack},
execution::{
ExecutionState, InternalBreakReason, finalize_clock_cycle,
finalize_clock_cycle_with_continuation, get_next_ctx_id,
},
mast::{MastForest, MastNodeId},
processor::{MemoryInterface, Processor, StackInterface, SystemInterface},
tracer::Tracer,
};
#[inline(always)]
pub(super) fn start_dyn_node<P, H, S, T>(
state: &mut ExecutionState<'_, P, H, S, T>,
current_node_id: MastNodeId,
current_forest: &mut Arc<MastForest>,
) -> ControlFlow<InternalBreakReason>
where
P: Processor,
H: Host,
S: Stopper<Processor = P>,
T: Tracer<Processor = P>,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::StartNode(current_node_id),
state.continuation_stack,
current_forest,
);
state
.processor
.execute_before_enter_decorators(current_node_id, current_forest, state.host)
.map_break(InternalBreakReason::from)?;
let dyn_node = current_forest[current_node_id].unwrap_dyn();
let read_ctx = state.processor.system().ctx();
let clk = state.processor.system().clock();
let mem_addr = state.processor.stack().get(0);
let callee_hash = match state
.processor
.memory_mut()
.read_word(read_ctx, mem_addr, clk)
.map_exec_err(current_forest, current_node_id, state.host)
{
Ok(w) => w,
Err(err) => {
return ControlFlow::Break(BreakReason::Err(err).into());
},
};
if let Err(err) = state.processor.stack_mut().decrement_size().map_exec_err(
current_forest,
current_node_id,
state.host,
) {
return ControlFlow::Break(InternalBreakReason::from(BreakReason::Err(err)));
}
if dyn_node.is_dyncall() {
let new_ctx: ContextId = get_next_ctx_id(state.processor);
state.processor.save_context_and_truncate_stack();
state.processor.system_mut().set_ctx(new_ctx);
state.processor.system_mut().set_caller_hash(callee_hash);
if let Err(err) = state
.processor
.memory_mut()
.write_element(new_ctx, FMP_ADDR, FMP_INIT_VALUE)
.map_exec_err(current_forest, current_node_id, state.host)
{
return ControlFlow::Break(BreakReason::Err(err).into());
}
state
.tracer
.record_dyncall_memory(callee_hash, mem_addr, read_ctx, new_ctx, clk);
} else {
state.tracer.record_memory_read_word(callee_hash, mem_addr, read_ctx, clk);
};
state.continuation_stack.push_finish_dyn(current_node_id);
match current_forest.find_procedure_root(callee_hash) {
Some(callee_id) => {
state.continuation_stack.push_start_node(callee_id);
},
None => {
return ControlFlow::Break(InternalBreakReason::LoadMastForestFromDyn {
dyn_node_id: current_node_id,
callee_hash,
});
},
}
finalize_clock_cycle(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
current_forest,
)
.map_break(InternalBreakReason::from)
}
pub fn finish_load_mast_forest_from_dyn_start<P, S, T>(
root_id: MastNodeId,
new_forest: Arc<MastForest>,
processor: &mut P,
current_forest: &mut Arc<MastForest>,
continuation_stack: &mut ContinuationStack,
tracer: &mut T,
stopper: &S,
) -> ControlFlow<BreakReason>
where
P: Processor,
S: Stopper<Processor = P>,
T: Tracer<Processor = P>,
{
let old_forest = Arc::clone(current_forest);
continuation_stack.push_enter_forest(Arc::clone(current_forest));
continuation_stack.push_start_node(root_id);
*current_forest = new_forest;
finalize_clock_cycle(processor, tracer, stopper, continuation_stack, &old_forest)?;
tracer.record_mast_forest_resolution(root_id, current_forest);
ControlFlow::Continue(())
}
#[inline(always)]
pub(super) fn finish_dyn_node<P, H, S, T>(
state: &mut ExecutionState<'_, P, H, S, T>,
node_id: MastNodeId,
current_forest: &Arc<MastForest>,
) -> ControlFlow<BreakReason>
where
P: Processor,
H: Host,
S: Stopper<Processor = P>,
T: Tracer<Processor = P>,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::FinishDyn(node_id),
state.continuation_stack,
current_forest,
);
let dyn_node = current_forest[node_id].unwrap_dyn();
if dyn_node.is_dyncall()
&& let Err(e) = state.processor.restore_context()
{
return ControlFlow::Break(BreakReason::Err(e.with_context(
current_forest,
node_id,
state.host,
)));
}
finalize_clock_cycle_with_continuation(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
|| Some(Continuation::AfterExitDecorators(node_id)),
current_forest,
)?;
state
.processor
.execute_after_exit_decorators(node_id, current_forest, state.host)
}