1use crate::{
2 types::instruction::{
3 ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Register, Res,
4 },
5 vm::errors::vm_errors::VirtualMachineError,
6};
7
8pub fn decode_instruction(encoded_instr: u128) -> Result<Instruction, VirtualMachineError> {
16 const DST_REG_MASK: u128 = 0x0001;
17 const DST_REG_OFF: u128 = 0;
18 const OP0_REG_MASK: u128 = 0x0002;
19 const OP0_REG_OFF: u128 = 1;
20 const OP1_SRC_MASK: u128 = 0x001C;
21 const OP1_SRC_OFF: u128 = 2;
22 const RES_LOGIC_MASK: u128 = 0x0060;
23 const RES_LOGIC_OFF: u128 = 5;
24 const PC_UPDATE_MASK: u128 = 0x0380;
25 const PC_UPDATE_OFF: u128 = 7;
26 const AP_UPDATE_MASK: u128 = 0x0C00;
27 const AP_UPDATE_OFF: u128 = 10;
28 const OPCODE_MASK: u128 = 0x7000;
29 const OPCODE_OFF: u128 = 12;
30 const OPCODE_EXTENSION_OFF: u128 = 63;
31
32 const FLAGS_OFFSET: u128 = 48;
34 const OFF0_OFF: u128 = 0;
35 const OFF1_OFF: u128 = 16;
36 const OFF2_OFF: u128 = 32;
37 const OFFX_MASK: u128 = 0xFFFF;
38
39 let off0 = decode_offset(encoded_instr >> OFF0_OFF & OFFX_MASK);
41 let off1 = decode_offset(encoded_instr >> OFF1_OFF & OFFX_MASK);
42 let off2 = decode_offset(encoded_instr >> OFF2_OFF & OFFX_MASK);
43
44 let flags = encoded_instr >> FLAGS_OFFSET;
46 let dst_reg_num = (flags & DST_REG_MASK) >> DST_REG_OFF;
48 let op0_reg_num = (flags & OP0_REG_MASK) >> OP0_REG_OFF;
49 let op1_src_num = (flags & OP1_SRC_MASK) >> OP1_SRC_OFF;
50 let res_logic_num = (flags & RES_LOGIC_MASK) >> RES_LOGIC_OFF;
51 let pc_update_num = (flags & PC_UPDATE_MASK) >> PC_UPDATE_OFF;
52 let ap_update_num = (flags & AP_UPDATE_MASK) >> AP_UPDATE_OFF;
53 let opcode_num = (flags & OPCODE_MASK) >> OPCODE_OFF;
54
55 let opcode_extension_num = encoded_instr >> OPCODE_EXTENSION_OFF;
57
58 let dst_register = if dst_reg_num == 1 {
60 Register::FP
61 } else {
62 Register::AP
63 };
64
65 let op0_register = if op0_reg_num == 1 {
66 Register::FP
67 } else {
68 Register::AP
69 };
70
71 let op1_addr = match op1_src_num {
72 0 => Op1Addr::Op0,
73 1 => Op1Addr::Imm,
74 2 => Op1Addr::FP,
75 4 => Op1Addr::AP,
76 _ => return Err(VirtualMachineError::InvalidOp1Reg(op1_src_num)),
77 };
78
79 let pc_update = match pc_update_num {
80 0 => PcUpdate::Regular,
81 1 => PcUpdate::Jump,
82 2 => PcUpdate::JumpRel,
83 4 => PcUpdate::Jnz,
84 _ => return Err(VirtualMachineError::InvalidPcUpdate(pc_update_num)),
85 };
86
87 let res = match (res_logic_num, pc_update == PcUpdate::Jnz) {
88 (0, true) => Res::Unconstrained,
89 (0, false) => Res::Op1,
90 (1, false) => Res::Add,
91 (2, false) => Res::Mul,
92 _ => return Err(VirtualMachineError::InvalidRes(res_logic_num)),
93 };
94
95 let opcode = match opcode_num {
96 0 => Opcode::NOp,
97 1 => Opcode::Call,
98 2 => Opcode::Ret,
99 4 => Opcode::AssertEq,
100 _ => return Err(VirtualMachineError::InvalidOpcode(opcode_num)),
101 };
102
103 let opcode_extension = match opcode_extension_num {
104 0 => OpcodeExtension::Stone,
105 1 => OpcodeExtension::Blake,
106 2 => OpcodeExtension::BlakeFinalize,
107 3 => OpcodeExtension::QM31Operation,
108 _ => {
109 return Err(VirtualMachineError::InvalidOpcodeExtension(
110 opcode_extension_num,
111 ))
112 }
113 };
114
115 let blake_flags_valid = opcode == Opcode::NOp
116 && (op1_addr == Op1Addr::FP || op1_addr == Op1Addr::AP)
117 && res == Res::Op1
118 && pc_update == PcUpdate::Regular
119 && (ap_update_num == 0 || ap_update_num == 2);
120
121 if (opcode_extension == OpcodeExtension::Blake
122 || opcode_extension == OpcodeExtension::BlakeFinalize)
123 && !blake_flags_valid
124 {
125 return Err(VirtualMachineError::InvalidBlake2sFlags(flags & 0x7FFF));
126 }
127
128 let qm31_operation_flags_valid = (res == Res::Add || res == Res::Mul)
129 && op1_addr != Op1Addr::Op0
130 && pc_update == PcUpdate::Regular
131 && opcode == Opcode::AssertEq
132 && (ap_update_num == 0 || ap_update_num == 2);
133
134 if opcode_extension == OpcodeExtension::QM31Operation && !qm31_operation_flags_valid {
135 return Err(VirtualMachineError::InvalidQM31AddMulFlags(flags & 0x7FFF));
136 }
137
138 let ap_update = match (ap_update_num, opcode == Opcode::Call) {
139 (0, true) => ApUpdate::Add2,
140 (0, false) => ApUpdate::Regular,
141 (1, false) => ApUpdate::Add,
142 (2, false) => ApUpdate::Add1,
143 _ => return Err(VirtualMachineError::InvalidApUpdate(ap_update_num)),
144 };
145
146 let fp_update = match opcode {
147 Opcode::Call => {
148 if off0 != 0
149 || off1 != 1
150 || ap_update != ApUpdate::Add2
151 || dst_register != Register::AP
152 || op0_register != Register::AP
153 {
154 return Err(VirtualMachineError::InvalidOpcode(opcode_num));
155 };
156 FpUpdate::APPlus2
157 }
158 Opcode::Ret => {
159 if off0 != -2
160 || off2 != -1
161 || dst_register != Register::FP
162 || op1_addr != Op1Addr::FP
163 || res != Res::Op1
164 || pc_update != PcUpdate::Jump
165 {
166 return Err(VirtualMachineError::InvalidOpcode(opcode_num));
167 };
168 FpUpdate::Dst
169 }
170 _ => FpUpdate::Regular,
171 };
172
173 Ok(Instruction {
174 off0,
175 off1,
176 off2,
177 dst_register,
178 op0_register,
179 op1_addr,
180 res,
181 pc_update,
182 ap_update,
183 fp_update,
184 opcode,
185 opcode_extension,
186 })
187}
188
189fn decode_offset(offset: u128) -> isize {
190 let vectorized_offset: [u8; 16] = offset.to_le_bytes();
191 let offset_16b_encoded = u16::from_le_bytes([vectorized_offset[0], vectorized_offset[1]]);
192 let complement_const = 0x8000u16;
193 let (offset_16b, _) = offset_16b_encoded.overflowing_sub(complement_const);
194 isize::from(offset_16b as i16)
195}
196
197#[cfg(test)]
198mod decoder_test {
199 use super::*;
200 use crate::stdlib::string::ToString;
201 use assert_matches::assert_matches;
202
203 #[cfg(target_arch = "wasm32")]
204 use wasm_bindgen_test::*;
205
206 #[test]
207 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
208 fn non_zero_high_bits() {
209 let error = decode_instruction(0x214a7800080008000);
210 assert_eq!(
211 error.unwrap_err().to_string(),
212 "Invalid opcode extension value: 4",
213 )
214 }
215
216 #[test]
217 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
218 fn invalid_op1_reg() {
219 let error = decode_instruction(0x294F800080008000);
220 assert_matches!(error, Err(VirtualMachineError::InvalidOp1Reg(3)));
221 assert_eq!(
222 error.unwrap_err().to_string(),
223 "Invalid op1_register value: 3"
224 )
225 }
226
227 #[test]
228 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
229 fn invalid_pc_update() {
230 let error = decode_instruction(0x29A8800080008000);
231 assert_matches!(error, Err(VirtualMachineError::InvalidPcUpdate(3)));
232 assert_eq!(error.unwrap_err().to_string(), "Invalid pc_update value: 3")
233 }
234
235 #[test]
236 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
237 fn invalid_res_logic() {
238 let error = decode_instruction(0x2968800080008000);
239 assert_matches!(error, Err(VirtualMachineError::InvalidRes(3)));
240 assert_eq!(error.unwrap_err().to_string(), "Invalid res value: 3")
241 }
242
243 #[test]
244 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
245 fn invalid_opcode() {
246 let error = decode_instruction(0x3948800080008000);
247 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(3)));
248 assert_eq!(error.unwrap_err().to_string(), "Invalid opcode value: 3")
249 }
250
251 #[test]
252 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
253 fn invalid_ap_update() {
254 let error = decode_instruction(0x2D48800080008000);
255 assert_matches!(error, Err(VirtualMachineError::InvalidApUpdate(3)));
256 assert_eq!(error.unwrap_err().to_string(), "Invalid ap_update value: 3")
257 }
258
259 #[test]
260 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
261 fn decode_flags_nop_add_jmp_add_imm_fp_fp() {
262 let inst = decode_instruction(0x04A7800080008000).unwrap();
268 assert_matches!(inst.dst_register, Register::FP);
269 assert_matches!(inst.op0_register, Register::FP);
270 assert_matches!(inst.op1_addr, Op1Addr::Imm);
271 assert_matches!(inst.res, Res::Add);
272 assert_matches!(inst.pc_update, PcUpdate::Jump);
273 assert_matches!(inst.ap_update, ApUpdate::Add);
274 assert_matches!(inst.opcode, Opcode::NOp);
275 assert_matches!(inst.fp_update, FpUpdate::Regular);
276 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
277 }
278
279 #[test]
280 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
281 fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() {
282 let inst = decode_instruction(0x0948800080008000).unwrap();
288 assert_matches!(inst.dst_register, Register::AP);
289 assert_matches!(inst.op0_register, Register::AP);
290 assert_matches!(inst.op1_addr, Op1Addr::FP);
291 assert_matches!(inst.res, Res::Mul);
292 assert_matches!(inst.pc_update, PcUpdate::JumpRel);
293 assert_matches!(inst.ap_update, ApUpdate::Add1);
294 assert_matches!(inst.opcode, Opcode::NOp);
295 assert_matches!(inst.fp_update, FpUpdate::Regular);
296 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
297 }
298
299 #[test]
300 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
301 fn decode_flags_assrt_add_regular_mul_ap_ap_ap() {
302 let inst = decode_instruction(0x4850800080008000).unwrap();
308 assert_matches!(inst.dst_register, Register::AP);
309 assert_matches!(inst.op0_register, Register::AP);
310 assert_matches!(inst.op1_addr, Op1Addr::AP);
311 assert_matches!(inst.res, Res::Mul);
312 assert_matches!(inst.pc_update, PcUpdate::Regular);
313 assert_matches!(inst.ap_update, ApUpdate::Add1);
314 assert_matches!(inst.opcode, Opcode::AssertEq);
315 assert_matches!(inst.fp_update, FpUpdate::Regular);
316 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
317 }
318
319 #[test]
320 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
321 fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() {
322 let inst = decode_instruction(0x4200800080008000).unwrap();
328 assert_matches!(inst.dst_register, Register::AP);
329 assert_matches!(inst.op0_register, Register::AP);
330 assert_matches!(inst.op1_addr, Op1Addr::Op0);
331 assert_matches!(inst.res, Res::Unconstrained);
332 assert_matches!(inst.pc_update, PcUpdate::Jnz);
333 assert_matches!(inst.ap_update, ApUpdate::Regular);
334 assert_matches!(inst.opcode, Opcode::AssertEq);
335 assert_matches!(inst.fp_update, FpUpdate::Regular);
336 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
337 }
338
339 #[test]
340 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
341 fn decode_flags_nop_regu_regu_op1_op0_ap_ap() {
342 let inst = decode_instruction(0x0000800080008000).unwrap();
348 assert_matches!(inst.dst_register, Register::AP);
349 assert_matches!(inst.op0_register, Register::AP);
350 assert_matches!(inst.op1_addr, Op1Addr::Op0);
351 assert_matches!(inst.res, Res::Op1);
352 assert_matches!(inst.pc_update, PcUpdate::Regular);
353 assert_matches!(inst.ap_update, ApUpdate::Regular);
354 assert_matches!(inst.opcode, Opcode::NOp);
355 assert_matches!(inst.fp_update, FpUpdate::Regular);
356 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
357 }
358
359 #[test]
360 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
361 fn decode_offset_negative() {
362 let inst = decode_instruction(0x0000800180007FFF).unwrap();
368 assert_eq!(inst.off0, -1);
369 assert_eq!(inst.off1, 0);
370 assert_eq!(inst.off2, 1);
371 }
372
373 #[test]
374 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
375 fn decode_ret_cairo_standard() {
376 let inst = decode_instruction(0x208b7fff7fff7ffe).unwrap();
382 assert_matches!(inst.opcode, Opcode::Ret);
383 assert_matches!(inst.off0, -2);
384 assert_matches!(inst.off1, -1);
385 assert_matches!(inst.dst_register, Register::FP);
386 assert_matches!(inst.op0_register, Register::FP);
387 assert_matches!(inst.op1_addr, Op1Addr::FP);
388 assert_matches!(inst.res, Res::Op1);
389 assert_matches!(inst.pc_update, PcUpdate::Jump);
390 assert_matches!(inst.ap_update, ApUpdate::Regular);
391 assert_matches!(inst.fp_update, FpUpdate::Dst);
392 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
393 }
394
395 #[test]
396 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
397 fn decode_call_cairo_standard() {
398 let inst = decode_instruction(0x1104800180018000).unwrap();
404 assert_matches!(inst.opcode, Opcode::Call);
405 assert_matches!(inst.off0, 0);
406 assert_matches!(inst.off1, 1);
407 assert_matches!(inst.dst_register, Register::AP);
408 assert_matches!(inst.op0_register, Register::AP);
409 assert_matches!(inst.op1_addr, Op1Addr::Imm);
410 assert_matches!(inst.res, Res::Op1);
411 assert_matches!(inst.pc_update, PcUpdate::JumpRel);
412 assert_matches!(inst.ap_update, ApUpdate::Add2);
413 assert_matches!(inst.fp_update, FpUpdate::APPlus2);
414 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
415 }
416
417 #[test]
418 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
419 fn decode_ret_opcode_error() {
420 let error = decode_instruction(0x208b7fff7fff7fff);
426 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(2)));
427 }
428
429 #[test]
430 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
431 fn decode_call_opcode_error() {
432 let error = decode_instruction(0x1104800180018001);
438 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1)));
439 }
440
441 #[test]
442 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
443 fn decode_opcode_extension_clash() {
444 let error = decode_instruction(0x9008800180018001);
450 assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104)));
451 }
452
453 #[test]
454 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
455 fn decode_blake_imm() {
456 let error = decode_instruction(0x8004800180018001);
462 assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4)));
463 }
464
465 #[test]
466 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
467 fn decode_blake() {
468 let inst = decode_instruction(0x8813800180018001).unwrap();
474 assert_matches!(inst.opcode, Opcode::NOp);
475 assert_matches!(inst.off0, 1);
476 assert_matches!(inst.off1, 1);
477 assert_matches!(inst.dst_register, Register::FP);
478 assert_matches!(inst.op0_register, Register::FP);
479 assert_matches!(inst.op1_addr, Op1Addr::AP);
480 assert_matches!(inst.res, Res::Op1);
481 assert_matches!(inst.pc_update, PcUpdate::Regular);
482 assert_matches!(inst.ap_update, ApUpdate::Add1);
483 assert_matches!(inst.fp_update, FpUpdate::Regular);
484 assert_matches!(inst.opcode_extension, OpcodeExtension::Blake);
485 }
486
487 #[test]
488 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
489 fn decode_invalid_opcode_extension_error() {
490 let error = decode_instruction(0x39104800180018000);
496 assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(7)));
497 }
498
499 #[test]
500 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
501 fn decode_qm31_operation_invalid_flags() {
502 let error = decode_instruction(0x19108800180018001);
508 assert_matches!(
509 error,
510 Err(VirtualMachineError::InvalidQM31AddMulFlags(0x1108))
511 );
512 }
513
514 #[test]
515 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
516 fn decode_qm31_operation() {
517 let inst = decode_instruction(0x1c048800180018001).unwrap();
523 assert_matches!(inst.opcode, Opcode::AssertEq);
524 assert_matches!(inst.off0, 1);
525 assert_matches!(inst.off1, 1);
526 assert_matches!(inst.dst_register, Register::AP);
527 assert_matches!(inst.op0_register, Register::AP);
528 assert_matches!(inst.op1_addr, Op1Addr::FP);
529 assert_matches!(inst.res, Res::Mul);
530 assert_matches!(inst.pc_update, PcUpdate::Regular);
531 assert_matches!(inst.ap_update, ApUpdate::Regular);
532 assert_matches!(inst.fp_update, FpUpdate::Regular);
533 assert_matches!(inst.opcode_extension, OpcodeExtension::QM31Operation);
534 }
535}