Skip to main content

cairo_vm/vm/context/
run_context.rs

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::types::instruction::{ApUpdate, FpUpdate, Opcode, OpcodeExtension, PcUpdate, Res};
109    use crate::utils::test_utils::mayberelocatable;
110    use crate::vm::errors::memory_errors::MemoryError;
111    use crate::Felt252;
112    use assert_matches::assert_matches;
113
114    #[test]
115    fn compute_dst_addr_for_ap_register() {
116        let instruction = Instruction {
117            off0: 1,
118            off1: 2,
119            off2: 3,
120            dst_register: Register::AP,
121            op0_register: Register::FP,
122            op1_addr: Op1Addr::AP,
123            res: Res::Add,
124            pc_update: PcUpdate::Regular,
125            ap_update: ApUpdate::Regular,
126            fp_update: FpUpdate::Regular,
127            opcode: Opcode::NOp,
128            opcode_extension: OpcodeExtension::Stone,
129        };
130
131        let run_context = RunContext {
132            pc: relocatable!(0, 4),
133            ap: 5,
134            fp: 6,
135        };
136        assert_matches!(
137            run_context.compute_dst_addr(&instruction),
138            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 6)
139        );
140    }
141
142    #[test]
143    fn compute_dst_addr_for_fp_register() {
144        let instruction = Instruction {
145            off0: 1,
146            off1: 2,
147            off2: 3,
148            dst_register: Register::FP,
149            op0_register: Register::AP,
150            op1_addr: Op1Addr::AP,
151            res: Res::Add,
152            pc_update: PcUpdate::Regular,
153            ap_update: ApUpdate::Regular,
154            fp_update: FpUpdate::Regular,
155            opcode: Opcode::NOp,
156            opcode_extension: OpcodeExtension::Stone,
157        };
158
159        let run_context = RunContext {
160            pc: relocatable!(0, 4),
161            ap: 5,
162            fp: 6,
163        };
164
165        assert_matches!(
166            run_context.compute_dst_addr(&instruction),
167            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 7)
168        );
169    }
170
171    #[test]
172    fn compute_op0_addr_for_ap_register() {
173        let instruction = Instruction {
174            off0: 1,
175            off1: 2,
176            off2: 3,
177            dst_register: Register::AP,
178            op0_register: Register::AP,
179            op1_addr: Op1Addr::AP,
180            res: Res::Add,
181            pc_update: PcUpdate::Regular,
182            ap_update: ApUpdate::Regular,
183            fp_update: FpUpdate::Regular,
184            opcode: Opcode::NOp,
185            opcode_extension: OpcodeExtension::Stone,
186        };
187
188        let run_context = RunContext {
189            pc: relocatable!(0, 4),
190            ap: 5,
191            fp: 6,
192        };
193        assert_matches!(
194            run_context.compute_op0_addr(&instruction),
195            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 7)
196        );
197    }
198
199    #[test]
200    fn compute_op0_addr_for_fp_register() {
201        let instruction = Instruction {
202            off0: 1,
203            off1: 2,
204            off2: 3,
205            dst_register: Register::FP,
206            op0_register: Register::FP,
207            op1_addr: Op1Addr::AP,
208            res: Res::Add,
209            pc_update: PcUpdate::Regular,
210            ap_update: ApUpdate::Regular,
211            fp_update: FpUpdate::Regular,
212            opcode: Opcode::NOp,
213            opcode_extension: OpcodeExtension::Stone,
214        };
215
216        let run_context = RunContext {
217            pc: relocatable!(0, 4),
218            ap: 5,
219            fp: 6,
220        };
221        assert_matches!(
222            run_context.compute_op0_addr(&instruction),
223            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
224        );
225    }
226
227    #[test]
228    fn compute_op1_addr_for_fp_op1_addr() {
229        let instruction = Instruction {
230            off0: 1,
231            off1: 2,
232            off2: 3,
233            dst_register: Register::FP,
234            op0_register: Register::AP,
235            op1_addr: Op1Addr::FP,
236            res: Res::Add,
237            pc_update: PcUpdate::Regular,
238            ap_update: ApUpdate::Regular,
239            fp_update: FpUpdate::Regular,
240            opcode: Opcode::NOp,
241            opcode_extension: OpcodeExtension::Stone,
242        };
243
244        let run_context = RunContext {
245            pc: relocatable!(0, 4),
246            ap: 5,
247            fp: 6,
248        };
249        assert_matches!(
250            run_context.compute_op1_addr(&instruction, None),
251            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 9)
252        );
253    }
254
255    #[test]
256    fn compute_op1_addr_for_ap_op1_addr() {
257        let instruction = Instruction {
258            off0: 1,
259            off1: 2,
260            off2: 3,
261            dst_register: Register::FP,
262            op0_register: Register::AP,
263            op1_addr: Op1Addr::AP,
264            res: Res::Add,
265            pc_update: PcUpdate::Regular,
266            ap_update: ApUpdate::Regular,
267            fp_update: FpUpdate::Regular,
268            opcode: Opcode::NOp,
269            opcode_extension: OpcodeExtension::Stone,
270        };
271
272        let run_context = RunContext {
273            pc: relocatable!(0, 4),
274            ap: 5,
275            fp: 6,
276        };
277        assert_matches!(
278            run_context.compute_op1_addr(&instruction, None),
279            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
280        );
281    }
282
283    #[test]
284    fn compute_op1_addr_for_imm_op1_addr_correct_off2() {
285        let instruction = Instruction {
286            off0: 1,
287            off1: 2,
288            off2: 1,
289            dst_register: Register::FP,
290            op0_register: Register::AP,
291            op1_addr: Op1Addr::Imm,
292            res: Res::Add,
293            pc_update: PcUpdate::Regular,
294            ap_update: ApUpdate::Regular,
295            fp_update: FpUpdate::Regular,
296            opcode: Opcode::NOp,
297            opcode_extension: OpcodeExtension::Stone,
298        };
299
300        let run_context = RunContext {
301            pc: relocatable!(0, 4),
302            ap: 5,
303            fp: 6,
304        };
305        assert_matches!(
306            run_context.compute_op1_addr(&instruction, None),
307            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(0, 5)
308        );
309    }
310
311    #[test]
312    fn compute_op1_addr_for_imm_op1_addr_incorrect_off2() {
313        let instruction = Instruction {
314            off0: 1,
315            off1: 2,
316            off2: 3,
317            dst_register: Register::FP,
318            op0_register: Register::AP,
319            op1_addr: Op1Addr::Imm,
320            res: Res::Add,
321            pc_update: PcUpdate::Regular,
322            ap_update: ApUpdate::Regular,
323            fp_update: FpUpdate::Regular,
324            opcode: Opcode::NOp,
325            opcode_extension: OpcodeExtension::Stone,
326        };
327
328        let run_context = RunContext {
329            pc: relocatable!(0, 4),
330            ap: 5,
331            fp: 6,
332        };
333
334        let error = run_context.compute_op1_addr(&instruction, None);
335        assert_matches!(error, Err(VirtualMachineError::ImmShouldBe1));
336        assert_eq!(
337            error.unwrap_err().to_string(),
338            "In immediate mode, off2 should be 1"
339        );
340    }
341
342    #[test]
343    fn compute_op1_addr_for_op0_op1_addr_with_op0() {
344        let instruction = Instruction {
345            off0: 1,
346            off1: 2,
347            off2: 1,
348            dst_register: Register::FP,
349            op0_register: Register::AP,
350            op1_addr: Op1Addr::Op0,
351            res: Res::Add,
352            pc_update: PcUpdate::Regular,
353            ap_update: ApUpdate::Regular,
354            fp_update: FpUpdate::Regular,
355            opcode: Opcode::NOp,
356            opcode_extension: OpcodeExtension::Stone,
357        };
358
359        let run_context = RunContext {
360            pc: relocatable!(0, 4),
361            ap: 5,
362            fp: 6,
363        };
364
365        let op0 = mayberelocatable!(1, 7);
366        assert_matches!(
367            run_context.compute_op1_addr(&instruction, Some(&op0)),
368            Ok::<Relocatable, VirtualMachineError>(x) if x == relocatable!(1, 8)
369        );
370    }
371
372    #[test]
373    fn compute_op1_addr_with_no_relocatable_address() {
374        let instruction = Instruction {
375            off0: 1,
376            off1: 2,
377            off2: 1,
378            dst_register: Register::FP,
379            op0_register: Register::AP,
380            op1_addr: Op1Addr::Op0,
381            res: Res::Add,
382            pc_update: PcUpdate::Regular,
383            ap_update: ApUpdate::Regular,
384            fp_update: FpUpdate::Regular,
385            opcode: Opcode::NOp,
386            opcode_extension: OpcodeExtension::Stone,
387        };
388
389        let run_context = RunContext {
390            pc: relocatable!(0, 4),
391            ap: 5,
392            fp: 6,
393        };
394
395        let op0 = MaybeRelocatable::from(Felt252::from(7));
396        assert_matches!(
397            run_context.compute_op1_addr(&instruction, Some(&op0)),
398            Err::<Relocatable, VirtualMachineError>(VirtualMachineError::Memory(
399                MemoryError::AddressNotRelocatable
400            ))
401        );
402    }
403
404    #[test]
405    fn compute_op1_addr_for_op0_op1_addr_without_op0() {
406        let instruction = Instruction {
407            off0: 1,
408            off1: 2,
409            off2: 3,
410            dst_register: Register::FP,
411            op0_register: Register::AP,
412            op1_addr: Op1Addr::Op0,
413            res: Res::Add,
414            pc_update: PcUpdate::Regular,
415            ap_update: ApUpdate::Regular,
416            fp_update: FpUpdate::Regular,
417            opcode: Opcode::NOp,
418            opcode_extension: OpcodeExtension::Stone,
419        };
420
421        let run_context = RunContext {
422            pc: relocatable!(0, 4),
423            ap: 5,
424            fp: 6,
425        };
426
427        let error = run_context.compute_op1_addr(&instruction, None);
428        assert_matches!(error, Err(VirtualMachineError::UnknownOp0));
429        assert_eq!(
430            error.unwrap_err().to_string(),
431            "op0 must be known in double dereference"
432        );
433    }
434}