1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::fmt;
7
8use miden_air::RowIndex;
9use miden_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 = self.clk.saturating_sub(1);
174
175 result
176 }
177
178 pub fn into_parts(self) -> (System, Decoder, Stack, Chiplets, Option<ExecutionError>) {
179 (self.system, self.decoder, self.stack, self.chiplets, self.error)
180 }
181
182 pub fn trace_len_summary(&self) -> &TraceLenSummary {
183 &self.trace_len_summary
184 }
185
186 fn build_trace_len_summary(
188 system: &System,
189 range: &mut RangeChecker,
190 chiplets: &Chiplets,
191 ) -> TraceLenSummary {
192 let clk = system.clk();
193 let range_table_len = range.get_number_range_checker_rows();
194 chiplets.append_range_checks(range);
195
196 TraceLenSummary::new(clk.into(), range_table_len, ChipletsLengths::new(chiplets))
197 }
198}
199
200impl Iterator for VmStateIterator {
201 type Item = Result<VmState, ExecutionError>;
202
203 fn next(&mut self) -> Option<Self::Item> {
204 if self.clk > self.system.clk() {
205 match &self.error {
206 Some(_) => {
207 let error = core::mem::take(&mut self.error);
208 return Some(Err(error.unwrap()));
209 },
210 None => return None,
211 }
212 }
213
214 if !self.forward && self.clk < self.system.clk() {
216 self.clk += 1_u32;
217 self.forward = true;
218 }
219
220 let ctx = self.system.get_ctx_at(self.clk);
221
222 let op = if self.clk == 0 {
223 None
224 } else {
225 Some(self.decoder.debug_info().operations()[self.clk - 1])
226 };
227
228 let (asmop, is_start) = self.get_asmop();
229 if is_start {
230 self.asmop_idx += 1;
231 }
232
233 let result = Some(Ok(VmState {
234 clk: self.clk,
235 ctx,
236 op,
237 asmop,
238 fmp: self.system.get_fmp_at(self.clk),
239 stack: self.stack.get_state_at(self.clk),
240 memory: self.chiplets.memory.get_state_at(ctx, self.clk),
241 }));
242
243 self.clk += 1_u32;
244
245 result
246 }
247}
248
249#[derive(Clone, Debug, Eq, PartialEq)]
252pub struct AsmOpInfo {
253 asmop: AssemblyOp,
254 cycle_idx: u8,
255}
256
257impl AsmOpInfo {
261 pub fn new(asmop: AssemblyOp, cycle_idx: u8) -> Self {
265 Self { asmop, cycle_idx }
266 }
267
268 pub fn context_name(&self) -> &str {
270 self.asmop.context_name()
271 }
272
273 pub fn op(&self) -> &str {
275 self.asmop.op()
276 }
277
278 pub fn op_generalized(&self) -> String {
280 let op_vec: Vec<&str> = self.op().split('.').collect();
281 let keep_params = matches!(op_vec[0], "movdn" | "movup");
282 if !keep_params && op_vec.last().unwrap().parse::<usize>().is_ok() {
283 op_vec.split_last().unwrap().1.join(".")
284 } else {
285 self.op().to_string()
286 }
287 }
288
289 pub fn num_cycles(&self) -> u8 {
291 self.asmop.num_cycles()
292 }
293
294 pub fn cycle_idx(&self) -> u8 {
297 self.cycle_idx
298 }
299
300 pub const fn should_break(&self) -> bool {
302 self.asmop.should_break()
303 }
304}
305
306impl fmt::Display for AsmOpInfo {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 write!(f, "{}, cycles={}", self.asmop, self.cycle_idx)
309 }
310}
311
312impl AsRef<AssemblyOp> for AsmOpInfo {
313 #[inline]
314 fn as_ref(&self) -> &AssemblyOp {
315 &self.asmop
316 }
317}
318
319pub(crate) trait BusMessage<E: FieldElement<BaseField = Felt>>: fmt::Display {
324 fn value(&self, alphas: &[E]) -> E;
326
327 fn source(&self) -> &str;
329}
330
331pub(crate) struct BusDebugger<E: FieldElement<BaseField = Felt>> {
337 pub bus_name: String,
338 pub outstanding_requests: Vec<(E, Box<dyn BusMessage<E>>)>,
339 pub outstanding_responses: Vec<(E, Box<dyn BusMessage<E>>)>,
340}
341
342impl<E> BusDebugger<E>
343where
344 E: FieldElement<BaseField = Felt>,
345{
346 pub fn new(bus_name: String) -> Self {
347 Self {
348 bus_name,
349 outstanding_requests: Vec::new(),
350 outstanding_responses: Vec::new(),
351 }
352 }
353}
354
355impl<E> BusDebugger<E>
356where
357 E: FieldElement<BaseField = Felt>,
358{
359 #[allow(dead_code)]
363 pub fn add_request(&mut self, request_msg: Box<dyn BusMessage<E>>, alphas: &[E]) {
364 let msg_value = request_msg.value(alphas);
365
366 if let Some(pos) =
367 self.outstanding_responses.iter().position(|(value, _)| *value == msg_value)
368 {
369 self.outstanding_responses.swap_remove(pos);
370 } else {
371 self.outstanding_requests.push((msg_value, request_msg));
372 }
373 }
374
375 #[allow(dead_code)]
379 pub fn add_response(&mut self, response_msg: Box<dyn BusMessage<E>>, alphas: &[E]) {
380 let msg_value = response_msg.value(alphas);
381
382 if let Some(pos) =
383 self.outstanding_requests.iter().position(|(value, _)| *value == msg_value)
384 {
385 self.outstanding_requests.swap_remove(pos);
386 } else {
387 self.outstanding_responses.push((msg_value, response_msg));
388 }
389 }
390
391 #[allow(dead_code)]
398 pub fn is_empty(&self) -> bool {
399 self.outstanding_requests.is_empty() && self.outstanding_responses.is_empty()
400 }
401}
402
403impl<E> fmt::Display for BusDebugger<E>
404where
405 E: FieldElement<BaseField = Felt>,
406{
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 if self.is_empty() {
409 writeln!(f, "Bus '{}' is empty.", self.bus_name)?;
410 } else {
411 writeln!(f, "Bus '{}' construction failed.", self.bus_name)?;
412
413 if !self.outstanding_requests.is_empty() {
414 writeln!(f, "The following requests are still outstanding:")?;
415 for (_value, msg) in &self.outstanding_requests {
416 writeln!(f, "- {}: {}", msg.source(), msg)?;
417 }
418 }
419
420 if !self.outstanding_responses.is_empty() {
421 writeln!(f, "\nThe following responses are still outstanding:")?;
422 for (_value, msg) in &self.outstanding_responses {
423 writeln!(f, "- {}: {}", msg.source(), msg)?;
424 }
425 }
426 }
427
428 Ok(())
429 }
430}