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, CallInstruction, Instruction, InstructionBody,
22 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
34#[derive(Clone, Debug, Default, Eq, PartialEq)]
36pub struct State {
37 vars: OrderedHashMap<Var, CellExpression>,
39 allocated: i16,
41 pub ap_change: usize,
43 pub steps: usize,
45}
46impl State {
47 fn get_value(&self, var: Var) -> CellExpression {
49 self.vars[&var].clone()
50 }
51
52 pub fn get_adjusted(&self, var: Var) -> CellExpression {
54 self.get_value(var).unchecked_apply_known_ap_change(self.ap_change)
55 }
56
57 pub fn get_adjusted_as_cell_ref(&self, var: Var) -> CellRef {
59 extract_matches!(self.get_adjusted(var), CellExpression::Deref)
60 }
61
62 fn validate_finality(&self) {
64 assert!(
65 self.ap_change >= self.allocated.into_or_panic(),
66 "Not enough instructions to update ap. Add an `ap += *` instruction. ap_change: {}, \
67 allocated: {}",
68 self.ap_change,
69 self.allocated,
70 );
71 }
72
73 fn intersect(&mut self, other: &Self, read_only: bool) {
76 assert_eq!(self.ap_change, other.ap_change, "Merged branches not aligned on AP change.");
77 assert_eq!(
78 self.allocated, other.allocated,
79 "Merged branches not aligned on number of allocations."
80 );
81 if read_only {
82 assert!(self.steps >= other.steps, "Finalized branch cannot be updated.");
83 } else {
84 self.steps = self.steps.max(other.steps);
85 }
86 self.vars.retain(|var, value| {
89 other
90 .vars
91 .get(var)
92 .map(|x| assert_eq!(x, value, "Var mismatch between branches."))
93 .is_some()
94 });
95 }
96}
97
98enum Statement {
100 Final(Instruction),
102 Jump(String, Instruction),
104 Label(String, usize),
107}
108
109pub struct CasmBuildResult<const BRANCH_COUNT: usize> {
111 pub instructions: Vec<Instruction>,
113 pub branches: [(State, Vec<usize>); BRANCH_COUNT],
115}
116
117pub struct CasmBuilder {
123 label_state: UnorderedHashMap<String, State>,
125 read_labels: UnorderedHashSet<String>,
127 main_state: State,
129 statements: Vec<Statement>,
131 current_hints: Vec<Hint>,
133 var_count: usize,
135 reachable: bool,
138}
139impl CasmBuilder {
140 pub fn build<const BRANCH_COUNT: usize>(
143 mut self,
144 branch_names: [&str; BRANCH_COUNT],
145 ) -> CasmBuildResult<BRANCH_COUNT> {
146 assert!(
147 self.current_hints.is_empty(),
148 "Build cannot be called with hints as the last addition."
149 );
150 let label_offsets = self.compute_label_offsets();
151 if self.reachable {
152 self.label_state.insert("Fallthrough".to_owned(), self.main_state);
153 }
154 let mut instructions = vec![];
155 let mut branch_relocations = UnorderedHashMap::<String, Vec<usize>>::default();
156 let mut offset = 0;
157 for statement in self.statements {
158 match statement {
159 Statement::Final(inst) => {
160 offset += inst.body.op_size();
161 instructions.push(inst);
162 }
163 Statement::Jump(label, mut inst) => {
164 match label_offsets.get(&label) {
165 Some(label_offset) => match &mut inst.body {
166 InstructionBody::Jnz(JnzInstruction {
167 jump_offset: DerefOrImmediate::Immediate(value),
168 ..
169 })
170 | InstructionBody::Jump(JumpInstruction {
171 target: DerefOrImmediate::Immediate(value),
172 ..
173 })
174 | InstructionBody::Call(CallInstruction {
175 target: DerefOrImmediate::Immediate(value),
176 ..
177 }) => {
178 value.value += *label_offset as i128 - offset as i128;
181 }
182
183 _ => unreachable!("Only jump or call statements should be here."),
184 },
185 None => {
186 branch_relocations.entry(label).or_default().push(instructions.len())
187 }
188 }
189 offset += inst.body.op_size();
190 instructions.push(inst);
191 }
192 Statement::Label(name, _) => {
193 self.label_state.remove(&name);
194 }
195 }
196 }
197 let branches = branch_names.map(|label| {
198 let state = self
199 .label_state
200 .remove(label)
201 .unwrap_or_else(|| panic!("Requested a non existing final label: {label:?}."));
202 state.validate_finality();
203 (state, branch_relocations.remove(label).unwrap_or_default())
204 });
205 assert!(self.label_state.is_empty(), "Did not use all branches.");
206 assert!(branch_relocations.is_empty(), "Did not use all branch relocations.");
207 CasmBuildResult { instructions, branches }
208 }
209
210 pub fn curr_ap_change(&self) -> usize {
213 self.main_state.ap_change
214 }
215
216 fn compute_label_offsets(&self) -> UnorderedHashMap<String, usize> {
218 let mut label_offsets = UnorderedHashMap::default();
219 let mut offset = 0;
220 for statement in &self.statements {
221 match statement {
222 Statement::Final(inst) | Statement::Jump(_, inst) => {
223 offset += inst.body.op_size();
224 }
225 Statement::Label(name, extra_offset) => {
226 label_offsets.insert(name.clone(), offset + extra_offset);
227 }
228 }
229 }
230 label_offsets
231 }
232
233 pub fn add_var(&mut self, value: CellExpression) -> Var {
235 let var = Var(self.var_count);
236 self.var_count += 1;
237 self.main_state.vars.insert(var, value);
238 var
239 }
240
241 pub fn alloc_var(&mut self, local_var: bool) -> Var {
243 let var = self.add_var(CellExpression::Deref(CellRef {
244 offset: self.main_state.allocated,
245 register: if local_var { Register::FP } else { Register::AP },
246 }));
247 self.main_state.allocated += 1;
248 var
249 }
250
251 pub fn duplicate_var(&mut self, var: Var) -> Var {
253 self.add_var(self.get_value(var, false))
254 }
255
256 pub fn add_hint<
259 const INPUTS_COUNT: usize,
260 const OUTPUTS_COUNT: usize,
261 THint: Into<Hint>,
262 F: FnOnce([ResOperand; INPUTS_COUNT], [CellRef; OUTPUTS_COUNT]) -> THint,
263 >(
264 &mut self,
265 f: F,
266 inputs: [Var; INPUTS_COUNT],
267 outputs: [Var; OUTPUTS_COUNT],
268 ) {
269 self.current_hints.push(
270 f(
271 inputs.map(|v| match self.get_value(v, true) {
272 CellExpression::Deref(cell) => ResOperand::Deref(cell),
273 CellExpression::DoubleDeref(cell, offset) => {
274 ResOperand::DoubleDeref(cell, offset)
275 }
276 CellExpression::Immediate(imm) => imm.into(),
277 CellExpression::BinOp { op, a: other, b } => match op {
278 CellOperator::Add => {
279 ResOperand::BinOp(BinOpOperand { op: Operation::Add, a: other, b })
280 }
281 CellOperator::Mul => {
282 ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a: other, b })
283 }
284 CellOperator::Sub | CellOperator::Div => {
285 panic!("hints to non ResOperand references are not supported.")
286 }
287 },
288 }),
289 outputs.map(|v| self.as_cell_ref(v, true)),
290 )
291 .into(),
292 );
293 }
294
295 pub fn assert_vars_eq(&mut self, dst: Var, res: Var) {
298 let a = self.as_cell_ref(dst, true);
299 let b = self.get_value(res, true);
300 let (a, b) = match b {
301 CellExpression::Deref(cell) => (a, ResOperand::Deref(cell)),
302 CellExpression::DoubleDeref(cell, offset) => (a, ResOperand::DoubleDeref(cell, offset)),
303 CellExpression::Immediate(imm) => (a, imm.into()),
304 CellExpression::BinOp { op, a: other, b } => match op {
305 CellOperator::Add => {
306 (a, ResOperand::BinOp(BinOpOperand { op: Operation::Add, a: other, b }))
307 }
308 CellOperator::Mul => {
309 (a, ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a: other, b }))
310 }
311 CellOperator::Sub => {
312 (other, ResOperand::BinOp(BinOpOperand { op: Operation::Add, a, b }))
313 }
314 CellOperator::Div => {
315 (other, ResOperand::BinOp(BinOpOperand { op: Operation::Mul, a, b }))
316 }
317 },
318 };
319 let instruction =
320 self.next_instruction(InstructionBody::AssertEq(AssertEqInstruction { a, b }), true);
321 self.statements.push(Statement::Final(instruction));
322 }
323
324 pub fn buffer_write_and_inc(&mut self, buffer: Var, value: Var) {
329 let (cell, offset) = self.buffer_get_and_inc(buffer);
330 let location = self.add_var(CellExpression::DoubleDeref(cell, offset));
331 self.assert_vars_eq(value, location);
332 }
333
334 pub fn maybe_add_tempvar(&mut self, var: Var) -> Var {
338 self.add_var(CellExpression::Deref(match self.get_value(var, false) {
339 CellExpression::Deref(cell) => cell,
340 CellExpression::BinOp {
341 op: CellOperator::Add | CellOperator::Sub,
342 a,
343 b: DerefOrImmediate::Immediate(imm),
344 } if imm.value.is_zero() => a,
345 CellExpression::BinOp {
346 op: CellOperator::Mul | CellOperator::Div,
347 a,
348 b: DerefOrImmediate::Immediate(imm),
349 } if imm.value.is_one() => a,
350 _ => {
351 let temp = self.alloc_var(false);
352 self.assert_vars_eq(temp, var);
353 return temp;
354 }
355 }))
356 }
357
358 pub fn get_ref_and_inc(&mut self, buffer: Var) -> Var {
360 let (cell, offset) = self.as_cell_ref_plus_const(buffer, 0, false);
361 self.main_state.vars.insert(buffer, CellExpression::BinOp {
362 op: CellOperator::Add,
363 a: cell,
364 b: deref_or_immediate!(BigInt::from(offset) + 1),
365 });
366 self.add_var(CellExpression::DoubleDeref(cell, offset))
367 }
368
369 fn buffer_get_and_inc(&mut self, buffer: Var) -> (CellRef, i16) {
373 let (base, offset) = match self.get_value(buffer, false) {
374 CellExpression::Deref(cell) => (cell, 0),
375 CellExpression::BinOp {
376 op: CellOperator::Add,
377 a,
378 b: DerefOrImmediate::Immediate(imm),
379 } => (a, imm.value.try_into().expect("Too many buffer writes.")),
380 _ => panic!("Not a valid buffer."),
381 };
382 self.main_state.vars.insert(buffer, CellExpression::BinOp {
383 op: CellOperator::Add,
384 a: base,
385 b: deref_or_immediate!(offset + 1),
386 });
387 (base, offset)
388 }
389
390 pub fn add_ap(&mut self, size: usize) {
392 let instruction = self.next_instruction(
393 InstructionBody::AddAp(AddApInstruction { operand: BigInt::from(size).into() }),
394 false,
395 );
396 self.statements.push(Statement::Final(instruction));
397 self.main_state.ap_change += size;
398 }
399
400 pub fn increase_ap_change(&mut self, amount: usize) {
402 self.main_state.ap_change += amount;
403 self.main_state.allocated += amount.into_or_panic::<i16>();
404 }
405
406 pub fn bin_op(&mut self, op: CellOperator, lhs: Var, rhs: Var) -> Var {
409 let (a, b) = match self.get_value(lhs, false) {
410 CellExpression::Deref(cell) => (cell, self.as_deref_or_imm(rhs, false)),
412 CellExpression::BinOp {
414 op: CellOperator::Add,
415 a,
416 b: DerefOrImmediate::Immediate(imm),
417 } if op == CellOperator::Add => (
418 a,
419 DerefOrImmediate::Immediate(
420 (imm.value
421 + extract_matches!(self.get_value(rhs, false), CellExpression::Immediate))
422 .into(),
423 ),
424 ),
425 _ => panic!(
426 "`bin_op` is supported only between a `deref` and a `deref_or_imm`, or a \
427 `add_with_const` and `imm`."
428 ),
429 };
430 self.add_var(CellExpression::BinOp { op, a, b })
431 }
432
433 pub fn double_deref(&mut self, var: Var, offset: i16) -> Var {
436 let (cell, full_offset) = self.as_cell_ref_plus_const(var, offset, false);
437 self.add_var(CellExpression::DoubleDeref(cell, full_offset))
438 }
439
440 fn set_or_test_label_state(&mut self, label: String, state: State) {
443 match self.label_state.entry(label) {
444 Entry::Occupied(e) => {
445 let read_only = self.read_labels.contains(e.key());
446 e.into_mut().intersect(&state, read_only);
447 }
448 Entry::Vacant(e) => {
449 e.insert(state);
450 }
451 }
452 }
453
454 pub fn jump(&mut self, label: String) {
456 let instruction = self.next_instruction(
457 InstructionBody::Jump(JumpInstruction {
458 target: deref_or_immediate!(0),
459 relative: true,
460 }),
461 true,
462 );
463 self.statements.push(Statement::Jump(label.clone(), instruction));
464 let mut state = State::default();
465 core::mem::swap(&mut state, &mut self.main_state);
466 self.set_or_test_label_state(label, state);
467 self.reachable = false;
468 }
469
470 pub fn jump_nz(&mut self, condition: Var, label: String) {
473 let cell = self.as_cell_ref(condition, true);
474 let instruction = self.next_instruction(
475 InstructionBody::Jnz(JnzInstruction {
476 condition: cell,
477 jump_offset: deref_or_immediate!(0),
478 }),
479 true,
480 );
481 self.statements.push(Statement::Jump(label.clone(), instruction));
482 self.set_or_test_label_state(label, self.main_state.clone());
483 }
484
485 pub fn label(&mut self, name: String) {
487 if self.reachable {
488 self.set_or_test_label_state(name.clone(), self.main_state.clone());
489 }
490 self.read_labels.insert(name.clone());
491 self.main_state = self
492 .label_state
493 .get(&name)
494 .unwrap_or_else(|| panic!("No known value for state on reaching {name}."))
495 .clone();
496 self.statements.push(Statement::Label(name, 0));
497 self.reachable = true;
498 }
499
500 pub fn future_label(&mut self, name: String, offset: usize) {
503 self.statements.push(Statement::Label(name, offset));
504 }
505
506 pub fn rescope<const VAR_COUNT: usize>(&mut self, vars: [(Var, Var); VAR_COUNT]) {
509 self.main_state.validate_finality();
510 let values =
511 vars.map(|(new_var, value_var)| (new_var, self.main_state.get_adjusted(value_var)));
512 self.main_state.ap_change = 0;
513 self.main_state.allocated = 0;
514 self.main_state.vars.clear();
515 self.main_state.vars.extend(values);
516 }
517
518 pub fn call(&mut self, label: String) {
521 self.main_state.validate_finality();
522 let mut function_vars = OrderedHashMap::<Var, CellExpression>::default();
524 let mut main_vars = OrderedHashMap::<Var, CellExpression>::default();
526 let ap_change = self.main_state.ap_change;
527 let cell_to_var_flags = |cell: &CellRef| {
528 if cell.register == Register::AP { (true, false) } else { (false, true) }
529 };
530 for (var, value) in self.main_state.vars.iter() {
531 let (function_var, main_var) = match value {
532 CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
533 cell_to_var_flags(cell)
534 }
535 CellExpression::Immediate(_) => (true, true),
536 CellExpression::BinOp { op: _, a, b } => match b {
537 DerefOrImmediate::Deref(cell) => {
538 if a.register == cell.register {
539 cell_to_var_flags(cell)
540 } else {
541 (false, false)
543 }
544 }
545 DerefOrImmediate::Immediate(_) => (true, true),
546 },
547 };
548 if function_var {
549 let mut value = value.clone().unchecked_apply_known_ap_change(ap_change + 2);
552 match &mut value {
553 CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
554 cell.register = Register::FP
555 }
556 CellExpression::Immediate(_) => {}
557 CellExpression::BinOp { a, b, .. } => {
558 a.register = Register::FP;
559 match b {
560 DerefOrImmediate::Deref(cell) => cell.register = Register::FP,
561 DerefOrImmediate::Immediate(_) => {}
562 }
563 }
564 }
565 function_vars.insert(*var, value);
566 }
567 if main_var {
568 main_vars.insert(*var, value.clone());
569 }
570 }
571
572 let instruction = self.next_instruction(
573 InstructionBody::Call(CallInstruction {
574 relative: true,
575 target: deref_or_immediate!(0),
576 }),
577 false,
578 );
579 self.statements.push(Statement::Jump(label.clone(), instruction));
580
581 self.main_state.vars = main_vars;
582 self.main_state.allocated = 0;
583 self.main_state.ap_change = 0;
584 let function_state = State { vars: function_vars, ..Default::default() };
585 self.set_or_test_label_state(label, function_state);
586 }
587
588 pub fn ret(&mut self) {
590 self.main_state.validate_finality();
591 let instruction = self.next_instruction(InstructionBody::Ret(RetInstruction {}), false);
592 self.statements.push(Statement::Final(instruction));
593 self.reachable = false;
594 }
595
596 pub fn steps(&self) -> usize {
598 self.main_state.steps
599 }
600
601 pub fn reset_steps(&mut self) {
603 self.main_state.steps = 0;
604 }
605
606 pub fn fail(&mut self) {
608 let cell = CellRef { offset: -1, register: Register::FP };
609 let instruction = self.next_instruction(
610 InstructionBody::AssertEq(AssertEqInstruction {
611 a: cell,
612 b: ResOperand::BinOp(BinOpOperand {
613 op: Operation::Add,
614 a: cell,
615 b: DerefOrImmediate::Immediate(BigInt::one().into()),
616 }),
617 }),
618 false,
619 );
620 self.statements.push(Statement::Final(instruction));
621 self.mark_unreachable();
622 }
623
624 pub fn mark_unreachable(&mut self) {
627 self.reachable = false;
628 }
629
630 pub fn get_value(&self, var: Var, adjust_ap: bool) -> CellExpression {
632 if adjust_ap { self.main_state.get_adjusted(var) } else { self.main_state.get_value(var) }
633 }
634
635 fn as_cell_ref(&self, var: Var, adjust_ap: bool) -> CellRef {
637 extract_matches!(self.get_value(var, adjust_ap), CellExpression::Deref)
638 }
639
640 fn as_deref_or_imm(&self, var: Var, adjust_ap: bool) -> DerefOrImmediate {
642 match self.get_value(var, adjust_ap) {
643 CellExpression::Deref(cell) => DerefOrImmediate::Deref(cell),
644 CellExpression::Immediate(imm) => DerefOrImmediate::Immediate(imm.into()),
645 CellExpression::DoubleDeref(_, _) | CellExpression::BinOp { .. } => {
646 panic!("wrong usage.")
647 }
648 }
649 }
650
651 fn as_cell_ref_plus_const(
654 &self,
655 var: Var,
656 additional_offset: i16,
657 adjust_ap: bool,
658 ) -> (CellRef, i16) {
659 match self.get_value(var, adjust_ap) {
660 CellExpression::Deref(cell) => (cell, additional_offset),
661 CellExpression::BinOp {
662 op: CellOperator::Add,
663 a,
664 b: DerefOrImmediate::Immediate(imm),
665 } => (
666 a,
667 (imm.value + additional_offset).try_into().expect("Offset too large for deref."),
668 ),
669 _ => panic!("Not a valid ptr."),
670 }
671 }
672
673 fn next_instruction(&mut self, body: InstructionBody, inc_ap_supported: bool) -> Instruction {
676 assert!(self.reachable, "Cannot add instructions at unreachable code.");
677 let inc_ap =
678 inc_ap_supported && self.main_state.allocated as usize > self.main_state.ap_change;
679 if inc_ap {
680 self.main_state.ap_change += 1;
681 }
682 self.main_state.steps += 1;
683 let mut hints = vec![];
684 core::mem::swap(&mut hints, &mut self.current_hints);
685 Instruction { body, inc_ap, hints }
686 }
687}
688
689impl Default for CasmBuilder {
690 fn default() -> Self {
691 Self {
692 label_state: Default::default(),
693 read_labels: Default::default(),
694 main_state: Default::default(),
695 statements: Default::default(),
696 current_hints: Default::default(),
697 var_count: Default::default(),
698 reachable: true,
699 }
700 }
701}
702
703#[macro_export]
704macro_rules! casm_build_extend {
705 ($builder:ident,) => {};
706 ($builder:ident, tempvar $var:ident; $($tok:tt)*) => {
707 let $var = $builder.alloc_var(false);
708 $crate::casm_build_extend!($builder, $($tok)*)
709 };
710 ($builder:ident, localvar $var:ident; $($tok:tt)*) => {
711 let $var = $builder.alloc_var(true);
712 $crate::casm_build_extend!($builder, $($tok)*)
713 };
714 ($builder:ident, ap += $value:expr; $($tok:tt)*) => {
715 $builder.add_ap($value);
716 $crate::casm_build_extend!($builder, $($tok)*)
717 };
718 ($builder:ident, const $imm:ident = $value:expr; $($tok:tt)*) => {
719 let $imm = $builder.add_var($crate::cell_expression::CellExpression::Immediate(($value).into()));
720 $crate::casm_build_extend!($builder, $($tok)*)
721 };
722 ($builder:ident, assert $dst:ident = $res:ident; $($tok:tt)*) => {
723 $builder.assert_vars_eq($dst, $res);
724 $crate::casm_build_extend!($builder, $($tok)*)
725 };
726 ($builder:ident, assert $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
727 {
728 let __sum = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
729 $builder.assert_vars_eq($dst, __sum);
730 }
731 $crate::casm_build_extend!($builder, $($tok)*)
732 };
733 ($builder:ident, assert $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
734 {
735 let __product = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
736 $builder.assert_vars_eq($dst, __product);
737 }
738 $crate::casm_build_extend!($builder, $($tok)*)
739 };
740 ($builder:ident, assert $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
741 {
742 let __diff = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
743 $builder.assert_vars_eq($dst, __diff);
744 }
745 $crate::casm_build_extend!($builder, $($tok)*)
746 };
747 ($builder:ident, assert $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
748 {
749 let __division = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
750 $builder.assert_vars_eq($dst, __division);
751 }
752 $crate::casm_build_extend!($builder, $($tok)*)
753 };
754 ($builder:ident, assert $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
755 {
756 let __deref = $builder.double_deref($buffer, $offset);
757 $builder.assert_vars_eq($dst, __deref);
758 }
759 $crate::casm_build_extend!($builder, $($tok)*)
760 };
761 ($builder:ident, assert $dst:ident = * $buffer:ident; $($tok:tt)*) => {
762 {
763 let __deref = $builder.double_deref($buffer, 0);
764 $builder.assert_vars_eq($dst, __deref);
765 }
766 $crate::casm_build_extend!($builder, $($tok)*)
767 };
768 ($builder:ident, assert $value:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
769 $builder.buffer_write_and_inc($buffer, $value);
770 $crate::casm_build_extend!($builder, $($tok)*)
771 };
772 ($builder:ident, tempvar $var:ident = $value:ident; $($tok:tt)*) => {
773 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $value; $($tok)*);
774 };
775 ($builder:ident, tempvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
776 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs + $rhs; $($tok)*);
777 };
778 ($builder:ident, tempvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
779 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs * $rhs; $($tok)*);
780 };
781 ($builder:ident, tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
782 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs - $rhs; $($tok)*);
783 };
784 ($builder:ident, tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
785 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs / $rhs; $($tok)*);
786 };
787 ($builder:ident, tempvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
788 $crate::casm_build_extend!($builder, tempvar $var; assert $var = *($buffer++); $($tok)*);
789 };
790 ($builder:ident, tempvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
791 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $buffer[$offset]; $($tok)*);
792 };
793 ($builder:ident, tempvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
794 $crate::casm_build_extend!($builder, tempvar $var; assert $var = *$buffer; $($tok)*);
795 };
796 ($builder:ident, maybe_tempvar $var:ident = $value:ident; $($tok:tt)*) => {
797 let $var = $builder.maybe_add_tempvar($value);
798 $crate::casm_build_extend!($builder, $($tok)*);
799 };
800 ($builder:ident, maybe_tempvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
801 $crate::casm_build_extend! {$builder,
802 let $var = $lhs + $rhs;
803 maybe_tempvar $var = $var;
804 $($tok)*
805 };
806 };
807 ($builder:ident, maybe_tempvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
808 $crate::casm_build_extend! {$builder,
809 let $var = $lhs * $rhs;
810 maybe_tempvar $var = $var;
811 $($tok)*
812 };
813 };
814 ($builder:ident, maybe_tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
815 $crate::casm_build_extend! {$builder,
816 let $var = $lhs - $rhs;
817 maybe_tempvar $var = $var;
818 $($tok)*
819 };
820 };
821 ($builder:ident, maybe_tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
822 $crate::casm_build_extend! {$builder,
823 let $var = $lhs / $rhs;
824 maybe_tempvar $var = $var;
825 $($tok)*
826 };
827 };
828 ($builder:ident, localvar $var:ident = $value:ident; $($tok:tt)*) => {
829 $crate::casm_build_extend!($builder, localvar $var; assert $var = $value; $($tok)*);
830 };
831 ($builder:ident, localvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
832 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs + $rhs; $($tok)*);
833 };
834 ($builder:ident, localvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
835 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs * $rhs; $($tok)*);
836 };
837 ($builder:ident, localvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
838 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs - $rhs; $($tok)*);
839 };
840 ($builder:ident, localvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
841 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs / $rhs; $($tok)*);
842 };
843 ($builder:ident, localvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
844 $crate::casm_build_extend!($builder, localvar $var; assert $var = *($buffer++); $($tok)*);
845 };
846 ($builder:ident, localvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
847 $crate::casm_build_extend!($builder, localvar $var; assert $var = $buffer[$offset]; $($tok)*);
848 };
849 ($builder:ident, localvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
850 $crate::casm_build_extend!($builder, localvar $var; assert $var = *$buffer; $($tok)*);
851 };
852 ($builder:ident, let $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
853 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
854 $crate::casm_build_extend!($builder, $($tok)*)
855 };
856 ($builder:ident, let $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
857 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
858 $crate::casm_build_extend!($builder, $($tok)*)
859 };
860 ($builder:ident, let $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
861 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
862 $crate::casm_build_extend!($builder, $($tok)*)
863 };
864 ($builder:ident, let $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
865 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
866 $crate::casm_build_extend!($builder, $($tok)*)
867 };
868 ($builder:ident, let $dst:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
869 let $dst = $builder.get_ref_and_inc($buffer);
870 $crate::casm_build_extend!($builder, $($tok)*)
871 };
872 ($builder:ident, let $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
873 let $dst = $builder.double_deref($buffer, $offset);
874 $crate::casm_build_extend!($builder, $($tok)*)
875 };
876 ($builder:ident, let $dst:ident = *$buffer:ident; $($tok:tt)*) => {
877 let $dst = $builder.double_deref($buffer, 0);
878 $crate::casm_build_extend!($builder, $($tok)*)
879 };
880 ($builder:ident, let $dst:ident = $src:ident; $($tok:tt)*) => {
881 let $dst = $builder.duplicate_var($src);
882 $crate::casm_build_extend!($builder, $($tok)*)
883 };
884 ($builder:ident, jump $target:ident; $($tok:tt)*) => {
885 $builder.jump($crate::builder::ToOwned::to_owned(core::stringify!($target)));
886 $crate::casm_build_extend!($builder, $($tok)*)
887 };
888 ($builder:ident, jump $target:ident if $condition:ident != 0; $($tok:tt)*) => {
889 $builder.jump_nz($condition, $crate::builder::ToOwned::to_owned(core::stringify!($target)));
890 $crate::casm_build_extend!($builder, $($tok)*)
891 };
892 ($builder:ident, let ($($var_name:ident),*) = call $target:ident; $($tok:tt)*) => {
893 $builder.call($crate::builder::ToOwned::to_owned(core::stringify!($target)));
894
895 let __var_count = {0i16 $(+ (stringify!($var_name), 1i16).1)*};
896 let mut __var_index = 0;
897 $(
898 let $var_name = $builder.add_var($crate::cell_expression::CellExpression::Deref($crate::operand::CellRef {
899 offset: __var_index - __var_count,
900 register: $crate::operand::Register::AP,
901 }));
902 __var_index += 1;
903 )*
904 $crate::casm_build_extend!($builder, $($tok)*)
905 };
906 ($builder:ident, ret; $($tok:tt)*) => {
907 $builder.ret();
908 $crate::casm_build_extend!($builder, $($tok)*)
909 };
910 ($builder:ident, $label:ident: $($tok:tt)*) => {
911 $builder.label($crate::builder::ToOwned::to_owned(core::stringify!($label)));
912 $crate::casm_build_extend!($builder, $($tok)*)
913 };
914 ($builder:ident, fail; $($tok:tt)*) => {
915 $builder.fail();
916 $crate::casm_build_extend!($builder, $($tok)*)
917 };
918 ($builder:ident, unsatisfiable_assert $dst:ident = $res:ident; $($tok:tt)*) => {
919 $crate::casm_build_extend!($builder, assert $dst = $res;);
920 $builder.mark_unreachable();
921 $crate::casm_build_extend!($builder, $($tok)*)
922 };
923 ($builder:ident, hint $hint_head:ident$(::$hint_tail:ident)+ {
924 $($input_name:ident $(: $input_value:ident)?),*
925 } into {
926 $($output_name:ident $(: $output_value:ident)?),*
927 }; $($tok:tt)*) => {
928 $builder.add_hint(
929 |[$($input_name),*], [$($output_name),*]| $hint_head$(::$hint_tail)+ {
930 $($input_name,)* $($output_name,)*
931 },
932 [$($crate::casm_build_hint_param_value!($input_name $(: $input_value)?),)*],
933 [$($crate::casm_build_hint_param_value!($output_name $(: $output_value)?),)*],
934 );
935 $crate::casm_build_extend!($builder, $($tok)*)
936 };
937 ($builder:ident, hint $hint_name:ident { $($inputs:tt)* } into { $($outputs:tt)* }; $($tok:tt)*) => {
938 $crate::casm_build_extend! {$builder,
939 hint $crate::hints::CoreHint::$hint_name { $($inputs)* } into { $($outputs)* };
940 $($tok)*
941 }
942 };
943 ($builder:ident, hint $hint_head:ident$(::$hint_tail:ident)* { $($inputs:tt)* }; $($tok:tt)*) => {
944 $crate::casm_build_extend! {$builder,
945 hint $hint_head$(::$hint_tail)* { $($inputs)* } into {};
946 $($tok)*
947 }
948 };
949 ($builder:ident, hint $hint_head:ident$(::$hint_tail:ident)* into { $($outputs:tt)* }; $($tok:tt)*) => {
950 $crate::casm_build_extend! {$builder,
951 hint $hint_head$(::$hint_tail)* {} into { $($outputs)* };
952 $($tok)*
953 }
954 };
955 ($builder:ident, rescope { $($new_var:ident = $value_var:ident),* }; $($tok:tt)*) => {
956 $builder.rescope([$(($new_var, $value_var)),*]);
957 $crate::casm_build_extend!($builder, $($tok)*)
958 };
959 ($builder:ident, #{ validate steps == $count:expr; } $($tok:tt)*) => {
960 assert_eq!($builder.steps(), $count);
961 $crate::casm_build_extend!($builder, $($tok)*)
962 };
963 ($builder:ident, #{ steps = 0; } $($tok:tt)*) => {
964 $builder.reset_steps();
965 $crate::casm_build_extend!($builder, $($tok)*)
966 };
967 ($builder:ident, #{ $counter:ident += steps; steps = 0; } $($tok:tt)*) => {
968 $counter += $builder.steps() as i32;
969 $builder.reset_steps();
970 $crate::casm_build_extend!($builder, $($tok)*)
971 };
972}
973
974#[macro_export]
975macro_rules! casm_build_hint_param_value {
976 ($_name:ident : $value:ident) => {
977 $value
978 };
979 ($name:ident) => {
980 $name
981 };
982}