1use miden_core::Word;
2use miden_processor::{
3 ContextId, FastProcessor, Felt, ProcessorState, StackInputs, StackOutputs, trace::RowIndex,
4};
5use smallvec::SmallVec;
6
7use crate::{debug::NativePtr, felt::FromMidenRepr};
8
9#[derive(Debug, thiserror::Error)]
11pub enum MemoryReadError {
12 #[error("attempted to read beyond end of linear memory")]
13 OutOfBounds,
14 #[error("unaligned reads are not supported yet")]
15 UnalignedRead,
16}
17
18pub struct ExecutionTrace {
24 pub(super) processor: FastProcessor,
25 pub(super) outputs: StackOutputs,
26}
27
28impl ExecutionTrace {
29 pub fn empty() -> Self {
33 Self {
34 processor: FastProcessor::new(StackInputs::default()),
35 outputs: StackOutputs::default(),
36 }
37 }
38
39 pub fn parse_result<T>(&self) -> Option<T>
41 where
42 T: FromMidenRepr,
43 {
44 let size = <T as FromMidenRepr>::size_in_felts();
45 let stack = self.outputs.get_num_elements(size);
46 if stack.len() < size {
47 return None;
48 }
49 let mut stack = stack.to_vec();
50 stack.reverse();
51 Some(<T as FromMidenRepr>::pop_from_stack(&mut stack))
52 }
53
54 #[inline]
56 pub fn into_outputs(self) -> StackOutputs {
57 self.outputs
58 }
59
60 #[inline]
62 pub fn outputs(&self) -> &StackOutputs {
63 &self.outputs
64 }
65}
66
67impl super::query::DebugQuery for ExecutionTrace {
68 fn state(&self) -> ProcessorState<'_> {
69 self.processor.state()
70 }
71
72 fn current_context(&self) -> ContextId {
73 self.processor.state().ctx()
74 }
75
76 fn current_clock(&self) -> RowIndex {
77 self.processor.state().clock()
78 }
79}
80
81impl ExecutionTrace {
82 pub fn read_memory_word_in_context(
84 &self,
85 addr: u32,
86 ctx: ContextId,
87 clk: RowIndex,
88 ) -> Option<Word> {
89 const ZERO: Word = Word::new([Felt::ZERO; 4]);
90
91 match self.processor.memory().read_word(
92 ctx,
93 Felt::new(addr as u64).expect("value exceeds field modulus"),
94 clk,
95 ) {
96 Ok(word) => Some(word),
97 Err(_) => Some(ZERO),
98 }
99 }
100
101 #[track_caller]
103 pub fn read_memory_element_in_context(
104 &self,
105 addr: u32,
106 ctx: ContextId,
107 _clk: RowIndex,
108 ) -> Option<Felt> {
109 self.processor
110 .memory()
111 .read_element(ctx, Felt::new(addr as u64).expect("value exceeds field modulus"))
112 .ok()
113 }
114
115 pub fn read_bytes_for_type_in_context(
118 &self,
119 addr: NativePtr,
120 ty: &miden_assembly_syntax::ast::types::Type,
121 ctx: ContextId,
122 clk: RowIndex,
123 ) -> Result<Vec<u8>, MemoryReadError> {
124 let size = ty.size_in_bytes();
125 super::query::read_memory_bytes(addr, size, |addr| {
126 Ok(self.read_memory_element_in_context(addr, ctx, clk).unwrap_or_default())
127 })
128 }
129
130 #[track_caller]
133 pub fn read_from_rust_memory_in_context<T>(
134 &self,
135 addr: u32,
136 ctx: ContextId,
137 clk: RowIndex,
138 ) -> Option<T>
139 where
140 T: core::any::Any + FromMidenRepr,
141 {
142 let ptr = NativePtr::from_ptr(addr);
143 assert_eq!(ptr.offset, 0, "support for unaligned reads is not yet implemented");
144 let size = <T as FromMidenRepr>::size_in_felts();
145 let mut felts = SmallVec::<[_; 4]>::with_capacity(size);
146 for index in 0..(size as u32) {
147 felts.push(self.read_memory_element_in_context(ptr.addr + index, ctx, clk)?);
148 }
149 Some(T::from_felts(&felts))
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use std::sync::Arc;
156
157 use miden_assembly::DefaultSourceManager;
158 use miden_assembly_syntax::ast::types::Type;
159 use miden_processor::{ContextId, trace::RowIndex};
160
161 use super::ExecutionTrace;
162 use crate::{Executor, debug::NativePtr, exec::DebugQuery, felt::ToMidenRepr};
163
164 fn empty_trace() -> ExecutionTrace {
165 ExecutionTrace {
166 processor: miden_processor::FastProcessor::new(miden_processor::StackInputs::default()),
167 outputs: miden_processor::StackOutputs::default(),
168 }
169 }
170
171 fn execute_trace(source: &str) -> ExecutionTrace {
172 let source_manager = Arc::new(DefaultSourceManager::default());
173 let program = miden_assembly::Assembler::new(source_manager.clone())
174 .assemble_program("program", source)
175 .map(Arc::from)
176 .unwrap();
177
178 Executor::new(vec![]).capture_trace(program, source_manager)
179 }
180
181 #[test]
182 fn parse_result_reads_multi_felt_outputs_in_stack_order() {
183 let outputs = 0x0807_0605_0403_0201_u64.to_felts();
184 let trace = ExecutionTrace {
185 outputs: miden_processor::StackOutputs::new(&outputs).unwrap(),
186 ..empty_trace()
187 };
188
189 let result = trace.parse_result::<u64>().unwrap();
190
191 assert_eq!(result, 0x0807_0605_0403_0201_u64);
192 }
193
194 #[test]
195 fn read_bytes_for_type_preserves_little_endian_bytes() {
196 let trace = execute_trace(
197 r#"
198begin
199 push.4660
200 push.8
201 mem_store
202
203 push.67305985
204 push.12
205 mem_store
206
207 push.134678021
208 push.13
209 mem_store
210end
211"#,
212 );
213 let ctx = ContextId::root();
214
215 let u16_bytes = trace
216 .read_bytes_for_type_in_context(
217 NativePtr::new(8, 0),
218 &Type::U16,
219 ctx,
220 RowIndex::from(0_u32),
221 )
222 .unwrap();
223 let u64_bytes = trace
224 .read_bytes_for_type_in_context(
225 NativePtr::new(12, 0),
226 &Type::U64,
227 ctx,
228 RowIndex::from(0_u32),
229 )
230 .unwrap();
231 let unaligned_u32_bytes = trace
232 .read_bytes_for_type_in_context(
233 NativePtr::new(12, 1),
234 &Type::U32,
235 ctx,
236 RowIndex::from(0_u32),
237 )
238 .unwrap();
239
240 assert_eq!(u16_bytes, vec![0x34, 0x12]);
241 assert_eq!(u64_bytes, vec![1, 2, 3, 4, 5, 6, 7, 8]);
242 assert_eq!(unaligned_u32_bytes, vec![2, 3, 4, 5]);
243 }
244
245 #[test]
246 fn debug_query_reads_rust_memory_from_byte_address() {
247 let trace = execute_trace(
248 r#"
249begin
250 push.67305985
251 push.3
252 mem_store
253
254 push.134678021
255 push.4
256 mem_store
257end
258"#,
259 );
260
261 assert_eq!(trace.read_from_rust_memory::<u64>(12), Some(0x0807_0605_0403_0201));
262 }
263
264 #[test]
265 fn debug_query_reads_uninitialized_rust_memory_as_zero() {
266 let trace = execute_trace(
267 r#"
268begin
269 push.67305985
270 push.3
271 mem_store
272end
273"#,
274 );
275
276 assert_eq!(trace.read_from_rust_memory::<u64>(12), Some(0x0000_0000_0403_0201));
277 }
278}