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