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 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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
263        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
264        //            Stone|      NOp|      ADD|     JUMP|      ADD|    IMM|     FP|     FP
265        //                0   0  0  0      0  1   0  0  1      0  1 0  0  1       1       1
266        //  0000 0100 1010 0111 = 0x04A7; offx = 0
267        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
283        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
284        //            Stone|      NOp|     ADD1| JUMP_REL|      MUL|     FP|     AP|     AP
285        //                0   0  0  0      1  0   0  1  0      1  0 0  1  0       0       0
286        //  0000 1001 0100 1000 = 0x0948; offx = 0
287        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
303        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
304        //            Stone| ASSRT_EQ|      ADD|  REGULAR|      MUL|     AP|     AP|     AP
305        //                0   1  0  0      1  0   0  0  0      1  0 1  0  0       0       0
306        //  0100 1000 0101 0000 = 0x4850; offx = 0
307        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
323        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
324        //            Stone| ASSRT_EQ|     ADD2|      JNZ|UNCONSTRD|    OP0|     AP|     AP
325        //                0   1  0  0      0  0   1  0  0      0  0 0  0  0       0       0
326        //  0100 0010 0000 0000 = 0x4200; offx = 0
327        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
343        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
344        //            Stone|      NOP|  REGULAR|  REGULAR|      OP1|    OP0|     AP|     AP
345        //                0   0  0  0      0  0   0  0  0      0  0 0  0  0       0       0
346        //  0000 0000 0000 0000 = 0x0000; offx = 0
347        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
363        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
364        //            Stone|      NOP|  REGULAR|  REGULAR|      OP1|    OP0|     AP|     AP
365        //                0   0  0  0      0  0   0  0  0      0  0 0  0  0       0       0
366        //  0000 0000 0000 0000 = 0x0000; offx = 0
367        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
377        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
378        //            Stone|      RET|  REGULAR|     JUMP|      Op1|     FP|     FP|     FP
379        //                0   0  1  0      0  0   0  0  1      0  0 0  1  0       1       1
380        //  0010 0000 1000 1011 = 0x208b; off0 = -2, off1 = -1
381        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
399        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
400        //            Stone|     CALL|  Regular|  JumpRel|      Op1|     FP|     FP|     FP
401        //                0   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
402        //  0001 0001 0000 0100 = 0x1104; off0 = 0, off1 = 1
403        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
421        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
422        //            Stone|      RET|  REGULAR|     JUMP|      Op1|     FP|     FP|     FP
423        //                0   0  1  0      0  0   0  0  1      0  0 0  1  0       1       1
424        //  0010 0000 1000 1011 = 0x208b; off0 = -1, off1 = -1
425        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
433        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
434        //            Stone|     CALL|  REGULAR|  JumpRel|      Op1|    IMM|     AP|     AP
435        //                0   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
436        //  0001 0001 0000 0100 = 0x1104; off0 = 1, off1 = 1
437        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
445        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
446        //            Blake|     CALL|  REGULAR|  REGULAR|      Op1|     FP|     AP|     AP
447        //                1   0  0  1      0  0   0  0  0      0  0 0  1  0       0       0
448        //  1001 0000 0000 1000 = 0x9008; off0 = 1, off1 = 1
449        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
457        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
458        //            Blake|      NOP|  REGULAR|  REGULAR|      Op1|    IMM|     AP|     AP
459        //                1   0  0  0      0  0   0  0  0      0  0 0  0  1       0       0
460        //  1000 0000 0000 0100 = 0x8004; off0 = 1, off1 = 1
461        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
469        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
470        //            Blake|      NOP|     ADD1|  REGULAR|      Op1|     AP|     FP|     FP
471        //                1   0  0  0      1  0   0  0  0      0  0 1  0  0       1       1
472        //  1000 1000 0001 0011 = 0x8813; off0 = 1, off1 = 1
473        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
491        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
492        //              ???|     CALL|     Add2|  JumpRel|      Op1|    IMM|     FP|     FP
493        //          1  1  1   0  0  1      0  0   0  1  0      0  0 0  0  1       0       0
494        //  0011 1001 0001 0000 0100 = 0x39104; off0 = 0, off1 = 1
495        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
503        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
504        //    QM31Operation|     CALL|  REGULAR|  JumpRel|      Op1|     FP|     AP|     AP
505        //             1  1   0  0  1      0  0   0  1  0      0  0 0  1  0       0       0
506        //  1 1001 0001 0000 1000 = 0x19108; off0 = 1, off1 = 1
507        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        // opcode_extension|   opcode|ap_update|pc_update|res_logic|op1_src|op0_reg|dst_reg
518        //  79 ... 17 16 15| 14 13 12|    11 10|  9  8  7|     6  5|4  3  2|      1|      0
519        //    QM31Operation|ASSERT_EQ|  REGULAR|  REGULAR|      MUL|     FP|     AP|     AP
520        //             1  1   1  0  0      0  0   0  0  0      1  0 0  1  0       0       0
521        //  1 1100 0000 0100 1000 = 0x1c048; off0 = 1, off1 = 1
522        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}