cairo_lang_casm/
instructions.rs

1#[cfg(not(feature = "std"))]
2use alloc::{vec, vec::Vec};
3use core::fmt::Display;
4
5use crate::hints::{Hint, PythonicHint};
6use crate::operand::{CellRef, DerefOrImmediate, ResOperand};
7
8#[cfg(test)]
9#[path = "instructions_test.rs"]
10mod test;
11
12// An enum of Cairo instructions.
13#[derive(Debug, Eq, PartialEq, Clone)]
14pub enum InstructionBody {
15    AddAp(AddApInstruction),
16    AssertEq(AssertEqInstruction),
17    QM31AssertEq(AssertEqInstruction),
18    Call(CallInstruction),
19    Jnz(JnzInstruction),
20    Jump(JumpInstruction),
21    Ret(RetInstruction),
22    Blake2sCompress(Blake2sCompressInstruction),
23}
24impl InstructionBody {
25    pub fn op_size(&self) -> usize {
26        // TODO(spapini): Make this correct.
27        match self {
28            InstructionBody::AddAp(insn) => insn.op_size(),
29            InstructionBody::AssertEq(insn) | InstructionBody::QM31AssertEq(insn) => insn.op_size(),
30            InstructionBody::Call(insn) => insn.op_size(),
31            InstructionBody::Jump(insn) => insn.op_size(),
32            InstructionBody::Jnz(insn) => insn.op_size(),
33            InstructionBody::Ret(insn) => insn.op_size(),
34            InstructionBody::Blake2sCompress(insn) => insn.op_size(),
35        }
36    }
37}
38impl Display for InstructionBody {
39    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40        match self {
41            InstructionBody::AddAp(insn) => write!(f, "{insn}",),
42            InstructionBody::AssertEq(insn) => write!(f, "{insn}",),
43            InstructionBody::QM31AssertEq(insn) => write!(f, "{{QM31}} {insn}",),
44            InstructionBody::Call(insn) => write!(f, "{insn}",),
45            InstructionBody::Jnz(insn) => write!(f, "{insn}",),
46            InstructionBody::Jump(insn) => write!(f, "{insn}",),
47            InstructionBody::Ret(insn) => write!(f, "{insn}",),
48            InstructionBody::Blake2sCompress(insn) => write!(f, "{insn}",),
49        }
50    }
51}
52
53/// Represents an instruction, including the ap++ flag (inc_ap).
54#[derive(Debug, Eq, PartialEq, Clone)]
55pub struct Instruction {
56    pub body: InstructionBody,
57    pub inc_ap: bool,
58    pub hints: Vec<Hint>,
59}
60impl Instruction {
61    pub fn new(body: InstructionBody, inc_ap: bool) -> Self {
62        Self { body, inc_ap, hints: vec![] }
63    }
64}
65
66impl Display for Instruction {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        for hint in &self.hints {
69            let hint_str = hint.get_pythonic_hint();
70            // Skip leading and trailing space if hint starts with `\n`.
71            if hint_str.starts_with('\n') {
72                writeln!(f, "%{{{hint_str}%}}")
73            } else {
74                writeln!(f, "%{{ {hint_str} %}}")
75            }?
76        }
77
78        write!(f, "{}", self.body)?;
79        if self.inc_ap {
80            write!(f, ", ap++")?
81        };
82        Ok(())
83    }
84}
85
86/// Represents a call instruction "call rel/abs target".
87#[derive(Debug, Eq, PartialEq, Clone)]
88pub struct CallInstruction {
89    pub target: DerefOrImmediate,
90    pub relative: bool,
91}
92impl Display for CallInstruction {
93    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94        write!(f, "call {} {}", if self.relative { "rel" } else { "abs" }, self.target,)
95    }
96}
97impl CallInstruction {
98    pub fn op_size(&self) -> usize {
99        match &self.target {
100            DerefOrImmediate::Deref(_) => 1,
101            DerefOrImmediate::Immediate(_) => 2,
102        }
103    }
104}
105
106/// Represents the InstructionBody "jmp rel/abs target".
107#[derive(Debug, Eq, PartialEq, Clone)]
108pub struct JumpInstruction {
109    pub target: DerefOrImmediate,
110    pub relative: bool,
111}
112impl JumpInstruction {
113    pub fn op_size(&self) -> usize {
114        match &self.target {
115            DerefOrImmediate::Deref(_) => 1,
116            DerefOrImmediate::Immediate(_) => 2,
117        }
118    }
119}
120impl Display for JumpInstruction {
121    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122        write!(f, "jmp {} {}", if self.relative { "rel" } else { "abs" }, self.target,)
123    }
124}
125
126/// Represents the InstructionBody "jmp rel <jump_offset> if condition != 0".
127#[derive(Debug, Eq, PartialEq, Clone)]
128pub struct JnzInstruction {
129    pub jump_offset: DerefOrImmediate,
130    pub condition: CellRef,
131}
132impl JnzInstruction {
133    pub fn op_size(&self) -> usize {
134        match &self.jump_offset {
135            DerefOrImmediate::Deref(_) => 1,
136            DerefOrImmediate::Immediate(_) => 2,
137        }
138    }
139}
140impl Display for JnzInstruction {
141    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142        write!(f, "jmp rel {} if {} != 0", self.jump_offset, self.condition)
143    }
144}
145
146/// Returns the size of instruction based on whether the res operand includes an immediate or not.
147pub fn op_size_based_on_res_operands(operand: &ResOperand) -> usize {
148    match operand {
149        ResOperand::Deref(_) => 1,
150        ResOperand::DoubleDeref(_, _) => 1,
151        ResOperand::Immediate(_) => 2,
152        ResOperand::BinOp(op) => match op.b {
153            DerefOrImmediate::Immediate(_) => 2,
154            DerefOrImmediate::Deref(_) => 1,
155        },
156    }
157}
158
159/// Represents the InstructionBody "a = b" for two operands a, b.
160#[derive(Debug, Eq, PartialEq, Clone)]
161pub struct AssertEqInstruction {
162    pub a: CellRef,
163    pub b: ResOperand,
164}
165impl AssertEqInstruction {
166    pub fn op_size(&self) -> usize {
167        op_size_based_on_res_operands(&self.b)
168    }
169}
170impl Display for AssertEqInstruction {
171    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172        write!(f, "{} = {}", self.a, self.b)
173    }
174}
175
176/// Represents a return instruction, "ret".
177#[derive(Debug, Eq, PartialEq, Clone)]
178pub struct RetInstruction {}
179impl Display for RetInstruction {
180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181        write!(f, "ret")
182    }
183}
184
185impl RetInstruction {
186    pub fn op_size(&self) -> usize {
187        1
188    }
189}
190
191/// Represents the InstructionBody "ap += op" for a given operand op.
192#[derive(Debug, Eq, PartialEq, Clone)]
193pub struct AddApInstruction {
194    pub operand: ResOperand,
195}
196impl AddApInstruction {
197    pub fn op_size(&self) -> usize {
198        op_size_based_on_res_operands(&self.operand)
199    }
200}
201impl Display for AddApInstruction {
202    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203        write!(f, "ap += {}", self.operand)
204    }
205}
206
207/// Represents a blake2s instruction, "blake2s".
208#[derive(Debug, Eq, PartialEq, Clone)]
209pub struct Blake2sCompressInstruction {
210    pub state: CellRef,
211    pub byte_count: CellRef,
212    pub message: CellRef,
213    pub finalize: bool,
214}
215impl Blake2sCompressInstruction {
216    pub fn op_size(&self) -> usize {
217        1
218    }
219}
220impl Display for Blake2sCompressInstruction {
221    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
222        write!(
223            f,
224            "blake2s[state={}, message={}, byte_count={}, finalize={}] => [ap + 0]",
225            self.state, self.message, self.byte_count, self.finalize
226        )
227    }
228}