cairo_lang_casm/
assembler.rs

1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use num_bigint::{BigInt, ToBigInt};
5
6use crate::hints::Hint;
7use crate::instructions::{Instruction, InstructionBody};
8use crate::operand::{DerefOrImmediate, Operation, Register, ResOperand};
9
10#[cfg(test)]
11#[path = "assembler_test.rs"]
12mod test;
13
14/// Cairo instruction structure flags.
15#[derive(Debug, Eq, PartialEq)]
16pub enum Op1Addr {
17    Imm,
18    AP,
19    FP,
20    Op0,
21}
22#[derive(Debug, Eq, PartialEq)]
23pub enum Res {
24    Op1,
25    Add,
26    Mul,
27    Unconstrained,
28}
29#[derive(Debug, Eq, PartialEq)]
30pub enum PcUpdate {
31    Regular,
32    Jump,
33    JumpRel,
34    Jnz,
35}
36
37#[derive(Debug, Eq, PartialEq)]
38pub enum ApUpdate {
39    Regular,
40    Add,
41    Add1,
42    Add2,
43}
44
45#[derive(Debug, Eq, PartialEq)]
46pub enum FpUpdate {
47    Regular,
48    ApPlus2,
49    Dst,
50}
51
52#[derive(Debug, Eq, PartialEq)]
53pub enum Opcode {
54    Nop,
55    AssertEq,
56    Call,
57    Ret,
58}
59
60#[derive(Debug, Eq, PartialEq)]
61pub enum OpcodeExtension {
62    Stone,
63    Blake2s,
64    Blake2sFinalize,
65    QM31,
66}
67
68/// The low level representation of a cairo instruction.
69#[derive(Debug, Eq, PartialEq)]
70pub struct InstructionRepr {
71    pub off0: i16,
72    pub off1: i16,
73    pub off2: i16,
74    pub imm: Option<BigInt>,
75    pub dst_register: Register,
76    pub op0_register: Register,
77    pub op1_addr: Op1Addr,
78    pub res: Res,
79    pub pc_update: PcUpdate,
80    pub ap_update: ApUpdate,
81    pub fp_update: FpUpdate,
82    pub opcode: Opcode,
83    pub opcode_extension: OpcodeExtension,
84}
85
86#[cfg(feature = "serde")]
87use cairo_lang_utils::bigint::{deserialize_big_ints, serialize_big_ints};
88
89/// An assembled representation of a cairo program.
90#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
91#[derive(Debug, Clone)]
92pub struct AssembledCairoProgram {
93    /// The bytecode of the program.
94    #[cfg_attr(
95        feature = "serde",
96        serde(serialize_with = "serialize_big_ints", deserialize_with = "deserialize_big_ints")
97    )]
98    pub bytecode: Vec<BigInt>,
99    /// The list of hints, and the instruction index they refer to.
100    pub hints: Vec<(usize, Vec<Hint>)>,
101}
102
103impl Instruction {
104    pub fn assemble(&self) -> InstructionRepr {
105        match &self.body {
106            InstructionBody::AddAp(insn) => {
107                assert!(!self.inc_ap, "An add_ap instruction cannot have an ap++.");
108                let res = insn.operand.to_res_description();
109                InstructionRepr {
110                    off0: -1,
111                    off1: res.off1,
112                    off2: res.off2,
113                    imm: res.imm,
114                    dst_register: Register::FP,
115                    op0_register: res.op0_register,
116                    op1_addr: res.op1_addr,
117                    res: res.res,
118                    pc_update: PcUpdate::Regular,
119                    ap_update: ApUpdate::Add,
120                    fp_update: FpUpdate::Regular,
121                    opcode: Opcode::Nop,
122                    opcode_extension: OpcodeExtension::Stone,
123                }
124            }
125            InstructionBody::AssertEq(insn) => {
126                let res = insn.b.to_res_description();
127                InstructionRepr {
128                    off0: insn.a.offset,
129                    off1: res.off1,
130                    off2: res.off2,
131                    imm: res.imm,
132                    dst_register: insn.a.register,
133                    op0_register: res.op0_register,
134                    op1_addr: res.op1_addr,
135                    res: res.res,
136                    pc_update: PcUpdate::Regular,
137                    ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
138                    fp_update: FpUpdate::Regular,
139                    opcode: Opcode::AssertEq,
140                    opcode_extension: OpcodeExtension::Stone,
141                }
142            }
143            InstructionBody::QM31AssertEq(insn) => {
144                let res = insn.b.to_res_description();
145                InstructionRepr {
146                    off0: insn.a.offset,
147                    off1: res.off1,
148                    off2: res.off2,
149                    imm: res.imm,
150                    dst_register: insn.a.register,
151                    op0_register: res.op0_register,
152                    op1_addr: res.op1_addr,
153                    res: res.res,
154                    pc_update: PcUpdate::Regular,
155                    ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
156                    fp_update: FpUpdate::Regular,
157                    opcode: Opcode::AssertEq,
158                    opcode_extension: OpcodeExtension::QM31,
159                }
160            }
161            InstructionBody::Call(insn) => {
162                assert!(!self.inc_ap, "A call instruction cannot have an ap++.");
163                let res = insn.target.to_res_description();
164                InstructionRepr {
165                    off0: 0,
166                    off1: 1,
167                    off2: res.off2,
168                    imm: res.imm,
169                    dst_register: Register::AP,
170                    op0_register: Register::AP,
171                    op1_addr: res.op1_addr,
172                    res: Res::Op1,
173                    pc_update: if insn.relative { PcUpdate::JumpRel } else { PcUpdate::Jump },
174                    ap_update: ApUpdate::Add2,
175                    fp_update: FpUpdate::ApPlus2,
176                    opcode: Opcode::Call,
177                    opcode_extension: OpcodeExtension::Stone,
178                }
179            }
180            InstructionBody::Jump(insn) => {
181                let res = insn.target.to_res_description();
182                InstructionRepr {
183                    off0: -1,
184                    off1: res.off1,
185                    off2: res.off2,
186                    imm: res.imm,
187                    dst_register: Register::FP,
188                    op0_register: Register::FP,
189                    op1_addr: res.op1_addr,
190                    res: Res::Op1,
191                    pc_update: if insn.relative { PcUpdate::JumpRel } else { PcUpdate::Jump },
192                    ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
193                    fp_update: FpUpdate::Regular,
194                    opcode: Opcode::Nop,
195                    opcode_extension: OpcodeExtension::Stone,
196                }
197            }
198            InstructionBody::Jnz(insn) => {
199                let res = insn.jump_offset.to_res_description();
200                InstructionRepr {
201                    off0: insn.condition.offset,
202                    off1: -1,
203                    off2: res.off2,
204                    imm: res.imm,
205                    dst_register: insn.condition.register,
206                    op0_register: Register::FP,
207                    op1_addr: res.op1_addr,
208                    res: Res::Unconstrained,
209                    pc_update: PcUpdate::Jnz,
210                    ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
211                    fp_update: FpUpdate::Regular,
212                    opcode: Opcode::Nop,
213                    opcode_extension: OpcodeExtension::Stone,
214                }
215            }
216            InstructionBody::Ret(_) => {
217                assert!(!self.inc_ap);
218                InstructionRepr {
219                    off0: -2,
220                    off1: -1,
221                    off2: -1,
222                    imm: None,
223                    dst_register: Register::FP,
224                    op0_register: Register::FP,
225                    op1_addr: Op1Addr::FP,
226                    res: Res::Op1,
227                    pc_update: PcUpdate::Jump,
228                    ap_update: ApUpdate::Regular,
229                    fp_update: FpUpdate::Dst,
230                    opcode: Opcode::Ret,
231                    opcode_extension: OpcodeExtension::Stone,
232                }
233            }
234            InstructionBody::Blake2sCompress(insn) => {
235                assert!(self.inc_ap);
236                InstructionRepr {
237                    off0: insn.byte_count.offset,
238                    off1: insn.state.offset,
239                    off2: insn.message.offset,
240                    imm: None,
241                    dst_register: insn.byte_count.register,
242                    op0_register: insn.state.register,
243                    op1_addr: insn.message.register.to_op1_addr(),
244                    res: Res::Op1,
245                    pc_update: PcUpdate::Regular,
246                    ap_update: ApUpdate::Add1,
247                    fp_update: FpUpdate::Regular,
248                    opcode: Opcode::Nop,
249                    opcode_extension: if insn.finalize {
250                        OpcodeExtension::Blake2sFinalize
251                    } else {
252                        OpcodeExtension::Blake2s
253                    },
254                }
255            }
256        }
257    }
258}
259
260impl Register {
261    fn to_op1_addr(self) -> Op1Addr {
262        match self {
263            Register::AP => Op1Addr::AP,
264            Register::FP => Op1Addr::FP,
265        }
266    }
267}
268
269impl Operation {
270    fn to_res(&self) -> Res {
271        match self {
272            Operation::Add => Res::Add,
273            Operation::Mul => Res::Mul,
274        }
275    }
276}
277
278impl DerefOrImmediate {
279    fn to_res_operand(&self) -> ResOperand {
280        match self {
281            DerefOrImmediate::Deref(operand) => ResOperand::Deref(*operand),
282            DerefOrImmediate::Immediate(operand) => ResOperand::Immediate(operand.clone()),
283        }
284    }
285    fn to_res_description(&self) -> ResDescription {
286        self.to_res_operand().to_res_description()
287    }
288}
289
290/// The part of the instruction describing the res operand.
291struct ResDescription {
292    off1: i16,
293    off2: i16,
294    imm: Option<BigInt>,
295    op0_register: Register,
296    op1_addr: Op1Addr,
297    res: Res,
298}
299
300impl ResOperand {
301    fn to_res_description(&self) -> ResDescription {
302        match self {
303            ResOperand::Deref(operand) => ResDescription {
304                off1: -1,
305                off2: operand.offset,
306                imm: None,
307                op0_register: Register::FP,
308                op1_addr: operand.register.to_op1_addr(),
309                res: Res::Op1,
310            },
311            ResOperand::DoubleDeref(operand, offset) => ResDescription {
312                off1: operand.offset,
313                off2: *offset,
314                imm: None,
315                op0_register: operand.register,
316                op1_addr: Op1Addr::Op0,
317                res: Res::Op1,
318            },
319            ResOperand::Immediate(operand) => ResDescription {
320                off1: -1,
321                off2: 1,
322                // TODO(alon): Change immediate to always work with bigint.
323                imm: operand.value.to_bigint(),
324                op0_register: Register::FP,
325                op1_addr: Op1Addr::Imm,
326                res: Res::Op1,
327            },
328            ResOperand::BinOp(operand) => {
329                let a_res = ResOperand::Deref(operand.a).to_res_description();
330                let b_res = operand.b.to_res_description();
331                ResDescription {
332                    off1: a_res.off2,
333                    off2: b_res.off2,
334                    imm: b_res.imm,
335                    op0_register: operand.a.register,
336                    op1_addr: match operand.b {
337                        DerefOrImmediate::Immediate(_) => Op1Addr::Imm,
338                        DerefOrImmediate::Deref(b) => b.register.to_op1_addr(),
339                    },
340                    res: operand.op.to_res(),
341                }
342            }
343        }
344    }
345}