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)]
62pub struct InstructionRepr {
63 pub off0: i16,
64 pub off1: i16,
65 pub off2: i16,
66 pub imm: Option<BigInt>,
67 pub dst_register: Register,
68 pub op0_register: Register,
69 pub op1_addr: Op1Addr,
70 pub res: Res,
71 pub pc_update: PcUpdate,
72 pub ap_update: ApUpdate,
73 pub fp_update: FpUpdate,
74 pub opcode: Opcode,
75}
76
77#[cfg(feature = "serde")]
78use cairo_lang_utils::bigint::{deserialize_big_ints, serialize_big_ints};
79
80#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
82#[derive(Debug, Clone)]
83pub struct AssembledCairoProgram {
84 #[cfg_attr(
86 feature = "serde",
87 serde(serialize_with = "serialize_big_ints", deserialize_with = "deserialize_big_ints")
88 )]
89 pub bytecode: Vec<BigInt>,
90 pub hints: Vec<(usize, Vec<Hint>)>,
92}
93
94impl Instruction {
95 pub fn assemble(&self) -> InstructionRepr {
96 match &self.body {
97 InstructionBody::AddAp(insn) => {
98 assert!(!self.inc_ap, "An add_ap instruction cannot have an ap++.");
99 let res = insn.operand.to_res_description();
100 InstructionRepr {
101 off0: -1,
102 off1: res.off1,
103 off2: res.off2,
104 imm: res.imm,
105 dst_register: Register::FP,
106 op0_register: res.op0_register,
107 op1_addr: res.op1_addr,
108 res: res.res,
109 pc_update: PcUpdate::Regular,
110 ap_update: ApUpdate::Add,
111 fp_update: FpUpdate::Regular,
112 opcode: Opcode::Nop,
113 }
114 }
115 InstructionBody::AssertEq(insn) => {
116 let res = insn.b.to_res_description();
117 InstructionRepr {
118 off0: insn.a.offset,
119 off1: res.off1,
120 off2: res.off2,
121 imm: res.imm,
122 dst_register: insn.a.register,
123 op0_register: res.op0_register,
124 op1_addr: res.op1_addr,
125 res: res.res,
126 pc_update: PcUpdate::Regular,
127 ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
128 fp_update: FpUpdate::Regular,
129 opcode: Opcode::AssertEq,
130 }
131 }
132 InstructionBody::Call(insn) => {
133 assert!(!self.inc_ap, "A call instruction cannot have an ap++.");
134 let res = insn.target.to_res_description();
135 InstructionRepr {
136 off0: 0,
137 off1: 1,
138 off2: res.off2,
139 imm: res.imm,
140 dst_register: Register::AP,
141 op0_register: Register::AP,
142 op1_addr: res.op1_addr,
143 res: Res::Op1,
144 pc_update: if insn.relative { PcUpdate::JumpRel } else { PcUpdate::Jump },
145 ap_update: ApUpdate::Add2,
146 fp_update: FpUpdate::ApPlus2,
147 opcode: Opcode::Call,
148 }
149 }
150 InstructionBody::Jump(insn) => {
151 let res = insn.target.to_res_description();
152 InstructionRepr {
153 off0: -1,
154 off1: res.off1,
155 off2: res.off2,
156 imm: res.imm,
157 dst_register: Register::FP,
158 op0_register: Register::FP,
159 op1_addr: res.op1_addr,
160 res: Res::Op1,
161 pc_update: if insn.relative { PcUpdate::JumpRel } else { PcUpdate::Jump },
162 ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
163 fp_update: FpUpdate::Regular,
164 opcode: Opcode::Nop,
165 }
166 }
167 InstructionBody::Jnz(insn) => {
168 let res = insn.jump_offset.to_res_description();
169 InstructionRepr {
170 off0: insn.condition.offset,
171 off1: -1,
172 off2: res.off2,
173 imm: res.imm,
174 dst_register: insn.condition.register,
175 op0_register: Register::FP,
176 op1_addr: res.op1_addr,
177 res: Res::Unconstrained,
178 pc_update: PcUpdate::Jnz,
179 ap_update: if self.inc_ap { ApUpdate::Add1 } else { ApUpdate::Regular },
180 fp_update: FpUpdate::Regular,
181 opcode: Opcode::Nop,
182 }
183 }
184 InstructionBody::Ret(_) => {
185 assert!(!self.inc_ap);
186 InstructionRepr {
187 off0: -2,
188 off1: -1,
189 off2: -1,
190 imm: None,
191 dst_register: Register::FP,
192 op0_register: Register::FP,
193 op1_addr: Op1Addr::FP,
194 res: Res::Op1,
195 pc_update: PcUpdate::Jump,
196 ap_update: ApUpdate::Regular,
197 fp_update: FpUpdate::Dst,
198 opcode: Opcode::Ret,
199 }
200 }
201 }
202 }
203}
204
205impl Register {
206 fn to_op1_addr(self) -> Op1Addr {
207 match self {
208 Register::AP => Op1Addr::AP,
209 Register::FP => Op1Addr::FP,
210 }
211 }
212}
213
214impl Operation {
215 fn to_res(&self) -> Res {
216 match self {
217 Operation::Add => Res::Add,
218 Operation::Mul => Res::Mul,
219 }
220 }
221}
222
223impl DerefOrImmediate {
224 fn to_res_operand(&self) -> ResOperand {
225 match self {
226 DerefOrImmediate::Deref(operand) => ResOperand::Deref(*operand),
227 DerefOrImmediate::Immediate(operand) => ResOperand::Immediate(operand.clone()),
228 }
229 }
230 fn to_res_description(&self) -> ResDescription {
231 self.to_res_operand().to_res_description()
232 }
233}
234
235struct ResDescription {
237 off1: i16,
238 off2: i16,
239 imm: Option<BigInt>,
240 op0_register: Register,
241 op1_addr: Op1Addr,
242 res: Res,
243}
244
245impl ResOperand {
246 fn to_res_description(&self) -> ResDescription {
247 match self {
248 ResOperand::Deref(operand) => ResDescription {
249 off1: -1,
250 off2: operand.offset,
251 imm: None,
252 op0_register: Register::FP,
253 op1_addr: operand.register.to_op1_addr(),
254 res: Res::Op1,
255 },
256 ResOperand::DoubleDeref(operand, offset) => ResDescription {
257 off1: operand.offset,
258 off2: *offset,
259 imm: None,
260 op0_register: operand.register,
261 op1_addr: Op1Addr::Op0,
262 res: Res::Op1,
263 },
264 ResOperand::Immediate(operand) => ResDescription {
265 off1: -1,
266 off2: 1,
267 imm: operand.value.to_bigint(),
269 op0_register: Register::FP,
270 op1_addr: Op1Addr::Imm,
271 res: Res::Op1,
272 },
273 ResOperand::BinOp(operand) => {
274 let a_res = ResOperand::Deref(operand.a).to_res_description();
275 let b_res = operand.b.to_res_description();
276 ResDescription {
277 off1: a_res.off2,
278 off2: b_res.off2,
279 imm: b_res.imm,
280 op0_register: operand.a.register,
281 op1_addr: match operand.b {
282 DerefOrImmediate::Immediate(_) => Op1Addr::Imm,
283 DerefOrImmediate::Deref(b) => b.register.to_op1_addr(),
284 },
285 res: operand.op.to_res(),
286 }
287 }
288 }
289 }
290}