cairo_vm/types/
instruction.rs

1use crate::Felt252;
2use num_traits::ToPrimitive;
3use serde::{Deserialize, Serialize};
4
5use crate::vm::decoding::decoder::decode_instruction;
6
7#[cfg(feature = "test_utils")]
8use arbitrary::Arbitrary;
9
10#[cfg_attr(feature = "test_utils", derive(Arbitrary))]
11#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
12pub enum Register {
13    AP,
14    FP,
15}
16
17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
18pub struct Instruction {
19    pub off0: isize,
20    pub off1: isize,
21    pub off2: isize,
22    pub dst_register: Register,
23    pub op0_register: Register,
24    pub op1_addr: Op1Addr,
25    pub res: Res,
26    pub pc_update: PcUpdate,
27    pub ap_update: ApUpdate,
28    pub fp_update: FpUpdate,
29    pub opcode: Opcode,
30    pub opcode_extension: OpcodeExtension,
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34pub enum Op1Addr {
35    Imm,
36    AP,
37    FP,
38    Op0,
39}
40
41#[derive(Copy, Clone, Debug, PartialEq, Eq)]
42pub enum Res {
43    Op1,
44    Add,
45    Mul,
46    Unconstrained,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub enum PcUpdate {
51    Regular,
52    Jump,
53    JumpRel,
54    Jnz,
55}
56
57#[derive(Copy, Clone, Debug, PartialEq, Eq)]
58pub enum ApUpdate {
59    Regular,
60    Add,
61    Add1,
62    Add2,
63}
64
65#[derive(Copy, Clone, Debug, PartialEq, Eq)]
66pub enum FpUpdate {
67    Regular,
68    APPlus2,
69    Dst,
70}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq)]
73pub enum Opcode {
74    NOp,
75    AssertEq,
76    Call,
77    Ret,
78}
79
80#[derive(Clone, Debug, Copy, PartialEq, Eq)]
81pub enum OpcodeExtension {
82    Stone,
83    Blake,
84    BlakeFinalize,
85    QM31Operation,
86}
87
88impl Instruction {
89    pub fn size(&self) -> usize {
90        match self.op1_addr {
91            Op1Addr::Imm => 2,
92            _ => 1,
93        }
94    }
95}
96
97// Returns True if the given instruction looks like a call instruction
98pub(crate) fn is_call_instruction(encoded_instruction: &Felt252) -> bool {
99    let encoded_u128_instruction = match encoded_instruction.to_u128() {
100        Some(num) => num,
101        None => return false,
102    };
103    let instruction = match decode_instruction(encoded_u128_instruction) {
104        Ok(inst) => inst,
105        Err(_) => return false,
106    };
107    instruction.res == Res::Op1
108        && (instruction.pc_update == PcUpdate::Jump || instruction.pc_update == PcUpdate::JumpRel)
109        && instruction.ap_update == ApUpdate::Add2
110        && instruction.fp_update == FpUpdate::APPlus2
111        && instruction.opcode == Opcode::Call
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[cfg(target_arch = "wasm32")]
119    use wasm_bindgen_test::*;
120
121    #[test]
122    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
123    fn is_call_instruction_true() {
124        let encoded_instruction = Felt252::from(1226245742482522112_i64);
125        assert!(is_call_instruction(&encoded_instruction));
126    }
127
128    #[test]
129    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
130    fn is_call_instruction_false() {
131        let encoded_instruction = Felt252::from(4612671187288031229_i64);
132        assert!(!is_call_instruction(&encoded_instruction));
133    }
134
135    #[test]
136    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
137    fn is_call_instruction_invalid() {
138        let encoded_instruction = Felt252::from(1u64 << 63);
139        assert!(!is_call_instruction(&encoded_instruction));
140    }
141
142    #[test]
143    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
144    fn instruction_size() {
145        let encoded_instruction = Felt252::from(1226245742482522112_i64);
146        let instruction = decode_instruction(encoded_instruction.to_u128().unwrap()).unwrap();
147        assert_eq!(instruction.size(), 2);
148    }
149}