cairo_lang_casm/
inline.rs

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    // TODO(spapini): Branches.
248    // TODO(spapini): Relocations.
249}
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}