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 assert_matches::assert_matches;
201
202 #[test]
203 fn non_zero_high_bits() {
204 let error = decode_instruction(0x214a7800080008000);
205 assert_eq!(
206 error.unwrap_err().to_string(),
207 "Invalid opcode extension value: 4",
208 )
209 }
210
211 #[test]
212 fn invalid_op1_reg() {
213 let error = decode_instruction(0x294F800080008000);
214 assert_matches!(error, Err(VirtualMachineError::InvalidOp1Reg(3)));
215 assert_eq!(
216 error.unwrap_err().to_string(),
217 "Invalid op1_register value: 3"
218 )
219 }
220
221 #[test]
222 fn invalid_pc_update() {
223 let error = decode_instruction(0x29A8800080008000);
224 assert_matches!(error, Err(VirtualMachineError::InvalidPcUpdate(3)));
225 assert_eq!(error.unwrap_err().to_string(), "Invalid pc_update value: 3")
226 }
227
228 #[test]
229 fn invalid_res_logic() {
230 let error = decode_instruction(0x2968800080008000);
231 assert_matches!(error, Err(VirtualMachineError::InvalidRes(3)));
232 assert_eq!(error.unwrap_err().to_string(), "Invalid res value: 3")
233 }
234
235 #[test]
236 fn invalid_opcode() {
237 let error = decode_instruction(0x3948800080008000);
238 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(3)));
239 assert_eq!(error.unwrap_err().to_string(), "Invalid opcode value: 3")
240 }
241
242 #[test]
243 fn invalid_ap_update() {
244 let error = decode_instruction(0x2D48800080008000);
245 assert_matches!(error, Err(VirtualMachineError::InvalidApUpdate(3)));
246 assert_eq!(error.unwrap_err().to_string(), "Invalid ap_update value: 3")
247 }
248
249 #[test]
250 fn decode_flags_nop_add_jmp_add_imm_fp_fp() {
251 let inst = decode_instruction(0x04A7800080008000).unwrap();
257 assert_matches!(inst.dst_register, Register::FP);
258 assert_matches!(inst.op0_register, Register::FP);
259 assert_matches!(inst.op1_addr, Op1Addr::Imm);
260 assert_matches!(inst.res, Res::Add);
261 assert_matches!(inst.pc_update, PcUpdate::Jump);
262 assert_matches!(inst.ap_update, ApUpdate::Add);
263 assert_matches!(inst.opcode, Opcode::NOp);
264 assert_matches!(inst.fp_update, FpUpdate::Regular);
265 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
266 }
267
268 #[test]
269 fn decode_flags_nop_add1_jmp_rel_mul_fp_ap_ap() {
270 let inst = decode_instruction(0x0948800080008000).unwrap();
276 assert_matches!(inst.dst_register, Register::AP);
277 assert_matches!(inst.op0_register, Register::AP);
278 assert_matches!(inst.op1_addr, Op1Addr::FP);
279 assert_matches!(inst.res, Res::Mul);
280 assert_matches!(inst.pc_update, PcUpdate::JumpRel);
281 assert_matches!(inst.ap_update, ApUpdate::Add1);
282 assert_matches!(inst.opcode, Opcode::NOp);
283 assert_matches!(inst.fp_update, FpUpdate::Regular);
284 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
285 }
286
287 #[test]
288 fn decode_flags_assrt_add_regular_mul_ap_ap_ap() {
289 let inst = decode_instruction(0x4850800080008000).unwrap();
295 assert_matches!(inst.dst_register, Register::AP);
296 assert_matches!(inst.op0_register, Register::AP);
297 assert_matches!(inst.op1_addr, Op1Addr::AP);
298 assert_matches!(inst.res, Res::Mul);
299 assert_matches!(inst.pc_update, PcUpdate::Regular);
300 assert_matches!(inst.ap_update, ApUpdate::Add1);
301 assert_matches!(inst.opcode, Opcode::AssertEq);
302 assert_matches!(inst.fp_update, FpUpdate::Regular);
303 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
304 }
305
306 #[test]
307 fn decode_flags_assrt_add2_jnz_uncon_op0_ap_ap() {
308 let inst = decode_instruction(0x4200800080008000).unwrap();
314 assert_matches!(inst.dst_register, Register::AP);
315 assert_matches!(inst.op0_register, Register::AP);
316 assert_matches!(inst.op1_addr, Op1Addr::Op0);
317 assert_matches!(inst.res, Res::Unconstrained);
318 assert_matches!(inst.pc_update, PcUpdate::Jnz);
319 assert_matches!(inst.ap_update, ApUpdate::Regular);
320 assert_matches!(inst.opcode, Opcode::AssertEq);
321 assert_matches!(inst.fp_update, FpUpdate::Regular);
322 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
323 }
324
325 #[test]
326 fn decode_flags_nop_regu_regu_op1_op0_ap_ap() {
327 let inst = decode_instruction(0x0000800080008000).unwrap();
333 assert_matches!(inst.dst_register, Register::AP);
334 assert_matches!(inst.op0_register, Register::AP);
335 assert_matches!(inst.op1_addr, Op1Addr::Op0);
336 assert_matches!(inst.res, Res::Op1);
337 assert_matches!(inst.pc_update, PcUpdate::Regular);
338 assert_matches!(inst.ap_update, ApUpdate::Regular);
339 assert_matches!(inst.opcode, Opcode::NOp);
340 assert_matches!(inst.fp_update, FpUpdate::Regular);
341 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
342 }
343
344 #[test]
345 fn decode_offset_negative() {
346 let inst = decode_instruction(0x0000800180007FFF).unwrap();
352 assert_eq!(inst.off0, -1);
353 assert_eq!(inst.off1, 0);
354 assert_eq!(inst.off2, 1);
355 }
356
357 #[test]
358 fn decode_ret_cairo_standard() {
359 let inst = decode_instruction(0x208b7fff7fff7ffe).unwrap();
365 assert_matches!(inst.opcode, Opcode::Ret);
366 assert_matches!(inst.off0, -2);
367 assert_matches!(inst.off1, -1);
368 assert_matches!(inst.dst_register, Register::FP);
369 assert_matches!(inst.op0_register, Register::FP);
370 assert_matches!(inst.op1_addr, Op1Addr::FP);
371 assert_matches!(inst.res, Res::Op1);
372 assert_matches!(inst.pc_update, PcUpdate::Jump);
373 assert_matches!(inst.ap_update, ApUpdate::Regular);
374 assert_matches!(inst.fp_update, FpUpdate::Dst);
375 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
376 }
377
378 #[test]
379 fn decode_call_cairo_standard() {
380 let inst = decode_instruction(0x1104800180018000).unwrap();
386 assert_matches!(inst.opcode, Opcode::Call);
387 assert_matches!(inst.off0, 0);
388 assert_matches!(inst.off1, 1);
389 assert_matches!(inst.dst_register, Register::AP);
390 assert_matches!(inst.op0_register, Register::AP);
391 assert_matches!(inst.op1_addr, Op1Addr::Imm);
392 assert_matches!(inst.res, Res::Op1);
393 assert_matches!(inst.pc_update, PcUpdate::JumpRel);
394 assert_matches!(inst.ap_update, ApUpdate::Add2);
395 assert_matches!(inst.fp_update, FpUpdate::APPlus2);
396 assert_matches!(inst.opcode_extension, OpcodeExtension::Stone);
397 }
398
399 #[test]
400 fn decode_ret_opcode_error() {
401 let error = decode_instruction(0x208b7fff7fff7fff);
407 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(2)));
408 }
409
410 #[test]
411 fn decode_call_opcode_error() {
412 let error = decode_instruction(0x1104800180018001);
418 assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1)));
419 }
420
421 #[test]
422 fn decode_opcode_extension_clash() {
423 let error = decode_instruction(0x9008800180018001);
429 assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104)));
430 }
431
432 #[test]
433 fn decode_blake_imm() {
434 let error = decode_instruction(0x8004800180018001);
440 assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4)));
441 }
442
443 #[test]
444 fn decode_blake() {
445 let inst = decode_instruction(0x8813800180018001).unwrap();
451 assert_matches!(inst.opcode, Opcode::NOp);
452 assert_matches!(inst.off0, 1);
453 assert_matches!(inst.off1, 1);
454 assert_matches!(inst.dst_register, Register::FP);
455 assert_matches!(inst.op0_register, Register::FP);
456 assert_matches!(inst.op1_addr, Op1Addr::AP);
457 assert_matches!(inst.res, Res::Op1);
458 assert_matches!(inst.pc_update, PcUpdate::Regular);
459 assert_matches!(inst.ap_update, ApUpdate::Add1);
460 assert_matches!(inst.fp_update, FpUpdate::Regular);
461 assert_matches!(inst.opcode_extension, OpcodeExtension::Blake);
462 }
463
464 #[test]
465 fn decode_invalid_opcode_extension_error() {
466 let error = decode_instruction(0x39104800180018000);
472 assert_matches!(error, Err(VirtualMachineError::InvalidOpcodeExtension(7)));
473 }
474
475 #[test]
476 fn decode_qm31_operation_invalid_flags() {
477 let error = decode_instruction(0x19108800180018001);
483 assert_matches!(
484 error,
485 Err(VirtualMachineError::InvalidQM31AddMulFlags(0x1108))
486 );
487 }
488
489 #[test]
490 fn decode_qm31_operation() {
491 let inst = decode_instruction(0x1c048800180018001).unwrap();
497 assert_matches!(inst.opcode, Opcode::AssertEq);
498 assert_matches!(inst.off0, 1);
499 assert_matches!(inst.off1, 1);
500 assert_matches!(inst.dst_register, Register::AP);
501 assert_matches!(inst.op0_register, Register::AP);
502 assert_matches!(inst.op1_addr, Op1Addr::FP);
503 assert_matches!(inst.res, Res::Mul);
504 assert_matches!(inst.pc_update, PcUpdate::Regular);
505 assert_matches!(inst.ap_update, ApUpdate::Regular);
506 assert_matches!(inst.fp_update, FpUpdate::Regular);
507 assert_matches!(inst.opcode_extension, OpcodeExtension::QM31Operation);
508 }
509}