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#[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#[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#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
91#[derive(Debug, Clone)]
92pub struct AssembledCairoProgram {
93 #[cfg_attr(
95 feature = "serde",
96 serde(serialize_with = "serialize_big_ints", deserialize_with = "deserialize_big_ints")
97 )]
98 pub bytecode: Vec<BigInt>,
99 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
290struct 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 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}