cairo_lang_casm/
inline.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use crate::hints::Hint;
use crate::instructions::Instruction;

#[cfg(test)]
#[path = "inline_test.rs"]
mod test;

#[macro_export]
macro_rules! casm {
    {$($tok:tt)*} => {
        {
            let mut ctx = $crate::inline::CasmContext::default();
            $crate::casm_extend!(ctx, $($tok)*);
            ctx
        }
    }
}

#[macro_export]
macro_rules! casm_extend {
    ($ctx:ident,) => {};
    ($ctx:ident, $dst:tt = $a:tt $(+ $b0:tt)? $(* $b1:tt)? $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::AssertEq(
            $crate::instructions::AssertEqInstruction {
                a: $crate::deref!($dst),
                b: $crate::res!($a $(+ $b0)? $(* $b1)?),
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, call rel $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Call(
            $crate::instructions::CallInstruction {
                target: $crate::deref_or_immediate!($target),
                relative: true,
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, call abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Call(
            $crate::instructions::CallInstruction {
                target: $crate::deref_or_immediate!($target),
                relative: false,
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, jmp rel $target:expr $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Jump(
            $crate::instructions::JumpInstruction {
                target: $crate::deref_or_immediate!($target),
                relative: true,
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, jmp abs $target:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Jump(
            $crate::instructions::JumpInstruction {
                target: $crate::deref_or_immediate!($target),
                relative: false,
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, jmp rel $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Jnz(
            $crate::instructions::JnzInstruction {
                jump_offset: $crate::deref_or_immediate!($target),
                condition: $crate::deref!($cond),
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, jmp $target:tt if $cond:tt != 0 $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Jnz(
            $crate::instructions::JnzInstruction {
                jump_offset: $crate::deref_or_immediate!($target),
                condition: $crate::deref!($cond),
            }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, ap += $operand:tt $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::AddAp(
            $crate::instructions::AddApInstruction { operand: $crate::res!($operand) }
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, ret $(,$ap:ident++)? ; $($tok:tt)*) => {
        let body = $crate::instructions::InstructionBody::Ret(
            $crate::instructions::RetInstruction {}
        );
        $crate::append_instruction!($ctx, body $(,$ap++)?);
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = segments . add ( ) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::AllocSegment{dst: $crate::deref!($dst)}.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt < $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = $lhs:tt < memory $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThan{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = memory $lhs:tt <= $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ memory $dst:tt = $lhs:tt <= memory $rhs:tt %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::TestLessThanOrEqual{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            dst: $crate::deref!($dst),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
         = divmod ( memory $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            quotient: $crate::deref!($quotient),
            remainder: $crate::deref!($remainder),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
         = divmod ( memory $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            quotient: $crate::deref!($quotient),
            remainder: $crate::deref!($remainder),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
         = divmod ( $lhs:tt , memory $rhs:tt ) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            quotient: $crate::deref!($quotient),
            remainder: $crate::deref!($remainder),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ ( memory $quotient:tt, memory $remainder:tt )
         = divmod ( $lhs:tt , $rhs:tt ) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::DivMod{
            lhs: $crate::res!($lhs),
            rhs: $crate::res!($rhs),
            quotient: $crate::deref!($quotient),
            remainder: $crate::deref!($remainder),
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt + $offset:tt) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
            system: $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
                op: cairo_lang_casm::operand::Operation::Add,
                a: $crate::deref!($addr),
                b: $crate::deref_or_immediate!(num_bigint::BigInt::from($offset)),
            })}.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
    ($ctx:ident, %{ syscall_handler.syscall(syscall_ptr=memory $addr:tt) %} $($tok:tt)*) => {
        $ctx.current_hints.push($crate::hints::CoreHint::SystemCall {
            system: $crate::operand::ResOperand::Deref($crate::deref!($addr))
        }.into());
        $crate::casm_extend!($ctx, $($tok)*)
    };
}

#[macro_export]
macro_rules! append_instruction {
    ($ctx:ident, $body:ident $(,$ap:ident++)?) => {
        let current_hints = core::mem::take(&mut $ctx.current_hints);
        let instr = $crate::instructions::Instruction {
            body: $body,
            inc_ap: $crate::is_inc_ap!($($ap++)?),
            hints: current_hints,
        };
        $ctx.current_code_offset += instr.body.op_size();
        $ctx.instructions.push(instr);
    };
}

#[macro_export]
macro_rules! is_inc_ap {
    () => {
        false
    };
    (ap + +) => {
        true
    };
}

#[derive(Default)]
pub struct CasmContext {
    pub current_code_offset: usize,
    pub current_hints: Vec<Hint>,
    pub instructions: Vec<Instruction>,
    // TODO(spapini): Branches.
    // TODO(spapini): Relocations.
}

#[macro_export]
macro_rules! deref {
    ([ap + $offset:expr]) => {
        $crate::operand::CellRef { register: $crate::reg!(ap), offset: $offset }
    };
    ([fp + $offset:expr]) => {
        $crate::operand::CellRef { register: $crate::reg!(fp), offset: $offset }
    };
    ([& $var:ident + $offset:expr]) => {
        $crate::operand::CellRef { register: $var.register, offset: $var.offset + $offset }
    };
    ([ap - $offset:expr]) => {
        $crate::operand::CellRef { register: $crate::reg!(ap), offset: -$offset }
    };
    ([fp - $offset:expr]) => {
        $crate::operand::CellRef { register: $crate::reg!(fp), offset: -$offset }
    };
    ([& $var:ident - $offset:expr]) => {
        $crate::operand::CellRef { register: $var.register, offset: $var.offset - $offset }
    };
    ([ap]) => {
        $crate::operand::CellRef { register: $crate::reg!(ap), offset: 0 }
    };
    ([fp]) => {
        $crate::operand::CellRef { register: $crate::reg!(fp), offset: 0 }
    };
    ([& $var:ident]) => {
        $var
    };
    ($a:expr) => {
        $a
    };
}

#[macro_export]
macro_rules! reg {
    (ap) => {
        $crate::operand::Register::AP
    };
    (fp) => {
        $crate::operand::Register::FP
    };
}

#[macro_export]
macro_rules! deref_or_immediate {
    ([$a:ident $($op:tt $offset:expr)?]) => {
        $crate::operand::DerefOrImmediate::Deref($crate::deref!([$a $($op $offset)?]))
    };
    ($a:expr) => {
        $crate::operand::DerefOrImmediate::from($a)
    };
}

#[macro_export]
macro_rules! res {
    ($a:tt + $b:tt) => {
        $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
            op: $crate::operand::Operation::Add,
            a: $crate::deref!($a),
            b: $crate::deref_or_immediate!($b),
        })
    };
    ($a:tt * $b:tt) => {
        $crate::operand::ResOperand::BinOp($crate::operand::BinOpOperand {
            op: $crate::operand::Operation::Mul,
            a: $crate::deref!($a),
            b: $crate::deref_or_immediate!($b),
        })
    };
    ([[ap $($op:tt $offset:expr)?]]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), 0)
    };
    ([[ap $($op:tt $offset:expr)?] + $outer:expr]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), $outer)
    };
    ([[ap $($op:tt $offset:expr)?] - $outer:expr]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([ap $($op $offset)?]), -$outer)
    };
    ([[fp $($op:tt $offset:expr)?]]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), 0)
    };
    ([[fp $($op:tt $offset:expr)?] + $outer:expr]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), $outer)
    };
    ([[fp $($op:tt $offset:expr)?] - $outer:expr]) => {
        $crate::operand::ResOperand::DoubleDeref($crate::deref!([fp $($op $offset)?]), -$outer)
    };
    ([[&$a:expr]]) => {
        $crate::operand::ResOperand::DoubleDeref($a, 0)
    };
    ([[&$a:expr] + $b:expr]) => {
        $crate::operand::ResOperand::DoubleDeref($a, $b)
    };
    ($a:tt) => {
        $crate::operand::ResOperand::from($crate::deref_or_immediate!($a))
    };
}