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}