Skip to main content

cairo_vm/vm/decoding/
decoder.rs

1use crate::{
2    types::instruction::{
3        ApUpdate, FpUpdate, Instruction, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Register, Res,
4    },
5    vm::errors::vm_errors::VirtualMachineError,
6};
7
8// opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
9//  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
10
11/// Decodes an instruction. The encoding is little endian, so flags go from bit 127 to 48.
12/// The bits 63 and beyond are reserved for the opcode extension.
13/// opcode_extension_num=0 means the instruction is a Stone instruction.
14/// opcode_extension_num>0 is for new Stwo opcodes.
15pub 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    // Flags start on the 48th bit.
33    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    // Grab offsets and convert them from little endian format.
40    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    // Grab flags
45    let flags = encoded_instr >> FLAGS_OFFSET;
46    // Grab individual flags
47    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    // Grab opcode_extension
56    let opcode_extension_num = encoded_instr >> OPCODE_EXTENSION_OFF;
57
58    // Match each flag to its corresponding enum value
59    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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
252        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
253        //            Stone|      NOp|      ADD|     JUMP|      ADD|    IMM|     FP|     FP
254        //                0   0  0  0      0  1   0  0  1      0  1 0  0  1       1       1
255        //  0000 0100 1010 0111 = 0x04A7; offx = 0
256        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
271        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
272        //            Stone|      NOp|     ADD1| JUMP_REL|      MUL|     FP|     AP|     AP
273        //                0   0  0  0      1  0   0  1  0      1  0 0  1  0       0       0
274        //  0000 1001 0100 1000 = 0x0948; offx = 0
275        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
290        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
291        //            Stone| ASSRT_EQ|      ADD|  REGULAR|      MUL|     AP|     AP|     AP
292        //                0   1  0  0      1  0   0  0  0      1  0 1  0  0       0       0
293        //  0100 1000 0101 0000 = 0x4850; offx = 0
294        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
309        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
310        //            Stone| ASSRT_EQ|     ADD2|      JNZ|UNCONSTRD|    OP0|     AP|     AP
311        //                0   1  0  0      0  0   1  0  0      0  0 0  0  0       0       0
312        //  0100 0010 0000 0000 = 0x4200; offx = 0
313        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
328        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
329        //            Stone|      NOP|  REGULAR|  REGULAR|      OP1|    OP0|     AP|     AP
330        //                0   0  0  0      0  0   0  0  0      0  0 0  0  0       0       0
331        //  0000 0000 0000 0000 = 0x0000; offx = 0
332        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
347        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
348        //            Stone|      NOP|  REGULAR|  REGULAR|      OP1|    OP0|     AP|     AP
349        //                0   0  0  0      0  0   0  0  0      0  0 0  0  0       0       0
350        //  0000 0000 0000 0000 = 0x0000; offx = 0
351        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
360        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
361        //            Stone|      RET|  REGULAR|     JUMP|      Op1|     FP|     FP|     FP
362        //                0   0  1  0      0  0   0  0  1      0  0 0  1  0       1       1
363        //  0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1
364        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
381        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
382        //            Stone|     CALL|  Regular|  JumpRel|      Op1|     FP|     FP|     FP
383        //                0   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
384        //  0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1
385        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
402        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
403        //            Stone|      RET|  REGULAR|     JUMP|      Op1|     FP|     FP|     FP
404        //                0   0  1  0      0  0   0  0  1      0  0 0  1  0       1       1
405        //  0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1
406        let error = decode_instruction(0x208b7fff7fff7fff);
407        assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(2)));
408    }
409
410    #[test]
411    fn decode_call_opcode_error() {
412        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
413        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
414        //            Stone|     CALL|  REGULAR|  JumpRel|      Op1|    IMM|     AP|     AP
415        //                0   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
416        //  0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1
417        let error = decode_instruction(0x1104800180018001);
418        assert_matches!(error, Err(VirtualMachineError::InvalidOpcode(1)));
419    }
420
421    #[test]
422    fn decode_opcode_extension_clash() {
423        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
424        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
425        //            Blake|     CALL|  REGULAR|  REGULAR|      Op1|     FP|     AP|     AP
426        //                1   0  0  1      0  0   0  0  0      0  0 0  1  0       0       0
427        //  1001 0000 0000 1000 = 0x9008; off0 = 1, off1 = 1
428        let error = decode_instruction(0x9008800180018001);
429        assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4104)));
430    }
431
432    #[test]
433    fn decode_blake_imm() {
434        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
435        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
436        //            Blake|      NOP|  REGULAR|  REGULAR|      Op1|    IMM|     AP|     AP
437        //                1   0  0  0      0  0   0  0  0      0  0 0  0  1       0       0
438        //  1000 0000 0000 0100 = 0x8004; off0 = 1, off1 = 1
439        let error = decode_instruction(0x8004800180018001);
440        assert_matches!(error, Err(VirtualMachineError::InvalidBlake2sFlags(4)));
441    }
442
443    #[test]
444    fn decode_blake() {
445        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
446        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
447        //            Blake|      NOP|     ADD1|  REGULAR|      Op1|     AP|     FP|     FP
448        //                1   0  0  0      1  0   0  0  0      0  0 1  0  0       1       1
449        //  1000 1000 0001 0011 = 0x8813; off0 = 1, off1 = 1
450        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
467        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
468        //              ???|     CALL|     Add2|  JumpRel|      Op1|    IMM|     FP|     FP
469        //          1  1  1   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
470        //  0011 1001 0001 0000 0100 = 0x39104; off0 = 0, off1 = 1
471        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
478        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
479        //    QM31Operation|     CALL|  REGULAR|  JumpRel|      Op1|     FP|     AP|     AP
480        //             1  1   0  0  1      0  0   0  1  0      0  0 0  1  0       0       0
481        //  1 1001 0001 0000 1000 = 0x19108; off0 = 1, off1 = 1
482        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
492        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
493        //    QM31Operation|ASSERT_EQ|  REGULAR|  REGULAR|      MUL|     FP|     AP|     AP
494        //             1  1   1  0  0      0  0   0  0  0      1  0 0  1  0       0       0
495        //  1 1100 0000 0100 1000 = 0x1c048; off0 = 1, off1 = 1
496        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}