use alloc::sync::Arc;
use core::ops::ControlFlow;
use miden_core::{FMP_ADDR, FMP_INIT_VALUE};
use miden_mast_package::debug_info::{DebugSourceNodeId, PackageDebugInfo};
use crate::{
BaseHost, BreakReason, ContextId, MapExecErr, Stopper,
continuation_stack::{Continuation, ContinuationStack},
execution::{
ExecutionState, InternalBreakReason, finalize_clock_cycle,
finalize_clock_cycle_with_continuation, get_next_ctx_id,
},
mast::{ExecutableMastForest, MastNodeId},
option_map_break_reason,
processor::{MemoryInterface, Processor, StackInterface, SystemInterface},
tracer::Tracer,
};
#[inline(always)]
pub(super) fn start_dyn_node<P, H, S, T, F>(
state: &mut ExecutionState<'_, P, H, S, T, F>,
current_node_id: MastNodeId,
current_forest: &mut F,
) -> ControlFlow<InternalBreakReason<F>>
where
P: Processor,
H: BaseHost,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::StartNode(current_node_id),
state.continuation_stack,
current_forest,
);
let dyn_node = option_map_break_reason(
current_forest.get_node_by_id(current_node_id),
"dyn node not found in current forest",
)
.map_break(InternalBreakReason::from)?
.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() {
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() {
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.stack_mut().start_context();
state.processor.system_mut().save_call_state();
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()
{
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_with_source_node_id(
Continuation::FinishDyn(current_node_id),
state.current_source_node_id(),
);
match current_forest.find_procedure_root(callee_hash) {
Some(callee_id) => {
let source_node_id = match &state.source_debug_info {
Some(source_debug_info) => source_debug_info
.unique_source_root_for_exec_node(callee_id)
.unwrap_or_default(),
None => None,
};
state
.continuation_stack
.push_with_source_node_id(Continuation::StartNode(callee_id), source_node_id);
},
None => {
return ControlFlow::Break(InternalBreakReason::LoadMastForestFromDyn {
callee_hash,
source_node_id: state.current_source_node_id(),
});
},
}
finalize_clock_cycle(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
current_forest,
)
.map_break(InternalBreakReason::from)
}
#[inline(always)]
pub(super) fn start_dyn_node_pure<P, H, S, T, F>(
state: &mut ExecutionState<'_, P, H, S, T, F>,
current_node_id: MastNodeId,
current_forest: &mut F,
) -> ControlFlow<InternalBreakReason<F>>
where
P: Processor,
H: BaseHost,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::StartNode(current_node_id),
state.continuation_stack,
current_forest,
);
let dyn_node = option_map_break_reason(
current_forest.get_node_by_id(current_node_id),
"dyn node not found in current forest",
)
.map_break(InternalBreakReason::from)?
.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() {
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() {
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.stack_mut().start_context();
state.processor.system_mut().save_call_state();
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()
{
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 {
callee_hash,
source_node_id: None,
});
},
}
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, F>(
root_id: MastNodeId,
new_forest: F,
new_package_debug_info: Option<Arc<PackageDebugInfo>>,
new_source_node_id: Option<DebugSourceNodeId>,
processor: &mut P,
current_forest: &mut F,
current_package_debug_info: &mut Option<Arc<PackageDebugInfo>>,
continuation_stack: &mut ContinuationStack<F>,
tracer: &mut T,
stopper: &S,
) -> ControlFlow<BreakReason<F>>
where
P: Processor,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
let old_forest = current_forest.clone();
let old_package_debug_info = current_package_debug_info.clone();
continuation_stack
.push_enter_forest_with_package_debug_info(current_forest.clone(), old_package_debug_info);
continuation_stack
.push_with_source_node_id(Continuation::StartNode(root_id), new_source_node_id);
*current_forest = new_forest;
*current_package_debug_info = new_package_debug_info;
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, F>(
state: &mut ExecutionState<'_, P, H, S, T, F>,
node_id: MastNodeId,
current_forest: &F,
) -> ControlFlow<BreakReason<F>>
where
P: Processor,
H: BaseHost,
S: Stopper<Processor = P, Forest = F>,
T: Tracer<Processor = P, Forest = F>,
F: ExecutableMastForest + Clone,
{
state.tracer.start_clock_cycle(
state.processor,
Continuation::FinishDyn(node_id),
state.continuation_stack,
current_forest,
);
let dyn_node = option_map_break_reason(
current_forest.get_node_by_id(node_id),
"dyn node not found in current forest",
)?
.unwrap_dyn();
if dyn_node.is_dyncall() {
if let Err(e) = state.processor.stack_mut().restore_context() {
return ControlFlow::Break(BreakReason::Err(
state.operation_error_with_current_context(e),
));
}
if let Err(e) = state.processor.system_mut().restore_call_state() {
return ControlFlow::Break(BreakReason::Err(
state.operation_error_with_current_context(e),
));
}
}
finalize_clock_cycle_with_continuation(
state.processor,
state.tracer,
state.stopper,
state.continuation_stack,
|| None,
current_forest,
)
}