use alloc::sync::Arc;
use core::ops::ControlFlow;
use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
use crate::{
BaseHost, BreakReason, ContextId, ExecutionError, Kernel, Stopper, Word,
continuation_stack::{Continuation, ContinuationStack},
errors::PackageSourceDebugContext,
mast::{ExecutableMastForest, MastNode, MastNodeId},
operation::OperationError,
processor::{Processor, SystemInterface},
tracer::{OperationHelperRegisters, Tracer},
};
mod basic_block;
mod call;
mod r#dyn;
mod external;
mod join;
mod r#loop;
mod operations;
mod split;
pub(crate) use basic_block::finish_emit_op_execution;
pub(crate) use r#dyn::finish_load_mast_forest_from_dyn_start;
pub(crate) use external::finish_load_mast_forest_from_external;
#[cfg(test)]
pub(crate) use operations::eval_circuit_impl;
use operations::execute_op;
pub(crate) struct ExecutionState<'a, P, H, S, T, F> {
pub processor: &'a mut P,
pub continuation_stack: &'a mut ContinuationStack<F>,
pub kernel: &'a Kernel,
pub host: &'a mut H,
pub tracer: &'a mut T,
pub stopper: &'a S,
pub source_debug_info: Option<Arc<PackageDebugInfo>>,
pub current_source_node_id: Option<DebugSourceNodeId>,
}
impl<'a, P, H, S, T, F> ExecutionState<'a, P, H, S, T, F> {
pub fn current_source_node_id(&self) -> Option<DebugSourceNodeId> {
self.current_source_node_id
}
pub fn child_source_node_id(
&self,
child_index: usize,
) -> Result<Option<DebugSourceNodeId>, ExecutionError> {
let Some(source_debug_info) = &self.source_debug_info else {
return Ok(None);
};
let Some(current_source_node_id) = self.current_source_node_id else {
return Ok(None);
};
Ok(source_debug_info
.child_source_node(current_source_node_id, child_index)
.map_err(|_| {
ExecutionError::Internal(
"package debug source graph has malformed child references",
)
})?
.map(|(source_node_id, _)| source_node_id))
}
pub fn package_source_context(&self) -> Option<PackageSourceDebugContext<'_>> {
Some(PackageSourceDebugContext::new_optional(
self.source_debug_info.as_deref()?,
self.current_source_node_id,
))
}
pub fn operation_error_with_current_context(&self, err: OperationError) -> ExecutionError
where
H: BaseHost,
{
match self.package_source_context() {
Some(context) => err.with_package_source_context(context, self.host, None),
None => err.with_context(),
}
}
}
pub(crate) fn execute_impl<P, S, T, F>(
processor: &mut P,
continuation_stack: &mut ContinuationStack<F>,
current_forest: &mut F,
kernel: &Kernel,
host: &mut impl BaseHost,
tracer: &mut T,
stopper: &S,
source_debug_info: &mut Option<Arc<PackageDebugInfo>>,
) -> ControlFlow<InternalBreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
if source_debug_info.is_none() && !continuation_stack.tracks_source_nodes() {
return execute_impl_pure(
processor,
continuation_stack,
current_forest,
kernel,
host,
tracer,
stopper,
);
};
let mut state = ExecutionState {
processor,
continuation_stack,
kernel,
host,
tracer,
stopper,
source_debug_info: source_debug_info.clone(),
current_source_node_id: None,
};
while let Some((continuation, source_node_id)) =
state.continuation_stack.pop_continuation_with_source_node_id()
{
state.current_source_node_id = source_node_id;
match continuation {
Continuation::StartNode(node_id) => {
let node = current_forest.get_node_by_id(node_id).unwrap();
match node {
MastNode::Block(basic_block_node) => {
basic_block::execute_basic_block_node_from_start(
&mut state,
basic_block_node,
node_id,
current_forest,
)?
},
MastNode::Join(join_node) => {
join::start_join_node(&mut state, join_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Split(split_node) => {
split::start_split_node(&mut state, split_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Loop(loop_node) => {
r#loop::start_loop_node(&mut state, loop_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Call(call_node) => {
call::start_call_node(&mut state, call_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Dyn(_) => r#dyn::start_dyn_node(&mut state, node_id, current_forest)?,
MastNode::External(_) => external::execute_external_node(
node_id,
state.current_source_node_id(),
current_forest,
state.tracer,
)?,
}
},
Continuation::FinishJoin(node_id) => {
join::finish_join_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishSplit(node_id) => {
split::finish_split_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishLoop(node_id) => {
r#loop::finish_loop_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishCall(node_id) => {
call::finish_call_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishDyn(node_id) => {
r#dyn::finish_dyn_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
let basic_block_node =
current_forest.get_node_by_id(node_id).unwrap().unwrap_basic_block();
basic_block::execute_basic_block_node_from_op_idx(
&mut state,
basic_block_node,
node_id,
batch_index,
op_idx_in_batch,
current_forest,
)?
},
Continuation::Respan { node_id, batch_index } => {
let basic_block_node =
current_forest.get_node_by_id(node_id).unwrap().unwrap_basic_block();
basic_block::execute_basic_block_node_from_batch(
&mut state,
basic_block_node,
node_id,
batch_index,
current_forest,
)?
},
Continuation::FinishBasicBlock(node_id) => {
basic_block::finish_basic_block(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::EnterForest { forest, package_debug_info } => {
*current_forest = forest;
state.source_debug_info = package_debug_info.clone();
*source_debug_info = package_debug_info;
},
}
}
ControlFlow::Continue(())
}
#[inline(always)]
fn execute_impl_pure<P, S, T, F>(
processor: &mut P,
continuation_stack: &mut ContinuationStack<F>,
current_forest: &mut F,
kernel: &Kernel,
host: &mut impl BaseHost,
tracer: &mut T,
stopper: &S,
) -> ControlFlow<InternalBreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
let mut state = ExecutionState {
processor,
continuation_stack,
kernel,
host,
tracer,
stopper,
source_debug_info: None,
current_source_node_id: None,
};
while let Some(continuation) = state.continuation_stack.pop_continuation() {
match continuation {
Continuation::StartNode(node_id) => {
let node = current_forest.get_node_by_id(node_id).unwrap();
match node {
MastNode::Block(basic_block_node) => {
basic_block::execute_basic_block_node_from_start(
&mut state,
basic_block_node,
node_id,
current_forest,
)?
},
MastNode::Join(join_node) => {
join::start_join_node_pure(&mut state, join_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Split(split_node) => split::start_split_node_pure(
&mut state,
split_node,
node_id,
current_forest,
)
.map_break(InternalBreakReason::from)?,
MastNode::Loop(loop_node) => {
r#loop::start_loop_node_pure(&mut state, loop_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Call(call_node) => {
call::start_call_node_pure(&mut state, call_node, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
MastNode::Dyn(_) => {
r#dyn::start_dyn_node_pure(&mut state, node_id, current_forest)?
},
MastNode::External(_) => external::execute_external_node(
node_id,
None,
current_forest,
state.tracer,
)?,
}
},
Continuation::FinishJoin(node_id) => {
join::finish_join_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishSplit(node_id) => {
split::finish_split_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishLoop(node_id) => {
r#loop::finish_loop_node_pure(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishCall(node_id) => {
call::finish_call_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::FinishDyn(node_id) => {
r#dyn::finish_dyn_node(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::ResumeBasicBlock { node_id, batch_index, op_idx_in_batch } => {
let basic_block_node =
current_forest.get_node_by_id(node_id).unwrap().unwrap_basic_block();
basic_block::execute_basic_block_node_from_op_idx(
&mut state,
basic_block_node,
node_id,
batch_index,
op_idx_in_batch,
current_forest,
)?
},
Continuation::Respan { node_id, batch_index } => {
let basic_block_node =
current_forest.get_node_by_id(node_id).unwrap().unwrap_basic_block();
basic_block::execute_basic_block_node_from_batch(
&mut state,
basic_block_node,
node_id,
batch_index,
current_forest,
)?
},
Continuation::FinishBasicBlock(node_id) => {
basic_block::finish_basic_block(&mut state, node_id, current_forest)
.map_break(InternalBreakReason::from)?
},
Continuation::EnterForest { forest, .. } => {
*current_forest = forest;
},
}
}
ControlFlow::Continue(())
}
pub enum InternalBreakReason<F> {
User(BreakReason<F>),
Emit {
op_idx: usize,
continuation: Continuation<F>,
source_node_id: Option<DebugSourceNodeId>,
},
LoadMastForestFromDyn {
callee_hash: Word,
source_node_id: Option<DebugSourceNodeId>,
},
LoadMastForestFromExternal {
external_node_id: MastNodeId,
procedure_hash: Word,
source_node_id: Option<DebugSourceNodeId>,
},
}
impl<F> From<BreakReason<F>> for InternalBreakReason<F> {
fn from(reason: BreakReason<F>) -> Self {
Self::User(reason)
}
}
#[inline(always)]
fn finalize_clock_cycle<P, S, T, F>(
processor: &mut P,
tracer: &mut T,
stopper: &S,
continuation_stack: &ContinuationStack<F>,
current_forest: &F,
) -> ControlFlow<BreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
finalize_clock_cycle_with_continuation(
processor,
tracer,
stopper,
continuation_stack,
|| None,
current_forest,
)
}
#[inline(always)]
fn finalize_clock_cycle_with_continuation<P, S, T, F>(
processor: &mut P,
tracer: &mut T,
stopper: &S,
continuation_stack: &ContinuationStack<F>,
continuation_after_stop: impl FnOnce() -> Option<(Continuation<F>, Option<DebugSourceNodeId>)>,
current_forest: &F,
) -> ControlFlow<BreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
finalize_clock_cycle_with_continuation_and_op_helpers(
processor,
tracer,
stopper,
continuation_stack,
continuation_after_stop,
OperationHelperRegisters::Empty,
current_forest,
)
}
#[inline(always)]
fn finalize_clock_cycle_with_continuation_and_op_helpers<P, S, T, F>(
processor: &mut P,
tracer: &mut T,
stopper: &S,
continuation_stack: &ContinuationStack<F>,
continuation_after_stop: impl FnOnce() -> Option<(Continuation<F>, Option<DebugSourceNodeId>)>,
op_helper_registers: OperationHelperRegisters,
current_forest: &F,
) -> ControlFlow<BreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
tracer.finalize_clock_cycle(processor, op_helper_registers, current_forest);
processor.system_mut().increment_clock();
stopper.should_stop(processor, continuation_stack, continuation_after_stop)
}
fn get_next_ctx_id(processor: &impl Processor) -> ContextId {
(processor.system().clock() + 1).into()
}