1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::fmt;
7
8use miden_air::RowIndex;
9use vm_core::{AssemblyOp, FieldElement, Operation, StackOutputs};
10
11use crate::{
12 Chiplets, ChipletsLengths, Decoder, ExecutionError, Felt, MemoryAddress, Process, Stack,
13 System, TraceLenSummary, range::RangeChecker, system::ContextId,
14};
15
16#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct VmState {
19 pub clk: RowIndex,
20 pub ctx: ContextId,
21 pub op: Option<Operation>,
22 pub asmop: Option<AsmOpInfo>,
23 pub fmp: Felt,
24 pub stack: Vec<Felt>,
25 pub memory: Vec<(MemoryAddress, Felt)>,
26}
27
28impl fmt::Display for VmState {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 let stack: Vec<u64> = self.stack.iter().map(|x| x.as_int()).collect();
31 write!(
32 f,
33 "clk={}{}{}, fmp={}, stack={stack:?}, memory={:?}",
34 self.clk,
35 match self.op {
36 Some(op) => format!(", op={op}"),
37 None => "".to_string(),
38 },
39 match &self.asmop {
40 Some(op) => format!(", {op}"),
41 None => "".to_string(),
42 },
43 self.fmp,
44 self.memory
45 )
46 }
47}
48
49pub struct VmStateIterator {
54 chiplets: Chiplets,
55 decoder: Decoder,
56 stack: Stack,
57 system: System,
58 error: Option<ExecutionError>,
59 clk: RowIndex,
60 asmop_idx: usize,
61 forward: bool,
62 trace_len_summary: TraceLenSummary,
63}
64
65impl VmStateIterator {
66 pub fn new(process: Process, result: Result<StackOutputs, ExecutionError>) -> Self {
67 let (system, decoder, stack, mut range, chiplets) = process.into_parts();
68 let trace_len_summary = Self::build_trace_len_summary(&system, &mut range, &chiplets);
69
70 Self {
71 chiplets,
72 decoder,
73 stack,
74 system,
75 error: result.err(),
76 clk: RowIndex::from(0),
77 asmop_idx: 0,
78 forward: true,
79 trace_len_summary,
80 }
81 }
82
83 fn get_asmop(&self) -> (Option<AsmOpInfo>, bool) {
86 let assembly_ops = self.decoder.debug_info().assembly_ops();
87
88 if self.clk == 0 || assembly_ops.is_empty() || self.asmop_idx > assembly_ops.len() {
89 return (None, false);
90 }
91
92 let next_asmop = if self.forward && self.asmop_idx < assembly_ops.len() {
95 &assembly_ops[self.asmop_idx]
96 } else {
97 &assembly_ops[self.asmop_idx.saturating_sub(1)]
98 };
99
100 let (curr_asmop, cycle_idx) = if self.asmop_idx > 0 {
103 let a = self.clk;
104 let b = RowIndex::from(assembly_ops[self.asmop_idx - 1].0);
105 (
106 &assembly_ops[self.asmop_idx - 1],
107 (a.max(b) - a.min(b)) as u8,
110 )
111 } else {
112 (next_asmop, 0) };
114
115 if next_asmop.0 == (self.clk - 1).as_usize() {
118 let cycle_idx = 1;
120 let asmop = AsmOpInfo::new(next_asmop.1.clone(), cycle_idx);
121 (Some(asmop), true)
122 }
123 else if self.asmop_idx > 0 && cycle_idx <= curr_asmop.1.num_cycles() {
127 let asmop = AsmOpInfo::new(curr_asmop.1.clone(), cycle_idx);
129 (Some(asmop), false)
130 }
131 else {
134 (None, false)
135 }
136 }
137
138 pub fn back(&mut self) -> Option<VmState> {
139 if self.clk == 0 {
140 return None;
141 }
142
143 if self.forward {
145 self.clk = self.clk.saturating_sub(1);
146 self.forward = false;
147 }
148
149 let ctx = self.system.get_ctx_at(self.clk);
150
151 let op = if self.clk == 0 {
152 None
153 } else {
154 Some(self.decoder.debug_info().operations()[self.clk - 1])
155 };
156
157 let (asmop, is_start) = self.get_asmop();
158 if is_start {
159 self.asmop_idx -= 1;
160 }
161
162 let result = Some(VmState {
163 clk: self.clk,
164 ctx,
165 op,
166 asmop,
167 fmp: self.system.get_fmp_at(self.clk),
168 stack: self.stack.get_state_at(self.clk),
169 memory: self.chiplets.memory.get_state_at(ctx, self.clk),
170 });
171
172 self.clk -= 1;
173
174 result
175 }
176
177 pub fn into_parts(self) -> (System, Decoder, Stack, Chiplets, Option<ExecutionError>) {
178 (self.system, self.decoder, self.stack, self.chiplets, self.error)
179 }
180
181 pub fn trace_len_summary(&self) -> &TraceLenSummary {
182 &self.trace_len_summary
183 }
184
185 fn build_trace_len_summary(
187 system: &System,
188 range: &mut RangeChecker,
189 chiplets: &Chiplets,
190 ) -> TraceLenSummary {
191 let clk = system.clk();
192 let range_table_len = range.get_number_range_checker_rows();
193 chiplets.append_range_checks(range);
194
195 TraceLenSummary::new(clk.into(), range_table_len, ChipletsLengths::new(chiplets))
196 }
197}
198
199impl Iterator for VmStateIterator {
200 type Item = Result<VmState, ExecutionError>;
201
202 fn next(&mut self) -> Option<Self::Item> {
203 if self.clk > self.system.clk() {
204 match &self.error {
205 Some(_) => {
206 let error = core::mem::take(&mut self.error);
207 return Some(Err(error.unwrap()));
208 },
209 None => return None,
210 }
211 }
212
213 if !self.forward && self.clk < self.system.clk() {
215 self.clk += 1_u32;
216 self.forward = true;
217 }
218
219 let ctx = self.system.get_ctx_at(self.clk);
220
221 let op = if self.clk == 0 {
222 None
223 } else {
224 Some(self.decoder.debug_info().operations()[self.clk - 1])
225 };
226
227 let (asmop, is_start) = self.get_asmop();
228 if is_start {
229 self.asmop_idx += 1;
230 }
231
232 let result = Some(Ok(VmState {
233 clk: self.clk,
234 ctx,
235 op,
236 asmop,
237 fmp: self.system.get_fmp_at(self.clk),
238 stack: self.stack.get_state_at(self.clk),
239 memory: self.chiplets.memory.get_state_at(ctx, self.clk),
240 }));
241
242 self.clk += 1_u32;
243
244 result
245 }
246}
247
248#[derive(Clone, Debug, Eq, PartialEq)]
251pub struct AsmOpInfo {
252 asmop: AssemblyOp,
253 cycle_idx: u8,
254}
255
256impl AsmOpInfo {
260 pub fn new(asmop: AssemblyOp, cycle_idx: u8) -> Self {
264 Self { asmop, cycle_idx }
265 }
266
267 pub fn context_name(&self) -> &str {
269 self.asmop.context_name()
270 }
271
272 pub fn op(&self) -> &str {
274 self.asmop.op()
275 }
276
277 pub fn op_generalized(&self) -> String {
279 let op_vec: Vec<&str> = self.op().split('.').collect();
280 let keep_params = matches!(op_vec[0], "movdn" | "movup");
281 if !keep_params && op_vec.last().unwrap().parse::<usize>().is_ok() {
282 op_vec.split_last().unwrap().1.join(".")
283 } else {
284 self.op().to_string()
285 }
286 }
287
288 pub fn num_cycles(&self) -> u8 {
290 self.asmop.num_cycles()
291 }
292
293 pub fn cycle_idx(&self) -> u8 {
296 self.cycle_idx
297 }
298
299 pub const fn should_break(&self) -> bool {
301 self.asmop.should_break()
302 }
303}
304
305impl fmt::Display for AsmOpInfo {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 write!(f, "{}, cycles={}", self.asmop, self.cycle_idx)
308 }
309}
310
311impl AsRef<AssemblyOp> for AsmOpInfo {
312 #[inline]
313 fn as_ref(&self) -> &AssemblyOp {
314 &self.asmop
315 }
316}
317
318pub(crate) trait BusMessage<E: FieldElement<BaseField = Felt>>: fmt::Display {
323 fn value(&self, alphas: &[E]) -> E;
325
326 fn source(&self) -> &str;
328}
329
330pub(crate) struct BusDebugger<E: FieldElement<BaseField = Felt>> {
336 pub bus_name: String,
337 pub outstanding_requests: Vec<(E, Box<dyn BusMessage<E>>)>,
338 pub outstanding_responses: Vec<(E, Box<dyn BusMessage<E>>)>,
339}
340
341impl<E> BusDebugger<E>
342where
343 E: FieldElement<BaseField = Felt>,
344{
345 pub fn new(bus_name: String) -> Self {
346 Self {
347 bus_name,
348 outstanding_requests: Vec::new(),
349 outstanding_responses: Vec::new(),
350 }
351 }
352}
353
354impl<E> BusDebugger<E>
355where
356 E: FieldElement<BaseField = Felt>,
357{
358 #[allow(dead_code)]
362 pub fn add_request(&mut self, request_msg: Box<dyn BusMessage<E>>, alphas: &[E]) {
363 let msg_value = request_msg.value(alphas);
364
365 if let Some(pos) =
366 self.outstanding_responses.iter().position(|(value, _)| *value == msg_value)
367 {
368 self.outstanding_responses.swap_remove(pos);
369 } else {
370 self.outstanding_requests.push((msg_value, request_msg));
371 }
372 }
373
374 #[allow(dead_code)]
378 pub fn add_response(&mut self, response_msg: Box<dyn BusMessage<E>>, alphas: &[E]) {
379 let msg_value = response_msg.value(alphas);
380
381 if let Some(pos) =
382 self.outstanding_requests.iter().position(|(value, _)| *value == msg_value)
383 {
384 self.outstanding_requests.swap_remove(pos);
385 } else {
386 self.outstanding_responses.push((msg_value, response_msg));
387 }
388 }
389
390 #[allow(dead_code)]
397 pub fn is_empty(&self) -> bool {
398 self.outstanding_requests.is_empty() && self.outstanding_responses.is_empty()
399 }
400}
401
402impl<E> fmt::Display for BusDebugger<E>
403where
404 E: FieldElement<BaseField = Felt>,
405{
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 if self.is_empty() {
408 writeln!(f, "Bus '{}' is empty.", self.bus_name)?;
409 } else {
410 writeln!(f, "Bus '{}' construction failed.", self.bus_name)?;
411
412 if !self.outstanding_requests.is_empty() {
413 writeln!(f, "The following requests are still outstanding:")?;
414 for (_value, msg) in &self.outstanding_requests {
415 writeln!(f, "- {}: {}", msg.source(), msg)?;
416 }
417 }
418
419 if !self.outstanding_responses.is_empty() {
420 writeln!(f, "\nThe following responses are still outstanding:")?;
421 for (_value, msg) in &self.outstanding_responses {
422 writeln!(f, "- {}: {}", msg.source(), msg)?;
423 }
424 }
425 }
426
427 Ok(())
428 }
429}