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