miden_debug/exec/
state.rs1use std::collections::{BTreeSet, VecDeque};
2
3use miden_core::{
4 mast::{MastNode, MastNodeId},
5 operations::AssemblyOp,
6};
7use miden_processor::{
8 ContextId, Continuation, ExecutionError, FastProcessor, Felt, ResumeContext, StackOutputs,
9 operation::Operation, trace::RowIndex,
10};
11
12use super::{DebuggerHost, ExecutionTrace};
13use crate::debug::{CallFrame, CallStack, StepInfo};
14
15fn poll_immediately<T>(fut: impl std::future::Future<Output = T>) -> T {
22 let waker = std::task::Waker::noop();
23 let mut cx = std::task::Context::from_waker(waker);
24 let mut fut = std::pin::pin!(fut);
25 match fut.as_mut().poll(&mut cx) {
26 std::task::Poll::Ready(val) => val,
27 std::task::Poll::Pending => panic!("future was expected to complete immediately"),
28 }
29}
30
31pub struct DebugExecutor {
38 pub processor: FastProcessor,
40 pub host: DebuggerHost<dyn miden_assembly::SourceManager>,
42 pub resume_ctx: Option<ResumeContext>,
44
45 pub current_stack: Vec<Felt>,
48 pub current_op: Option<Operation>,
50 pub current_asmop: Option<AssemblyOp>,
52
53 pub stack_outputs: StackOutputs,
55 pub contexts: BTreeSet<ContextId>,
57 pub root_context: ContextId,
59 pub current_context: ContextId,
61 pub callstack: CallStack,
63 pub recent: VecDeque<Operation>,
65 pub cycle: usize,
67 pub stopped: bool,
69}
70
71fn extract_current_op(
74 ctx: &ResumeContext,
75) -> (Option<Operation>, Option<MastNodeId>, Option<usize>) {
76 let forest = ctx.current_forest();
77 for cont in ctx.continuation_stack().iter_continuations_for_next_clock() {
78 match cont {
79 Continuation::ResumeBasicBlock {
80 node_id,
81 batch_index,
82 op_idx_in_batch,
83 } => {
84 let node = &forest[*node_id];
85 if let MastNode::Block(block) = node {
86 let mut global_idx = 0;
88 for batch in &block.op_batches()[..*batch_index] {
89 global_idx += batch.ops().len();
90 }
91 global_idx += op_idx_in_batch;
92 let op = block.op_batches()[*batch_index].ops().get(*op_idx_in_batch).copied();
93 return (op, Some(*node_id), Some(global_idx));
94 }
95 }
96 Continuation::StartNode(node_id) => {
97 return (None, Some(*node_id), None);
98 }
99 Continuation::FinishBasicBlock(_) => return (None, None, None),
100 other if other.increments_clk() => {
101 return (None, None, None);
102 }
103 _ => continue,
104 }
105 }
106 (None, None, None)
107}
108
109impl DebugExecutor {
110 pub fn step(&mut self) -> Result<Option<CallFrame>, ExecutionError> {
117 if self.stopped {
118 return Ok(None);
119 }
120
121 let resume_ctx = match self.resume_ctx.take() {
122 Some(ctx) => ctx,
123 None => {
124 self.stopped = true;
125 return Ok(None);
126 }
127 };
128
129 let (op, node_id, op_idx) = extract_current_op(&resume_ctx);
131 let asmop = node_id
132 .and_then(|nid| resume_ctx.current_forest().get_assembly_op(nid, op_idx).cloned());
133
134 match poll_immediately(self.processor.step(&mut self.host, resume_ctx)) {
136 Ok(Some(new_ctx)) => {
137 self.resume_ctx = Some(new_ctx);
138 self.cycle += 1;
139
140 let state = self.processor.state();
142 let ctx = state.ctx();
143 self.current_stack = state.get_stack_state();
144
145 if self.current_context != ctx {
146 self.contexts.insert(ctx);
147 self.current_context = ctx;
148 }
149
150 self.current_op = op;
152 self.current_asmop = asmop.clone();
153
154 if let Some(op) = op {
155 if self.recent.len() == 5 {
156 self.recent.pop_front();
157 }
158 self.recent.push_back(op);
159 }
160
161 let step_info = StepInfo {
163 op,
164 asmop: self.current_asmop.as_ref(),
165 clk: RowIndex::from(self.cycle as u32),
166 ctx: self.current_context,
167 };
168 let exited = self.callstack.next(&step_info);
169
170 Ok(exited)
171 }
172 Ok(None) => {
173 self.stopped = true;
175 let state = self.processor.state();
176 self.current_stack = state.get_stack_state();
177
178 let len = self.current_stack.len().min(16);
180 self.stack_outputs =
181 StackOutputs::new(&self.current_stack[..len]).expect("invalid stack outputs");
182 Ok(None)
183 }
184 Err(err) => {
185 self.stopped = true;
186 Err(err)
187 }
188 }
189 }
190
191 pub fn into_execution_trace(self) -> ExecutionTrace {
193 ExecutionTrace {
194 root_context: self.root_context,
195 last_cycle: RowIndex::from(self.cycle as u32),
196 processor: self.processor,
197 outputs: self.stack_outputs,
198 }
199 }
200}