cairo_lang_casm/
builder.rs

1#[cfg(not(feature = "std"))]
2pub use alloc::borrow::ToOwned;
3#[cfg(not(feature = "std"))]
4use alloc::{string::String, vec, vec::Vec};
5#[cfg(feature = "std")]
6pub use std::borrow::ToOwned;
7
8use cairo_lang_utils::casts::IntoOrPanic;
9use cairo_lang_utils::extract_matches;
10use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
11use cairo_lang_utils::unordered_hash_map::{Entry, UnorderedHashMap};
12use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
13use num_bigint::BigInt;
14use num_traits::{One, Zero};
15
16use crate::ap_change::ApplyApChange;
17use crate::cell_expression::{CellExpression, CellOperator};
18use crate::deref_or_immediate;
19use crate::hints::Hint;
20use crate::instructions::{
21    AddApInstruction, AssertEqInstruction, Blake2sCompressInstruction, CallInstruction,
22    Instruction, InstructionBody, JnzInstruction, JumpInstruction, RetInstruction,
23};
24use crate::operand::{BinOpOperand, CellRef, DerefOrImmediate, Operation, Register, ResOperand};
25
26#[cfg(test)]
27#[path = "builder_test.rs"]
28mod test;
29
30/// Variables for casm builder, representing a `CellExpression`.
31#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
32pub struct Var(usize);
33
34/// The kind of an assert_eq action.
35///
36/// To be used in `assert_vars_eq`.
37pub enum AssertEqKind {
38    Felt252,
39    QM31,
40}
41
42/// The state of the variables at some line.
43#[derive(Clone, Debug, Default, Eq, PartialEq)]
44pub struct State {
45    /// The value per variable.
46    vars: OrderedHashMap<Var, CellExpression>,
47    /// The number of allocated variables from the beginning of the run.
48    allocated: i16,
49    /// The AP change since the beginning of the run.
50    pub ap_change: usize,
51    /// The number of casm steps since the beginning of the run.
52    pub steps: usize,
53}
54impl State {
55    /// Returns the value, in relation to the initial ap value.
56    fn get_value(&self, var: Var) -> CellExpression {
57        self.vars[&var].clone()
58    }
59
60    /// Returns the value, in relation to the current ap value.
61    pub fn get_adjusted(&self, var: Var) -> CellExpression {
62        self.get_value(var).unchecked_apply_known_ap_change(self.ap_change)
63    }
64
65    /// Returns the value, assuming it is a direct cell reference.
66    pub fn get_adjusted_as_cell_ref(&self, var: Var) -> CellRef {
67        extract_matches!(self.get_adjusted(var), CellExpression::Deref)
68    }
69
70    /// Validates that the state is valid, as it had enough ap change.
71    fn validate_finality(&self) {
72        assert!(
73            self.ap_change >= self.allocated.into_or_panic(),
74            "Not enough instructions to update ap. Add an `ap += *` instruction. ap_change: {}, \
75             allocated: {}",
76            self.ap_change,
77            self.allocated,
78        );
79    }
80
81    /// Intersect the states of branches leading to the same label, validating that the states can
82    /// intersect.
83    fn intersect(&mut self, other: &Self, read_only: bool) {
84        assert_eq!(self.ap_change, other.ap_change, "Merged branches not aligned on AP change.");
85        assert_eq!(
86            self.allocated, other.allocated,
87            "Merged branches not aligned on number of allocations."
88        );
89        if read_only {
90            assert!(self.steps >= other.steps, "Finalized branch cannot be updated.");
91        } else {
92            self.steps = self.steps.max(other.steps);
93        }
94        // Allowing removal of variables as valid code won't be producible in that case anyway, and
95        // otherwise merging branches becomes very difficult.
96        self.vars.retain(|var, value| {
97            other
98                .vars
99                .get(var)
100                .map(|x| assert_eq!(x, value, "Var mismatch between branches."))
101                .is_some()
102        });
103    }
104}
105
106/// A statement added to the builder.
107enum Statement {
108    /// A final instruction, no need for further editing.
109    Final(Instruction),
110    /// A jump or call command, requires fixing the actual target label.
111    Jump(String, Instruction),
112    /// A target label for jumps, additionally with an offset for defining the label in a position
113    /// relative to the current statement.
114    Label(String, usize),
115}
116
117/// The builder result.
118pub struct CasmBuildResult<const BRANCH_COUNT: usize> {
119    /// The actual casm code.
120    pub instructions: Vec<Instruction>,
121    /// The state and relocations per branch.
122    pub branches: [(State, Vec<usize>); BRANCH_COUNT],
123}
124
125/// Builder to more easily write casm code.
126///
127/// Allows CASM building without specifically thinking about ap changes and the sizes of opcodes.
128/// Wrong usages of it would panic instead of returning a result, as this builder assumes we are in
129/// a post validation of parameters stage.
130pub struct CasmBuilder {
131    /// The state at a point of jumping into a label, per label.
132    label_state: UnorderedHashMap<String, State>,
133    /// The set of labels that were already read, so cannot be updated.
134    read_labels: UnorderedHashSet<String>,
135    /// The state at the last added statement.
136    main_state: State,
137    /// The added statements.
138    statements: Vec<Statement>,
139    /// The current set of added hints.
140    current_hints: Vec<Hint>,
141    /// The number of vars created. Used to not reuse var names.
142    var_count: usize,
143    /// Is the current state reachable.
144    /// Example for unreachable state is after a unconditional jump, before any label is stated.
145    reachable: bool,
146}
147impl CasmBuilder {
148    /// Finalizes the builder, with the requested labels as the returning branches.
149    /// "Fallthrough" is a special case for the fallthrough case.
150    pub fn build<const BRANCH_COUNT: usize>(
151        mut self,
152        branch_names: [&str; BRANCH_COUNT],
153    ) -> CasmBuildResult<BRANCH_COUNT> {
154        assert!(
155            self.current_hints.is_empty(),
156            "Build cannot be called with hints as the last addition."
157        );
158        let label_offsets = self.compute_label_offsets();
159        if self.reachable {
160            self.label_state.insert("Fallthrough".to_owned(), self.main_state);
161        }
162        let mut instructions = vec![];
163        let mut branch_relocations = UnorderedHashMap::<String, Vec<usize>>::default();
164        let mut offset = 0;
165        for statement in self.statements {
166            match statement {
167                Statement::Final(inst) => {
168                    offset += inst.body.op_size();
169                    instructions.push(inst);
170                }
171                Statement::Jump(label, mut inst) => {
172                    match label_offsets.get(&label) {
173                        Some(label_offset) => match &mut inst.body {
174                            InstructionBody::Jnz(JnzInstruction {
175                                jump_offset: DerefOrImmediate::Immediate(value),
176                                ..
177                            })
178                            | InstructionBody::Jump(JumpInstruction {
179                                target: DerefOrImmediate::Immediate(value),
180                                ..
181                            })
182                            | InstructionBody::Call(CallInstruction {
183                                target: DerefOrImmediate::Immediate(value),
184                                ..
185                            }) => {
186                                // Updating the value, instead of assigning into it, to avoid
187                                // allocating a BigInt since it is already 0.
188                                value.value += *label_offset as i128 - offset as i128;
189                            }
190
191                            _ => unreachable!("Only jump or call statements should be here."),
192                        },
193                        None => {
194                            branch_relocations.entry(label).or_default().push(instructions.len())
195                        }
196                    }
197                    offset += inst.body.op_size();
198                    instructions.push(inst);
199                }
200                Statement::Label(name, _) => {
201                    self.label_state.remove(&name);
202                }
203            }
204        }
205        let branches = branch_names.map(|label| {
206            let state = self
207                .label_state
208                .remove(label)
209                .unwrap_or_else(|| panic!("Requested a non existing final label: {label:?}."));
210            state.validate_finality();
211            (state, branch_relocations.remove(label).unwrap_or_default())
212        });
213        assert!(self.label_state.is_empty(), "Did not use all branches.");
214        assert!(branch_relocations.is_empty(), "Did not use all branch relocations.");
215        CasmBuildResult { instructions, branches }
216    }
217
218    /// Returns the current ap change of the builder.
219    /// Useful for manual ap change handling.
220    pub fn curr_ap_change(&self) -> usize {
221        self.main_state.ap_change
222    }
223
224    /// Computes the code offsets of all the labels.
225    fn compute_label_offsets(&self) -> UnorderedHashMap<String, usize> {
226        let mut label_offsets = UnorderedHashMap::default();
227        let mut offset = 0;
228        for statement in &self.statements {
229            match statement {
230                Statement::Final(inst) | Statement::Jump(_, inst) => {
231                    offset += inst.body.op_size();
232                }
233                Statement::Label(name, extra_offset) => {
234                    label_offsets.insert(name.clone(), offset + extra_offset);
235                }
236            }
237        }
238        label_offsets
239    }
240
241    /// Adds a variable pointing to `value`.
242    pub fn add_var(&mut self, value: CellExpression) -> Var {
243        let var = Var(self.var_count);
244        self.var_count += 1;
245        self.main_state.vars.insert(var, value);
246        var
247    }
248
249    /// Allocates a new variable in memory, either local (FP-based) or temp (AP-based).
250    pub fn alloc_var(&mut self, local_var: bool) -> Var {
251        let var = self.add_var(CellExpression::Deref(CellRef {
252            offset: self.main_state.allocated,
253            register: if local_var { Register::FP } else { Register::AP },
254        }));
255        self.main_state.allocated += 1;
256        var
257    }
258
259    /// Returns an additional variable pointing to the same value.
260    pub fn duplicate_var(&mut self, var: Var) -> Var {
261        self.add_var(self.get_value(var, false))
262    }
263
264    /// Adds a hint, generated from `inputs` which are cell refs or immediates and `outputs` which
265    /// must be cell refs.
266    pub fn add_hint<
267        const INPUTS_COUNT: usize,
268        const OUTPUTS_COUNT: usize,
269        THint: Into<Hint>,
270        F: FnOnce([ResOperand; INPUTS_COUNT], [CellRef; OUTPUTS_COUNT]) -> THint,
271    >(
272        &mut self,
273        f: F,
274        inputs: [Var; INPUTS_COUNT],
275        outputs: [Var; OUTPUTS_COUNT],
276    ) {
277        self.current_hints.push(
278            f(
279                inputs.map(|v| match self.get_value(v, true) {
280                    CellExpression::Deref(cell) => ResOperand::Deref(cell),
281                    CellExpression::DoubleDeref(cell, offset) => {
282                        ResOperand::DoubleDeref(cell, offset)
283                    }
284                    CellExpression::Immediate(imm) => imm.into(),
285                    CellExpression::BinOp { op, a: other, b } => match op {
286                        CellOperator::Add => {
287                            ResOperand::BinOp(BinOpOperand { op: Operation::Add, a: other, b })
288                        }
289                        CellOperator::Mul => {
290                            ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a: other, b })
291                        }
292                        CellOperator::Sub | CellOperator::Div => {
293                            panic!("hints to non ResOperand references are not supported.")
294                        }
295                    },
296                }),
297                outputs.map(|v| self.as_cell_ref(v, true)),
298            )
299            .into(),
300        );
301    }
302
303    /// Adds an assertion that `dst = res`.
304    /// `dst` must be a cell reference.
305    pub fn assert_vars_eq(&mut self, dst: Var, res: Var, kind: AssertEqKind) {
306        let a = self.as_cell_ref(dst, true);
307        let b = self.get_value(res, true);
308        let (a, b) = match b {
309            CellExpression::Deref(cell) => (a, ResOperand::Deref(cell)),
310            CellExpression::DoubleDeref(cell, offset) => (a, ResOperand::DoubleDeref(cell, offset)),
311            CellExpression::Immediate(imm) => (a, imm.into()),
312            CellExpression::BinOp { op, a: other, b } => match op {
313                CellOperator::Add => {
314                    (a, ResOperand::BinOp(BinOpOperand { op: Operation::Add, a: other, b }))
315                }
316                CellOperator::Mul => {
317                    (a, ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a: other, b }))
318                }
319                CellOperator::Sub => {
320                    (other, ResOperand::BinOp(BinOpOperand { op: Operation::Add, a, b }))
321                }
322                CellOperator::Div => {
323                    (other, ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a, b }))
324                }
325            },
326        };
327        let inner = AssertEqInstruction { a, b };
328        let instruction = self.next_instruction(
329            match kind {
330                AssertEqKind::Felt252 => InstructionBody::AssertEq(inner),
331                AssertEqKind::QM31 => InstructionBody::QM31AssertEq(inner),
332            },
333            true,
334        );
335        self.statements.push(Statement::Final(instruction));
336    }
337
338    /// Writes and increments a buffer.
339    /// Useful for RangeCheck and similar buffers.
340    /// `buffer` must be a cell reference, or a cell reference with a small added constant.
341    /// `value` must be a cell reference.
342    pub fn buffer_write_and_inc(&mut self, buffer: Var, value: Var) {
343        let (cell, offset) = self.buffer_get_and_inc(buffer);
344        let location = self.add_var(CellExpression::DoubleDeref(cell, offset));
345        self.assert_vars_eq(value, location, AssertEqKind::Felt252);
346    }
347
348    /// Writes `var` as a new tempvar and returns it as a variable, unless its value is already
349    /// `deref` (which includes trivial calculations as well) so it instead returns a variable
350    /// pointing to that location.
351    pub fn maybe_add_tempvar(&mut self, var: Var) -> Var {
352        self.add_var(CellExpression::Deref(match self.get_value(var, false) {
353            CellExpression::Deref(cell) => cell,
354            CellExpression::BinOp {
355                op: CellOperator::Add | CellOperator::Sub,
356                a,
357                b: DerefOrImmediate::Immediate(imm),
358            } if imm.value.is_zero() => a,
359            CellExpression::BinOp {
360                op: CellOperator::Mul | CellOperator::Div,
361                a,
362                b: DerefOrImmediate::Immediate(imm),
363            } if imm.value.is_one() => a,
364            _ => {
365                let temp = self.alloc_var(false);
366                self.assert_vars_eq(temp, var, AssertEqKind::Felt252);
367                return temp;
368            }
369        }))
370    }
371
372    /// Increments a buffer and allocates and returns variable pointing to its previous value.
373    pub fn get_ref_and_inc(&mut self, buffer: Var) -> Var {
374        let (cell, offset) = self.as_cell_ref_plus_const(buffer, 0, false);
375        self.main_state.vars.insert(
376            buffer,
377            CellExpression::BinOp {
378                op: CellOperator::Add,
379                a: cell,
380                b: deref_or_immediate!(BigInt::from(offset) + 1),
381            },
382        );
383        self.add_var(CellExpression::DoubleDeref(cell, offset))
384    }
385
386    /// Increments a buffer and returning the previous value it pointed to.
387    /// Useful for writing, reading and referencing values.
388    /// `buffer` must be a cell reference, or a cell reference with a small added constant.
389    fn buffer_get_and_inc(&mut self, buffer: Var) -> (CellRef, i16) {
390        let (base, offset) = match self.get_value(buffer, false) {
391            CellExpression::Deref(cell) => (cell, 0),
392            CellExpression::BinOp {
393                op: CellOperator::Add,
394                a,
395                b: DerefOrImmediate::Immediate(imm),
396            } => (a, imm.value.try_into().expect("Too many buffer writes.")),
397            _ => panic!("Not a valid buffer."),
398        };
399        self.main_state.vars.insert(
400            buffer,
401            CellExpression::BinOp {
402                op: CellOperator::Add,
403                a: base,
404                b: deref_or_immediate!(offset + 1),
405            },
406        );
407        (base, offset)
408    }
409
410    /// Increases AP by `size`.
411    pub fn add_ap(&mut self, size: usize) {
412        let instruction = self.next_instruction(
413            InstructionBody::AddAp(AddApInstruction { operand: BigInt::from(size).into() }),
414            false,
415        );
416        self.statements.push(Statement::Final(instruction));
417        self.main_state.ap_change += size;
418    }
419
420    /// Increases the AP change by `size`, without adding an instruction.
421    pub fn increase_ap_change(&mut self, amount: usize) {
422        self.main_state.ap_change += amount;
423        self.main_state.allocated += amount.into_or_panic::<i16>();
424    }
425
426    /// Returns a variable that is the `op` of `lhs` and `rhs`.
427    /// `lhs` must be a cell reference and `rhs` must be deref or immediate.
428    pub fn bin_op(&mut self, op: CellOperator, lhs: Var, rhs: Var) -> Var {
429        let (a, b) = match self.get_value(lhs, false) {
430            // Regular `bin_op` support.
431            CellExpression::Deref(cell) => (cell, self.as_deref_or_imm(rhs, false)),
432            // `add_with_const` + `imm` support.
433            CellExpression::BinOp {
434                op: CellOperator::Add,
435                a,
436                b: DerefOrImmediate::Immediate(imm),
437            } if op == CellOperator::Add => (
438                a,
439                DerefOrImmediate::Immediate(
440                    (imm.value
441                        + extract_matches!(self.get_value(rhs, false), CellExpression::Immediate))
442                    .into(),
443                ),
444            ),
445            _ => panic!(
446                "`bin_op` is supported only between a `deref` and a `deref_or_imm`, or a \
447                 `add_with_const` and `imm`."
448            ),
449        };
450        self.add_var(CellExpression::BinOp { op, a, b })
451    }
452
453    /// Returns a variable that is `[[var] + offset]`.
454    /// `var` must be a cell reference, or a cell ref plus a small constant.
455    pub fn double_deref(&mut self, var: Var, offset: i16) -> Var {
456        let (cell, full_offset) = self.as_cell_ref_plus_const(var, offset, false);
457        self.add_var(CellExpression::DoubleDeref(cell, full_offset))
458    }
459
460    /// Sets the label to have the set states, otherwise tests if the state matches the existing one
461    /// by merging.
462    fn set_or_test_label_state(&mut self, label: String, state: State) {
463        match self.label_state.entry(label) {
464            Entry::Occupied(e) => {
465                let read_only = self.read_labels.contains(e.key());
466                e.into_mut().intersect(&state, read_only);
467            }
468            Entry::Vacant(e) => {
469                e.insert(state);
470            }
471        }
472    }
473
474    /// Add a statement to jump to `label`.
475    pub fn jump(&mut self, label: String) {
476        let instruction = self.next_instruction(
477            InstructionBody::Jump(JumpInstruction {
478                target: deref_or_immediate!(0),
479                relative: true,
480            }),
481            true,
482        );
483        self.statements.push(Statement::Jump(label.clone(), instruction));
484        let mut state = State::default();
485        core::mem::swap(&mut state, &mut self.main_state);
486        self.set_or_test_label_state(label, state);
487        self.reachable = false;
488    }
489
490    /// Add a statement to jump to `label` if `condition != 0`.
491    /// `condition` must be a cell reference.
492    pub fn jump_nz(&mut self, condition: Var, label: String) {
493        let cell = self.as_cell_ref(condition, true);
494        let instruction = self.next_instruction(
495            InstructionBody::Jnz(JnzInstruction {
496                condition: cell,
497                jump_offset: deref_or_immediate!(0),
498            }),
499            true,
500        );
501        self.statements.push(Statement::Jump(label.clone(), instruction));
502        self.set_or_test_label_state(label, self.main_state.clone());
503    }
504
505    /// Add a statement performing Blake2s compression.
506    ///
507    /// `state` must be a cell reference to a pointer to `[u32; 8]` as the hash state.
508    /// `byte_count` must be a cell reference to the number of bytes in the message.
509    /// `message` must be a cell reference to a pointer to `[u32; 16]` as the message.
510    /// `finalize` should be `true` if this is the final compression.
511    ///
512    /// Additionally the pointer to the output hash state should be on the top of the stack.
513    pub fn blake2s_compress(&mut self, state: Var, byte_count: Var, message: Var, finalize: bool) {
514        let instruction = self.next_instruction(
515            InstructionBody::Blake2sCompress(Blake2sCompressInstruction {
516                state: self.as_cell_ref(state, true),
517                byte_count: self.as_cell_ref(byte_count, true),
518                message: self.as_cell_ref(message, true),
519                finalize,
520            }),
521            true,
522        );
523        assert!(instruction.inc_ap);
524        assert_eq!(
525            self.main_state.allocated as usize, self.main_state.ap_change,
526            "Output var must be top of stack"
527        );
528        self.statements.push(Statement::Final(instruction));
529    }
530
531    /// Adds a label here named `name`.
532    pub fn label(&mut self, name: String) {
533        if self.reachable {
534            self.set_or_test_label_state(name.clone(), self.main_state.clone());
535        }
536        self.read_labels.insert(name.clone());
537        self.main_state = self
538            .label_state
539            .get(&name)
540            .unwrap_or_else(|| panic!("No known value for state on reaching {name}."))
541            .clone();
542        self.statements.push(Statement::Label(name, 0));
543        self.reachable = true;
544    }
545
546    /// Adds a label `name` in distance `offset` from the current point.
547    /// Useful for calling code outside of the builder's context.
548    pub fn future_label(&mut self, name: String, offset: usize) {
549        self.statements.push(Statement::Label(name, offset));
550    }
551
552    /// Rescoping the values, while ignoring all vars not stated in `vars` and giving the vars on
553    /// the left side the values of the vars on the right side.
554    pub fn rescope<const VAR_COUNT: usize>(&mut self, vars: [(Var, Var); VAR_COUNT]) {
555        self.main_state.validate_finality();
556        let values =
557            vars.map(|(new_var, value_var)| (new_var, self.main_state.get_adjusted(value_var)));
558        self.main_state.ap_change = 0;
559        self.main_state.allocated = 0;
560        self.main_state.vars.clear();
561        self.main_state.vars.extend(values);
562    }
563
564    /// Adds a call command to 'label'. All AP based variables are passed to the called function
565    /// state and dropped from the calling function state.
566    pub fn call(&mut self, label: String) {
567        self.main_state.validate_finality();
568        // Vars to be passed to the called function state.
569        let mut function_vars = OrderedHashMap::<Var, CellExpression>::default();
570        // FP based vars which will remain in the current state.
571        let mut main_vars = OrderedHashMap::<Var, CellExpression>::default();
572        let ap_change = self.main_state.ap_change;
573        let cell_to_var_flags = |cell: &CellRef| {
574            if cell.register == Register::AP { (true, false) } else { (false, true) }
575        };
576        for (var, value) in self.main_state.vars.iter() {
577            let (function_var, main_var) = match value {
578                CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
579                    cell_to_var_flags(cell)
580                }
581                CellExpression::Immediate(_) => (true, true),
582                CellExpression::BinOp { op: _, a, b } => match b {
583                    DerefOrImmediate::Deref(cell) => {
584                        if a.register == cell.register {
585                            cell_to_var_flags(cell)
586                        } else {
587                            // Mixed FP and AP based, dropped from both states.
588                            (false, false)
589                        }
590                    }
591                    DerefOrImmediate::Immediate(_) => (true, true),
592                },
593            };
594            if function_var {
595                // Apply ap change (+2 because of the call statement) and change to FP based before
596                // the function call.
597                let mut value = value.clone().unchecked_apply_known_ap_change(ap_change + 2);
598                match &mut value {
599                    CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
600                        cell.register = Register::FP
601                    }
602                    CellExpression::Immediate(_) => {}
603                    CellExpression::BinOp { a, b, .. } => {
604                        a.register = Register::FP;
605                        match b {
606                            DerefOrImmediate::Deref(cell) => cell.register = Register::FP,
607                            DerefOrImmediate::Immediate(_) => {}
608                        }
609                    }
610                }
611                function_vars.insert(*var, value);
612            }
613            if main_var {
614                main_vars.insert(*var, value.clone());
615            }
616        }
617
618        let instruction = self.next_instruction(
619            InstructionBody::Call(CallInstruction {
620                relative: true,
621                target: deref_or_immediate!(0),
622            }),
623            false,
624        );
625        self.statements.push(Statement::Jump(label.clone(), instruction));
626
627        self.main_state.vars = main_vars;
628        self.main_state.allocated = 0;
629        self.main_state.ap_change = 0;
630        let function_state = State { vars: function_vars, ..Default::default() };
631        self.set_or_test_label_state(label, function_state);
632    }
633
634    /// A return statement in the code.
635    pub fn ret(&mut self) {
636        self.main_state.validate_finality();
637        let instruction = self.next_instruction(InstructionBody::Ret(RetInstruction {}), false);
638        self.statements.push(Statement::Final(instruction));
639        self.reachable = false;
640    }
641
642    /// The number of steps at the last added statement.
643    pub fn steps(&self) -> usize {
644        self.main_state.steps
645    }
646
647    /// Resets the steps counter.
648    pub fn reset_steps(&mut self) {
649        self.main_state.steps = 0;
650    }
651
652    /// Create an assert that would always fail.
653    pub fn fail(&mut self) {
654        let cell = CellRef { offset: -1, register: Register::FP };
655        let instruction = self.next_instruction(
656            InstructionBody::AssertEq(AssertEqInstruction {
657                a: cell,
658                b: ResOperand::BinOp(BinOpOperand {
659                    op: Operation::Add,
660                    a: cell,
661                    b: DerefOrImmediate::Immediate(BigInt::one().into()),
662                }),
663            }),
664            false,
665        );
666        self.statements.push(Statement::Final(instruction));
667        self.mark_unreachable();
668    }
669
670    /// Marks the current state as unreachable.
671    /// Useful for removing bookkeeping after unsatisfiable conditions.
672    pub fn mark_unreachable(&mut self) {
673        self.reachable = false;
674    }
675
676    /// Returns `var`s value, with fixed ap if `adjust_ap` is true.
677    pub fn get_value(&self, var: Var, adjust_ap: bool) -> CellExpression {
678        if adjust_ap { self.main_state.get_adjusted(var) } else { self.main_state.get_value(var) }
679    }
680
681    /// Returns `var`s value as a cell reference, with fixed ap if `adjust_ap` is true.
682    fn as_cell_ref(&self, var: Var, adjust_ap: bool) -> CellRef {
683        extract_matches!(self.get_value(var, adjust_ap), CellExpression::Deref)
684    }
685
686    /// Returns `var`s value as a cell reference or immediate, with fixed ap if `adjust_ap` is true.
687    fn as_deref_or_imm(&self, var: Var, adjust_ap: bool) -> DerefOrImmediate {
688        match self.get_value(var, adjust_ap) {
689            CellExpression::Deref(cell) => DerefOrImmediate::Deref(cell),
690            CellExpression::Immediate(imm) => DerefOrImmediate::Immediate(imm.into()),
691            CellExpression::DoubleDeref(_, _) | CellExpression::BinOp { .. } => {
692                panic!("wrong usage.")
693            }
694        }
695    }
696
697    /// Returns `var`s value as a cell reference plus a small const offset, with fixed ap if
698    /// `adjust_ap` is true.
699    fn as_cell_ref_plus_const(
700        &self,
701        var: Var,
702        additional_offset: i16,
703        adjust_ap: bool,
704    ) -> (CellRef, i16) {
705        match self.get_value(var, adjust_ap) {
706            CellExpression::Deref(cell) => (cell, additional_offset),
707            CellExpression::BinOp {
708                op: CellOperator::Add,
709                a,
710                b: DerefOrImmediate::Immediate(imm),
711            } => (
712                a,
713                (imm.value + additional_offset).try_into().expect("Offset too large for deref."),
714            ),
715            _ => panic!("Not a valid ptr."),
716        }
717    }
718
719    /// Returns an instruction wrapping the instruction body, and updates the state.
720    /// If `inc_ap_supported` may add an `ap++` to the instruction.
721    fn next_instruction(&mut self, body: InstructionBody, inc_ap_supported: bool) -> Instruction {
722        assert!(self.reachable, "Cannot add instructions at unreachable code.");
723        let inc_ap =
724            inc_ap_supported && self.main_state.allocated as usize > self.main_state.ap_change;
725        if inc_ap {
726            self.main_state.ap_change += 1;
727        }
728        self.main_state.steps += 1;
729        let mut hints = vec![];
730        core::mem::swap(&mut hints, &mut self.current_hints);
731        Instruction { body, inc_ap, hints }
732    }
733}
734
735impl Default for CasmBuilder {
736    fn default() -> Self {
737        Self {
738            label_state: Default::default(),
739            read_labels: Default::default(),
740            main_state: Default::default(),
741            statements: Default::default(),
742            current_hints: Default::default(),
743            var_count: Default::default(),
744            reachable: true,
745        }
746    }
747}
748
749#[macro_export]
750macro_rules! casm_build_extend {
751    ($builder:expr,) => {};
752    ($builder:expr, tempvar $var:ident; $($tok:tt)*) => {
753        let $var = $builder.alloc_var(false);
754        $crate::casm_build_extend!($builder, $($tok)*)
755    };
756    ($builder:expr, localvar $var:ident; $($tok:tt)*) => {
757        let $var = $builder.alloc_var(true);
758        $crate::casm_build_extend!($builder, $($tok)*)
759    };
760    ($builder:expr, ap += $value:expr; $($tok:tt)*) => {
761        $builder.add_ap($value);
762        $crate::casm_build_extend!($builder, $($tok)*)
763    };
764    ($builder:expr, const $imm:ident = $value:expr; $($tok:tt)*) => {
765        let $imm = $builder.add_var($crate::cell_expression::CellExpression::Immediate(($value).into()));
766        $crate::casm_build_extend!($builder, $($tok)*)
767    };
768    ($builder:expr, assert $dst:ident = $res:ident; $($tok:tt)*) => {
769        $builder.assert_vars_eq($dst, $res, $crate::builder::AssertEqKind::Felt252);
770        $crate::casm_build_extend!($builder, $($tok)*)
771    };
772    ($builder:expr, assert $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
773        {
774            let __sum = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
775            $builder.assert_vars_eq($dst, __sum, $crate::builder::AssertEqKind::Felt252);
776        }
777        $crate::casm_build_extend!($builder, $($tok)*)
778    };
779    ($builder:expr, assert $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
780        {
781            let __product = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
782            $builder.assert_vars_eq($dst, __product, $crate::builder::AssertEqKind::Felt252);
783        }
784        $crate::casm_build_extend!($builder, $($tok)*)
785    };
786    ($builder:expr, assert $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
787        {
788            let __diff = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
789            $builder.assert_vars_eq($dst, __diff, $crate::builder::AssertEqKind::Felt252);
790        }
791        $crate::casm_build_extend!($builder, $($tok)*)
792    };
793    ($builder:expr, assert $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
794        {
795            let __division = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
796            $builder.assert_vars_eq($dst, __division, $crate::builder::AssertEqKind::Felt252);
797        }
798        $crate::casm_build_extend!($builder, $($tok)*)
799    };
800    ($builder:expr, assert $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
801        {
802            let __deref = $builder.double_deref($buffer, $offset);
803            $builder.assert_vars_eq($dst, __deref, $crate::builder::AssertEqKind::Felt252);
804        }
805        $crate::casm_build_extend!($builder, $($tok)*)
806    };
807    ($builder:expr, assert $dst:ident = * $buffer:ident; $($tok:tt)*) => {
808        {
809            let __deref = $builder.double_deref($buffer, 0);
810            $builder.assert_vars_eq($dst, __deref, $crate::builder::AssertEqKind::Felt252);
811        }
812        $crate::casm_build_extend!($builder, $($tok)*)
813    };
814    ($builder:expr, assert $value:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
815        $builder.buffer_write_and_inc($buffer, $value);
816        $crate::casm_build_extend!($builder, $($tok)*)
817    };
818    ($builder:expr, tempvar $var:ident = $value:ident; $($tok:tt)*) => {
819        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $value; $($tok)*);
820    };
821    ($builder:expr, tempvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
822        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs + $rhs; $($tok)*);
823    };
824    ($builder:expr, tempvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
825        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs * $rhs; $($tok)*);
826    };
827    ($builder:expr, tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
828        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs - $rhs; $($tok)*);
829    };
830    ($builder:expr, tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
831        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs / $rhs; $($tok)*);
832    };
833    ($builder:expr, tempvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
834        $crate::casm_build_extend!($builder, tempvar $var; assert $var = *($buffer++); $($tok)*);
835    };
836    ($builder:expr, tempvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
837        $crate::casm_build_extend!($builder, tempvar $var; assert $var = $buffer[$offset]; $($tok)*);
838    };
839    ($builder:expr, tempvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
840        $crate::casm_build_extend!($builder, tempvar $var; assert $var = *$buffer; $($tok)*);
841    };
842    ($builder:expr, maybe_tempvar $var:ident = $value:ident; $($tok:tt)*) => {
843        let $var = $builder.maybe_add_tempvar($value);
844        $crate::casm_build_extend!($builder, $($tok)*);
845    };
846    ($builder:expr, maybe_tempvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
847        $crate::casm_build_extend! {$builder,
848            let $var = $lhs + $rhs;
849            maybe_tempvar $var = $var;
850            $($tok)*
851        };
852    };
853    ($builder:expr, maybe_tempvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
854        $crate::casm_build_extend! {$builder,
855            let $var = $lhs * $rhs;
856            maybe_tempvar $var = $var;
857            $($tok)*
858        };
859    };
860    ($builder:expr, maybe_tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
861        $crate::casm_build_extend! {$builder,
862            let $var = $lhs - $rhs;
863            maybe_tempvar $var = $var;
864            $($tok)*
865        };
866    };
867    ($builder:expr, maybe_tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
868        $crate::casm_build_extend! {$builder,
869            let $var = $lhs / $rhs;
870            maybe_tempvar $var = $var;
871            $($tok)*
872        };
873    };
874    ($builder:expr, localvar $var:ident = $value:ident; $($tok:tt)*) => {
875        $crate::casm_build_extend!($builder, localvar $var; assert $var = $value; $($tok)*);
876    };
877    ($builder:expr, localvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
878        $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs + $rhs; $($tok)*);
879    };
880    ($builder:expr, localvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
881        $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs * $rhs; $($tok)*);
882    };
883    ($builder:expr, localvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
884        $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs - $rhs; $($tok)*);
885    };
886    ($builder:expr, localvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
887        $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs / $rhs; $($tok)*);
888    };
889    ($builder:expr, localvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
890        $crate::casm_build_extend!($builder, localvar $var; assert $var = *($buffer++); $($tok)*);
891    };
892    ($builder:expr, localvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
893        $crate::casm_build_extend!($builder, localvar $var; assert $var = $buffer[$offset]; $($tok)*);
894    };
895    ($builder:expr, localvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
896        $crate::casm_build_extend!($builder, localvar $var; assert $var = *$buffer; $($tok)*);
897    };
898    ($builder:expr, let $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
899        let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
900        $crate::casm_build_extend!($builder, $($tok)*)
901    };
902    ($builder:expr, let $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
903        let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
904        $crate::casm_build_extend!($builder, $($tok)*)
905    };
906    ($builder:expr, let $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
907        let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
908        $crate::casm_build_extend!($builder, $($tok)*)
909    };
910    ($builder:expr, let $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
911        let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
912        $crate::casm_build_extend!($builder, $($tok)*)
913    };
914    ($builder:expr, let $dst:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
915        let $dst = $builder.get_ref_and_inc($buffer);
916        $crate::casm_build_extend!($builder, $($tok)*)
917    };
918    ($builder:expr, let $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
919        let $dst = $builder.double_deref($buffer, $offset);
920        $crate::casm_build_extend!($builder, $($tok)*)
921    };
922    ($builder:expr, let $dst:ident = *$buffer:ident; $($tok:tt)*) => {
923        let $dst = $builder.double_deref($buffer, 0);
924        $crate::casm_build_extend!($builder, $($tok)*)
925    };
926    ($builder:expr, let $dst:ident = $src:ident; $($tok:tt)*) => {
927        let $dst = $builder.duplicate_var($src);
928        $crate::casm_build_extend!($builder, $($tok)*)
929    };
930    ($builder:expr, jump $target:ident; $($tok:tt)*) => {
931        $builder.jump($crate::builder::ToOwned::to_owned(core::stringify!($target)));
932        $crate::casm_build_extend!($builder, $($tok)*)
933    };
934    ($builder:expr, jump $target:ident if $condition:ident != 0; $($tok:tt)*) => {
935        $builder.jump_nz($condition, $crate::builder::ToOwned::to_owned(core::stringify!($target)));
936        $crate::casm_build_extend!($builder, $($tok)*)
937    };
938    ($builder:expr, let ($($var_name:ident),*) = call $target:ident; $($tok:tt)*) => {
939        $builder.call($crate::builder::ToOwned::to_owned(core::stringify!($target)));
940
941        let __var_count = {0i16 $(+ (stringify!($var_name), 1i16).1)*};
942        let mut __var_index = 0;
943        $(
944            let $var_name = $builder.add_var($crate::cell_expression::CellExpression::Deref($crate::operand::CellRef {
945                offset: __var_index - __var_count,
946                register: $crate::operand::Register::AP,
947            }));
948            __var_index += 1;
949        )*
950        $crate::casm_build_extend!($builder, $($tok)*)
951    };
952    ($builder:expr, ret; $($tok:tt)*) => {
953        $builder.ret();
954        $crate::casm_build_extend!($builder, $($tok)*)
955    };
956    ($builder:expr, $label:ident: $($tok:tt)*) => {
957        $builder.label($crate::builder::ToOwned::to_owned(core::stringify!($label)));
958        $crate::casm_build_extend!($builder, $($tok)*)
959    };
960    ($builder:expr, fail; $($tok:tt)*) => {
961        $builder.fail();
962        $crate::casm_build_extend!($builder, $($tok)*)
963    };
964    ($builder:expr, unsatisfiable_assert $dst:ident = $res:ident; $($tok:tt)*) => {
965        $crate::casm_build_extend!($builder, assert $dst = $res;);
966        $builder.mark_unreachable();
967        $crate::casm_build_extend!($builder, $($tok)*)
968    };
969    ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)+ {
970            $($input_name:ident $(: $input_value:ident)?),*
971        } into {
972            $($output_name:ident $(: $output_value:ident)?),*
973        }; $($tok:tt)*) => {
974        $builder.add_hint(
975            |[$($input_name),*], [$($output_name),*]| $hint_head$(::$hint_tail)+ {
976                $($input_name,)* $($output_name,)*
977            },
978            [$($crate::casm_build_hint_param_value!($input_name  $(: $input_value)?),)*],
979            [$($crate::casm_build_hint_param_value!($output_name  $(: $output_value)?),)*],
980        );
981        $crate::casm_build_extend!($builder, $($tok)*)
982    };
983    ($builder:expr, hint $hint_name:ident { $($inputs:tt)* } into { $($outputs:tt)* }; $($tok:tt)*) => {
984        $crate::casm_build_extend! {$builder,
985            hint $crate::hints::CoreHint::$hint_name { $($inputs)* } into { $($outputs)* };
986            $($tok)*
987        }
988    };
989    ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)* { $($inputs:tt)* }; $($tok:tt)*) => {
990        $crate::casm_build_extend! {$builder,
991            hint $hint_head$(::$hint_tail)* { $($inputs)* } into {};
992            $($tok)*
993        }
994    };
995    ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)* into { $($outputs:tt)* }; $($tok:tt)*) => {
996        $crate::casm_build_extend! {$builder,
997            hint $hint_head$(::$hint_tail)* {} into { $($outputs)* };
998            $($tok)*
999        }
1000    };
1001    ($builder:expr, rescope { $($new_var:ident = $value_var:ident),* }; $($tok:tt)*) => {
1002        $builder.rescope([$(($new_var, $value_var)),*]);
1003        $crate::casm_build_extend!($builder, $($tok)*)
1004    };
1005    ($builder:expr, #{ validate steps == $count:expr; } $($tok:tt)*) => {
1006        assert_eq!($builder.steps(), $count);
1007        $crate::casm_build_extend!($builder, $($tok)*)
1008    };
1009    ($builder:expr, #{ steps = 0; } $($tok:tt)*) => {
1010        $builder.reset_steps();
1011        $crate::casm_build_extend!($builder, $($tok)*)
1012    };
1013    ($builder:expr, #{ $counter:ident += steps; steps = 0; } $($tok:tt)*) => {
1014        $counter += $builder.steps() as i32;
1015        $builder.reset_steps();
1016        $crate::casm_build_extend!($builder, $($tok)*)
1017    };
1018}
1019
1020#[macro_export]
1021macro_rules! casm_build_hint_param_value {
1022    ($_name:ident : $value:ident) => {
1023        $value
1024    };
1025    ($name:ident) => {
1026        $name
1027    };
1028}