1use crate::{
2 types::{
3 instruction::{Instruction, Op1Addr, Register},
4 relocatable::{MaybeRelocatable, Relocatable},
5 },
6 vm::errors::{
7 memory_errors::MemoryError::AddressNotRelocatable, vm_errors::VirtualMachineError,
8 },
9};
10use num_traits::abs;
11
12pub struct RunContext {
13 pub(crate) pc: Relocatable,
14 pub(crate) ap: usize,
15 pub(crate) fp: usize,
16}
17
18impl RunContext {
19 pub fn get_ap(&self) -> Relocatable {
20 Relocatable::from((1, self.ap))
21 }
22 pub fn get_fp(&self) -> Relocatable {
23 Relocatable::from((1, self.fp))
24 }
25 pub fn get_pc(&self) -> Relocatable {
26 self.pc
27 }
28
29 pub fn new(pc: Relocatable, ap: usize, fp: usize) -> Self {
30 RunContext { pc, ap, fp }
31 }
32
33 pub fn compute_dst_addr(
34 &self,
35 instruction: &Instruction,
36 ) -> Result<Relocatable, VirtualMachineError> {
37 let base_addr = match instruction.dst_register {
38 Register::AP => self.get_ap(),
39 Register::FP => self.get_fp(),
40 };
41 if instruction.off0 < 0 {
42 Ok((base_addr - abs(instruction.off0) as usize)?)
43 } else {
44 Ok((base_addr + (instruction.off0 as usize))?)
45 }
46 }
47
48 pub fn compute_op0_addr(
49 &self,
50 instruction: &Instruction,
51 ) -> Result<Relocatable, VirtualMachineError> {
52 let base_addr = match instruction.op0_register {
53 Register::AP => self.get_ap(),
54 Register::FP => self.get_fp(),
55 };
56 if instruction.off1 < 0 {
57 Ok((base_addr - abs(instruction.off1) as usize)?)
58 } else {
59 Ok((base_addr + (instruction.off1 as usize))?)
60 }
61 }
62
63 pub fn compute_op1_addr(
64 &self,
65 instruction: &Instruction,
66 op0: Option<&MaybeRelocatable>,
67 ) -> Result<Relocatable, VirtualMachineError> {
68 let base_addr = match instruction.op1_addr {
69 Op1Addr::FP => self.get_fp(),
70 Op1Addr::AP => self.get_ap(),
71 Op1Addr::Imm => match instruction.off2 == 1 {
72 true => self.pc,
73 false => return Err(VirtualMachineError::ImmShouldBe1),
74 },
75 Op1Addr::Op0 => match op0 {
76 Some(MaybeRelocatable::RelocatableValue(addr)) => *addr,
77 Some(_) => return Err(VirtualMachineError::Memory(AddressNotRelocatable)),
78 None => return Err(VirtualMachineError::UnknownOp0),
79 },
80 };
81 if instruction.off2 < 0 {
82 Ok((base_addr - abs(instruction.off2) as usize)?)
83 } else {
84 Ok((base_addr + (instruction.off2 as usize))?)
85 }
86 }
87
88 #[doc(hidden)]
89 pub(crate) fn set_ap(&mut self, ap: usize) {
90 self.ap = ap;
91 }
92
93 #[doc(hidden)]
94 pub(crate) fn set_fp(&mut self, fp: usize) {
95 self.fp = fp;
96 }
97
98 #[doc(hidden)]
99 pub(crate) fn set_pc(&mut self, pc: Relocatable) {
100 self.pc = pc;
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::relocatable;
108 use crate::stdlib::string::ToString;
109 use crate::types::instruction::{ApUpdate, FpUpdate, Opcode, OpcodeExtension, PcUpdate, Res};
110 use crate::utils::test_utils::mayberelocatable;
111 use crate::vm::errors::memory_errors::MemoryError;
112 use crate::Felt252;
113 use assert_matches::assert_matches;
114
115 #[cfg(target_arch = "wasm32")]
116 use wasm_bindgen_test::*;
117
118 #[test]
119 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
120 fn compute_dst_addr_for_ap_register() {
121 let instruction = Instruction {
122 off0: 1,
123 off1: 2,
124 off2: 3,
125 dst_register: Register::AP,
126 op0_register: Register::FP,
127 op1_addr: Op1Addr::AP,
128 res: Res::Add,
129 pc_update: PcUpdate::Regular,
130 ap_update: ApUpdate::Regular,
131 fp_update: FpUpdate::Regular,
132 opcode: Opcode::NOp,
133 opcode_extension: OpcodeExtension::Stone,
134 };
135
136 let run_context = RunContext {
137 pc: relocatable!(0, 4),
138 ap: 5,
139 fp: 6,
140 };
141 assert_matches!(
142 run_context.compute_dst_addr(&instruction),
143 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 6)
144 );
145 }
146
147 #[test]
148 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
149 fn compute_dst_addr_for_fp_register() {
150 let instruction = Instruction {
151 off0: 1,
152 off1: 2,
153 off2: 3,
154 dst_register: Register::FP,
155 op0_register: Register::AP,
156 op1_addr: Op1Addr::AP,
157 res: Res::Add,
158 pc_update: PcUpdate::Regular,
159 ap_update: ApUpdate::Regular,
160 fp_update: FpUpdate::Regular,
161 opcode: Opcode::NOp,
162 opcode_extension: OpcodeExtension::Stone,
163 };
164
165 let run_context = RunContext {
166 pc: relocatable!(0, 4),
167 ap: 5,
168 fp: 6,
169 };
170
171 assert_matches!(
172 run_context.compute_dst_addr(&instruction),
173 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 7)
174 );
175 }
176
177 #[test]
178 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
179 fn compute_op0_addr_for_ap_register() {
180 let instruction = Instruction {
181 off0: 1,
182 off1: 2,
183 off2: 3,
184 dst_register: Register::AP,
185 op0_register: Register::AP,
186 op1_addr: Op1Addr::AP,
187 res: Res::Add,
188 pc_update: PcUpdate::Regular,
189 ap_update: ApUpdate::Regular,
190 fp_update: FpUpdate::Regular,
191 opcode: Opcode::NOp,
192 opcode_extension: OpcodeExtension::Stone,
193 };
194
195 let run_context = RunContext {
196 pc: relocatable!(0, 4),
197 ap: 5,
198 fp: 6,
199 };
200 assert_matches!(
201 run_context.compute_op0_addr(&instruction),
202 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 7)
203 );
204 }
205
206 #[test]
207 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
208 fn compute_op0_addr_for_fp_register() {
209 let instruction = Instruction {
210 off0: 1,
211 off1: 2,
212 off2: 3,
213 dst_register: Register::FP,
214 op0_register: Register::FP,
215 op1_addr: Op1Addr::AP,
216 res: Res::Add,
217 pc_update: PcUpdate::Regular,
218 ap_update: ApUpdate::Regular,
219 fp_update: FpUpdate::Regular,
220 opcode: Opcode::NOp,
221 opcode_extension: OpcodeExtension::Stone,
222 };
223
224 let run_context = RunContext {
225 pc: relocatable!(0, 4),
226 ap: 5,
227 fp: 6,
228 };
229 assert_matches!(
230 run_context.compute_op0_addr(&instruction),
231 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
232 );
233 }
234
235 #[test]
236 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
237 fn compute_op1_addr_for_fp_op1_addr() {
238 let instruction = Instruction {
239 off0: 1,
240 off1: 2,
241 off2: 3,
242 dst_register: Register::FP,
243 op0_register: Register::AP,
244 op1_addr: Op1Addr::FP,
245 res: Res::Add,
246 pc_update: PcUpdate::Regular,
247 ap_update: ApUpdate::Regular,
248 fp_update: FpUpdate::Regular,
249 opcode: Opcode::NOp,
250 opcode_extension: OpcodeExtension::Stone,
251 };
252
253 let run_context = RunContext {
254 pc: relocatable!(0, 4),
255 ap: 5,
256 fp: 6,
257 };
258 assert_matches!(
259 run_context.compute_op1_addr(&instruction, None),
260 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 9)
261 );
262 }
263
264 #[test]
265 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
266 fn compute_op1_addr_for_ap_op1_addr() {
267 let instruction = Instruction {
268 off0: 1,
269 off1: 2,
270 off2: 3,
271 dst_register: Register::FP,
272 op0_register: Register::AP,
273 op1_addr: Op1Addr::AP,
274 res: Res::Add,
275 pc_update: PcUpdate::Regular,
276 ap_update: ApUpdate::Regular,
277 fp_update: FpUpdate::Regular,
278 opcode: Opcode::NOp,
279 opcode_extension: OpcodeExtension::Stone,
280 };
281
282 let run_context = RunContext {
283 pc: relocatable!(0, 4),
284 ap: 5,
285 fp: 6,
286 };
287 assert_matches!(
288 run_context.compute_op1_addr(&instruction, None),
289 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
290 );
291 }
292
293 #[test]
294 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
295 fn compute_op1_addr_for_imm_op1_addr_correct_off2() {
296 let instruction = Instruction {
297 off0: 1,
298 off1: 2,
299 off2: 1,
300 dst_register: Register::FP,
301 op0_register: Register::AP,
302 op1_addr: Op1Addr::Imm,
303 res: Res::Add,
304 pc_update: PcUpdate::Regular,
305 ap_update: ApUpdate::Regular,
306 fp_update: FpUpdate::Regular,
307 opcode: Opcode::NOp,
308 opcode_extension: OpcodeExtension::Stone,
309 };
310
311 let run_context = RunContext {
312 pc: relocatable!(0, 4),
313 ap: 5,
314 fp: 6,
315 };
316 assert_matches!(
317 run_context.compute_op1_addr(&instruction, None),
318 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(0, 5)
319 );
320 }
321
322 #[test]
323 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
324 fn compute_op1_addr_for_imm_op1_addr_incorrect_off2() {
325 let instruction = Instruction {
326 off0: 1,
327 off1: 2,
328 off2: 3,
329 dst_register: Register::FP,
330 op0_register: Register::AP,
331 op1_addr: Op1Addr::Imm,
332 res: Res::Add,
333 pc_update: PcUpdate::Regular,
334 ap_update: ApUpdate::Regular,
335 fp_update: FpUpdate::Regular,
336 opcode: Opcode::NOp,
337 opcode_extension: OpcodeExtension::Stone,
338 };
339
340 let run_context = RunContext {
341 pc: relocatable!(0, 4),
342 ap: 5,
343 fp: 6,
344 };
345
346 let error = run_context.compute_op1_addr(&instruction, None);
347 assert_matches!(error, Err(VirtualMachineError::ImmShouldBe1));
348 assert_eq!(
349 error.unwrap_err().to_string(),
350 "In immediate mode, off2 should be 1"
351 );
352 }
353
354 #[test]
355 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
356 fn compute_op1_addr_for_op0_op1_addr_with_op0() {
357 let instruction = Instruction {
358 off0: 1,
359 off1: 2,
360 off2: 1,
361 dst_register: Register::FP,
362 op0_register: Register::AP,
363 op1_addr: Op1Addr::Op0,
364 res: Res::Add,
365 pc_update: PcUpdate::Regular,
366 ap_update: ApUpdate::Regular,
367 fp_update: FpUpdate::Regular,
368 opcode: Opcode::NOp,
369 opcode_extension: OpcodeExtension::Stone,
370 };
371
372 let run_context = RunContext {
373 pc: relocatable!(0, 4),
374 ap: 5,
375 fp: 6,
376 };
377
378 let op0 = mayberelocatable!(1, 7);
379 assert_matches!(
380 run_context.compute_op1_addr(&instruction, Some(&op0)),
381 Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
382 );
383 }
384
385 #[test]
386 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
387 fn compute_op1_addr_with_no_relocatable_address() {
388 let instruction = Instruction {
389 off0: 1,
390 off1: 2,
391 off2: 1,
392 dst_register: Register::FP,
393 op0_register: Register::AP,
394 op1_addr: Op1Addr::Op0,
395 res: Res::Add,
396 pc_update: PcUpdate::Regular,
397 ap_update: ApUpdate::Regular,
398 fp_update: FpUpdate::Regular,
399 opcode: Opcode::NOp,
400 opcode_extension: OpcodeExtension::Stone,
401 };
402
403 let run_context = RunContext {
404 pc: relocatable!(0, 4),
405 ap: 5,
406 fp: 6,
407 };
408
409 let op0 = MaybeRelocatable::from(Felt252::from(7));
410 assert_matches!(
411 run_context.compute_op1_addr(&instruction, Some(&op0)),
412 Err::<Relocatable, VirtualMachineError>(VirtualMachineError::Memory(
413 MemoryError::AddressNotRelocatable
414 ))
415 );
416 }
417
418 #[test]
419 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
420 fn compute_op1_addr_for_op0_op1_addr_without_op0() {
421 let instruction = Instruction {
422 off0: 1,
423 off1: 2,
424 off2: 3,
425 dst_register: Register::FP,
426 op0_register: Register::AP,
427 op1_addr: Op1Addr::Op0,
428 res: Res::Add,
429 pc_update: PcUpdate::Regular,
430 ap_update: ApUpdate::Regular,
431 fp_update: FpUpdate::Regular,
432 opcode: Opcode::NOp,
433 opcode_extension: OpcodeExtension::Stone,
434 };
435
436 let run_context = RunContext {
437 pc: relocatable!(0, 4),
438 ap: 5,
439 fp: 6,
440 };
441
442 let error = run_context.compute_op1_addr(&instruction, None);
443 assert_matches!(error, Err(VirtualMachineError::UnknownOp0));
444 assert_eq!(
445 error.unwrap_err().to_string(),
446 "op0 must be known in double dereference"
447 );
448 }
449}