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#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
32pub struct Var(usize);
33
34pub enum AssertEqKind {
38 Felt252,
39 QM31,
40}
41
42#[derive(Clone, Debug, Default, Eq, PartialEq)]
44pub struct State {
45 vars: OrderedHashMap<Var, CellExpression>,
47 allocated: i16,
49 pub ap_change: usize,
51 pub steps: usize,
53}
54impl State {
55 fn get_value(&self, var: Var) -> CellExpression {
57 self.vars[&var].clone()
58 }
59
60 pub fn get_adjusted(&self, var: Var) -> CellExpression {
62 self.get_value(var).unchecked_apply_known_ap_change(self.ap_change)
63 }
64
65 pub fn get_adjusted_as_cell_ref(&self, var: Var) -> CellRef {
67 extract_matches!(self.get_adjusted(var), CellExpression::Deref)
68 }
69
70 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 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 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
106enum Statement {
108 Final(Instruction),
110 Jump(String, Instruction),
112 Label(String, usize),
115}
116
117pub struct CasmBuildResult<const BRANCH_COUNT: usize> {
119 pub instructions: Vec<Instruction>,
121 pub branches: [(State, Vec<usize>); BRANCH_COUNT],
123}
124
125pub struct CasmBuilder {
131 label_state: UnorderedHashMap<String, State>,
133 read_labels: UnorderedHashSet<String>,
135 main_state: State,
137 statements: Vec<Statement>,
139 current_hints: Vec<Hint>,
141 var_count: usize,
143 reachable: bool,
146}
147impl CasmBuilder {
148 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 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 pub fn curr_ap_change(&self) -> usize {
221 self.main_state.ap_change
222 }
223
224 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 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 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 pub fn duplicate_var(&mut self, var: Var) -> Var {
261 self.add_var(self.get_value(var, false))
262 }
263
264 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 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 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 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 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 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 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 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 pub fn bin_op(&mut self, op: CellOperator, lhs: Var, rhs: Var) -> Var {
429 let (a, b) = match self.get_value(lhs, false) {
430 CellExpression::Deref(cell) => (cell, self.as_deref_or_imm(rhs, false)),
432 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 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 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 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 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 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 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 pub fn future_label(&mut self, name: String, offset: usize) {
549 self.statements.push(Statement::Label(name, offset));
550 }
551
552 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 pub fn call(&mut self, label: String) {
567 self.main_state.validate_finality();
568 let mut function_vars = OrderedHashMap::<Var, CellExpression>::default();
570 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 (false, false)
589 }
590 }
591 DerefOrImmediate::Immediate(_) => (true, true),
592 },
593 };
594 if function_var {
595 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 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 pub fn steps(&self) -> usize {
644 self.main_state.steps
645 }
646
647 pub fn reset_steps(&mut self) {
649 self.main_state.steps = 0;
650 }
651
652 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 pub fn mark_unreachable(&mut self) {
673 self.reachable = false;
674 }
675
676 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 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 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 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 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}