1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use crate::hints::Hint;
5use crate::instructions::Instruction;
6
7#[cfg(test)]
8#[path = "inline_test.rs"]
9mod test;
10
11#[macro_export]
12macro_rules! casm {
13 {$($tok:tt)*} => {
14 {
15 let mut ctx = $crate::inline::CasmContext::default();
16 $crate::casm_extend!(ctx, $($tok)*);
17 ctx
18 }
19 }
20}
21
22#[macro_export]
23macro_rules! casm_extend {
24 ($ctx:ident,) => {};
25 ($ctx:ident, $dst:tt = $a:tt $(+ $b0:tt)? $(* $b1:tt)? $(,$ap:ident++)? ; $($tok:tt)*) => {
26 let body = $crate::instructions::InstructionBody::AssertEq(
27 $crate::instructions::AssertEqInstruction {
28 a: $crate::deref!($dst),
29 b: $crate::res!($a $(+ $b0)? $(* $b1)?),
30 }
31 );
32 $crate::append_instruction!($ctx, body $(,$ap++)?);
33 $crate::casm_extend!($ctx, $($tok)*)
34 };
35 ($ctx:ident, call rel $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
36 let body = $crate::instructions::InstructionBody::Call(
37 $crate::instructions::CallInstruction {
38 target: $crate::deref_or_immediate!($target),
39 relative: true,
40 }
41 );
42 $crate::append_instruction!($ctx, body $(,$ap++)?);
43 $crate::casm_extend!($ctx, $($tok)*)
44 };
45 ($ctx:ident, call abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
46 let body = $crate::instructions::InstructionBody::Call(
47 $crate::instructions::CallInstruction {
48 target: $crate::deref_or_immediate!($target),
49 relative: false,
50 }
51 );
52 $crate::append_instruction!($ctx, body $(,$ap++)?);
53 $crate::casm_extend!($ctx, $($tok)*)
54 };
55 ($ctx:ident, jmp rel $target:expr $(,$ap:ident++)? ; $($tok:tt)*) => {
56 let body = $crate::instructions::InstructionBody::Jump(
57 $crate::instructions::JumpInstruction {
58 target: $crate::deref_or_immediate!($target),
59 relative: true,
60 }
61 );
62 $crate::append_instruction!($ctx, body $(,$ap++)?);
63 $crate::casm_extend!($ctx, $($tok)*)
64 };
65 ($ctx:ident, jmp abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
66 let body = $crate::instructions::InstructionBody::Jump(
67 $crate::instructions::JumpInstruction {
68 target: $crate::deref_or_immediate!($target),
69 relative: false,
70 }
71 );
72 $crate::append_instruction!($ctx, body $(,$ap++)?);
73 $crate::casm_extend!($ctx, $($tok)*)
74 };
75 ($ctx:ident, jmp rel $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
76 let body = $crate::instructions::InstructionBody::Jnz(
77 $crate::instructions::JnzInstruction {
78 jump_offset: $crate::deref_or_immediate!($target),
79 condition: $crate::deref!($cond),
80 }
81 );
82 $crate::append_instruction!($ctx, body $(,$ap++)?);
83 $crate::casm_extend!($ctx, $($tok)*)
84 };
85 ($ctx:ident, jmp $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
86 let body = $crate::instructions::InstructionBody::Jnz(
87 $crate::instructions::JnzInstruction {
88 jump_offset: $crate::deref_or_immediate!($target),
89 condition: $crate::deref!($cond),
90 }
91 );
92 $crate::append_instruction!($ctx, body $(,$ap++)?);
93 $crate::casm_extend!($ctx, $($tok)*)
94 };
95 ($ctx:ident, ap += $operand:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
96 let body = $crate::instructions::InstructionBody::AddAp(
97 $crate::instructions::AddApInstruction { operand: $crate::res!($operand) }
98 );
99 $crate::append_instruction!($ctx, body $(,$ap++)?);
100 $crate::casm_extend!($ctx, $($tok)*)
101 };
102 ($ctx:ident, ret $(,$ap:ident++)? ; $($tok:tt)*) => {
103 let body = $crate::instructions::InstructionBody::Ret(
104 $crate::instructions::RetInstruction {}
105 );
106 $crate::append_instruction!($ctx, body $(,$ap++)?);
107 $crate::casm_extend!($ctx, $($tok)*)
108 };
109 ($ctx:ident, %{ memory $dst:tt = segments . add ( ) %} $($tok:tt)*) => {
110 $ctx.current_hints.push($crate::hints::CoreHint::AllocSegment{dst: $crate::deref!($dst)}.into());
111 $crate::casm_extend!($ctx, $($tok)*)
112 };
113 ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
114 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
115 lhs: $crate::res!($lhs),
116 rhs: $crate::res!($rhs),
117 dst: $crate::deref!($dst),
118 }.into());
119 $crate::casm_extend!($ctx, $($tok)*)
120 };
121 ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < $rhs:tt %} $($tok:tt)*) => {
122 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
123 lhs: $crate::res!($lhs),
124 rhs: $crate::res!($rhs),
125 dst: $crate::deref!($dst),
126 }.into());
127 $crate::casm_extend!($ctx, $($tok)*)
128 };
129 ($ctx:ident, %{ memory $dst:tt = $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
130 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
131 lhs: $crate::res!($lhs),
132 rhs: $crate::res!($rhs),
133 dst: $crate::deref!($dst),
134 }.into());
135 $crate::casm_extend!($ctx, $($tok)*)
136 };
137 ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
138 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
139 lhs: $crate::res!($lhs),
140 rhs: $crate::res!($rhs),
141 dst: $crate::deref!($dst),
142 }.into());
143 $crate::casm_extend!($ctx, $($tok)*)
144 };
145 ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= $rhs:tt %} $($tok:tt)*) => {
146 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
147 lhs: $crate::res!($lhs),
148 rhs: $crate::res!($rhs),
149 dst: $crate::deref!($dst),
150 }.into());
151 $crate::casm_extend!($ctx, $($tok)*)
152 };
153 ($ctx:ident, %{ memory $dst:tt = $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
154 $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
155 lhs: $crate::res!($lhs),
156 rhs: $crate::res!($rhs),
157 dst: $crate::deref!($dst),
158 }.into());
159 $crate::casm_extend!($ctx, $($tok)*)
160 };
161 ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
162 = divmod ( memory $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
163 $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
164 lhs: $crate::res!($lhs),
165 rhs: $crate::res!($rhs),
166 quotient: $crate::deref!($quotient),
167 remainder: $crate::deref!($remainder),
168 }.into());
169 $crate::casm_extend!($ctx, $($tok)*)
170 };
171 ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
172 = divmod ( memory $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
173 $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
174 lhs: $crate::res!($lhs),
175 rhs: $crate::res!($rhs),
176 quotient: $crate::deref!($quotient),
177 remainder: $crate::deref!($remainder),
178 }.into());
179 $crate::casm_extend!($ctx, $($tok)*)
180 };
181 ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
182 = divmod ( $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
183 $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
184 lhs: $crate::res!($lhs),
185 rhs: $crate::res!($rhs),
186 quotient: $crate::deref!($quotient),
187 remainder: $crate::deref!($remainder),
188 }.into());
189 $crate::casm_extend!($ctx, $($tok)*)
190 };
191 ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
192 = divmod ( $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
193 $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
194 lhs: $crate::res!($lhs),
195 rhs: $crate::res!($rhs),
196 quotient: $crate::deref!($quotient),
197 remainder: $crate::deref!($remainder),
198 }.into());
199 $crate::casm_extend!($ctx, $($tok)*)
200 };
201 ($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt + $offset:tt) %} $($tok:tt)*) => {
202 $ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
203 system: $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
204 op: cairo_lang_casm::operand::Operation::Add,
205 a: $crate::deref!($addr),
206 b: $crate::deref_or_immediate!(num_bigint::BigInt::from($offset)),
207 })}.into());
208 $crate::casm_extend!($ctx, $($tok)*)
209 };
210 ($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt) %} $($tok:tt)*) => {
211 $ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
212 system: $crate::operand::ResOperand::Deref($crate::deref!($addr))
213 }.into());
214 $crate::casm_extend!($ctx, $($tok)*)
215 };
216}
217
218#[macro_export]
219macro_rules! append_instruction {
220 ($ctx:ident, $body:ident $(,$ap:ident++)?) => {
221 let current_hints = core::mem::take(&mut $ctx.current_hints);
222 let instr = $crate::instructions::Instruction {
223 body: $body,
224 inc_ap: $crate::is_inc_ap!($($ap++)?),
225 hints: current_hints,
226 };
227 $ctx.current_code_offset += instr.body.op_size();
228 $ctx.instructions.push(instr);
229 };
230}
231
232#[macro_export]
233macro_rules! is_inc_ap {
234 () => {
235 false
236 };
237 (ap + +) => {
238 true
239 };
240}
241
242#[derive(Default)]
243pub struct CasmContext {
244 pub current_code_offset: usize,
245 pub current_hints: Vec<Hint>,
246 pub instructions: Vec<Instruction>,
247 }
250
251#[macro_export]
252macro_rules! deref {
253 ([ap + $offset:expr]) => {
254 $crate::operand::CellRef { register: $crate::reg!(ap), offset: $offset }
255 };
256 ([fp + $offset:expr]) => {
257 $crate::operand::CellRef { register: $crate::reg!(fp), offset: $offset }
258 };
259 ([& $var:ident + $offset:expr]) => {
260 $crate::operand::CellRef { register: $var.register, offset: $var.offset + $offset }
261 };
262 ([ap - $offset:expr]) => {
263 $crate::operand::CellRef { register: $crate::reg!(ap), offset: -$offset }
264 };
265 ([fp - $offset:expr]) => {
266 $crate::operand::CellRef { register: $crate::reg!(fp), offset: -$offset }
267 };
268 ([& $var:ident - $offset:expr]) => {
269 $crate::operand::CellRef { register: $var.register, offset: $var.offset - $offset }
270 };
271 ([ap]) => {
272 $crate::operand::CellRef { register: $crate::reg!(ap), offset: 0 }
273 };
274 ([fp]) => {
275 $crate::operand::CellRef { register: $crate::reg!(fp), offset: 0 }
276 };
277 ([& $var:ident]) => {
278 $var
279 };
280 ($a:expr) => {
281 $a
282 };
283}
284
285#[macro_export]
286macro_rules! reg {
287 (ap) => {
288 $crate::operand::Register::AP
289 };
290 (fp) => {
291 $crate::operand::Register::FP
292 };
293}
294
295#[macro_export]
296macro_rules! deref_or_immediate {
297 ([$a:ident $($op:tt $offset:expr)?]) => {
298 $crate::operand::DerefOrImmediate::Deref($crate::deref!([$a $($op $offset)?]))
299 };
300 ($a:expr) => {
301 $crate::operand::DerefOrImmediate::from($a)
302 };
303}
304
305#[macro_export]
306macro_rules! res {
307 ($a:tt + $b:tt) => {
308 $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
309 op: $crate::operand::Operation::Add,
310 a: $crate::deref!($a),
311 b: $crate::deref_or_immediate!($b),
312 })
313 };
314 ($a:tt * $b:tt) => {
315 $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
316 op: $crate::operand::Operation::Mul,
317 a: $crate::deref!($a),
318 b: $crate::deref_or_immediate!($b),
319 })
320 };
321 ([[ap $($op:tt $offset:expr)?]]) => {
322 $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), 0)
323 };
324 ([[ap $($op:tt $offset:expr)?] + $outer:expr]) => {
325 $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), $outer)
326 };
327 ([[ap $($op:tt $offset:expr)?] - $outer:expr]) => {
328 $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), -$outer)
329 };
330 ([[fp $($op:tt $offset:expr)?]]) => {
331 $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), 0)
332 };
333 ([[fp $($op:tt $offset:expr)?] + $outer:expr]) => {
334 $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), $outer)
335 };
336 ([[fp $($op:tt $offset:expr)?] - $outer:expr]) => {
337 $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), -$outer)
338 };
339 ([[&$a:expr]]) => {
340 $crate::operand::ResOperand::DoubleDeref($a, 0)
341 };
342 ([[&$a:expr] + $b:expr]) => {
343 $crate::operand::ResOperand::DoubleDeref($a, $b)
344 };
345 ($a:tt) => {
346 $crate::operand::ResOperand::from($crate::deref_or_immediate!($a))
347 };
348}