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(
362 buffer,
363 CellExpression::BinOp {
364 op: CellOperator::Add,
365 a: cell,
366 b: deref_or_immediate!(BigInt::from(offset) + 1),
367 },
368 );
369 self.add_var(CellExpression::DoubleDeref(cell, offset))
370 }
371
372 fn buffer_get_and_inc(&mut self, buffer: Var) -> (CellRef, i16) {
376 let (base, offset) = match self.get_value(buffer, false) {
377 CellExpression::Deref(cell) => (cell, 0),
378 CellExpression::BinOp {
379 op: CellOperator::Add,
380 a,
381 b: DerefOrImmediate::Immediate(imm),
382 } => (a, imm.value.try_into().expect("Too many buffer writes.")),
383 _ => panic!("Not a valid buffer."),
384 };
385 self.main_state.vars.insert(
386 buffer,
387 CellExpression::BinOp {
388 op: CellOperator::Add,
389 a: base,
390 b: deref_or_immediate!(offset + 1),
391 },
392 );
393 (base, offset)
394 }
395
396 pub fn add_ap(&mut self, size: usize) {
398 let instruction = self.next_instruction(
399 InstructionBody::AddAp(AddApInstruction { operand: BigInt::from(size).into() }),
400 false,
401 );
402 self.statements.push(Statement::Final(instruction));
403 self.main_state.ap_change += size;
404 }
405
406 pub fn increase_ap_change(&mut self, amount: usize) {
408 self.main_state.ap_change += amount;
409 self.main_state.allocated += amount.into_or_panic::<i16>();
410 }
411
412 pub fn bin_op(&mut self, op: CellOperator, lhs: Var, rhs: Var) -> Var {
415 let (a, b) = match self.get_value(lhs, false) {
416 CellExpression::Deref(cell) => (cell, self.as_deref_or_imm(rhs, false)),
418 CellExpression::BinOp {
420 op: CellOperator::Add,
421 a,
422 b: DerefOrImmediate::Immediate(imm),
423 } if op == CellOperator::Add => (
424 a,
425 DerefOrImmediate::Immediate(
426 (imm.value
427 + extract_matches!(self.get_value(rhs, false), CellExpression::Immediate))
428 .into(),
429 ),
430 ),
431 _ => panic!(
432 "`bin_op` is supported only between a `deref` and a `deref_or_imm`, or a \
433 `add_with_const` and `imm`."
434 ),
435 };
436 self.add_var(CellExpression::BinOp { op, a, b })
437 }
438
439 pub fn double_deref(&mut self, var: Var, offset: i16) -> Var {
442 let (cell, full_offset) = self.as_cell_ref_plus_const(var, offset, false);
443 self.add_var(CellExpression::DoubleDeref(cell, full_offset))
444 }
445
446 fn set_or_test_label_state(&mut self, label: String, state: State) {
449 match self.label_state.entry(label) {
450 Entry::Occupied(e) => {
451 let read_only = self.read_labels.contains(e.key());
452 e.into_mut().intersect(&state, read_only);
453 }
454 Entry::Vacant(e) => {
455 e.insert(state);
456 }
457 }
458 }
459
460 pub fn jump(&mut self, label: String) {
462 let instruction = self.next_instruction(
463 InstructionBody::Jump(JumpInstruction {
464 target: deref_or_immediate!(0),
465 relative: true,
466 }),
467 true,
468 );
469 self.statements.push(Statement::Jump(label.clone(), instruction));
470 let mut state = State::default();
471 core::mem::swap(&mut state, &mut self.main_state);
472 self.set_or_test_label_state(label, state);
473 self.reachable = false;
474 }
475
476 pub fn jump_nz(&mut self, condition: Var, label: String) {
479 let cell = self.as_cell_ref(condition, true);
480 let instruction = self.next_instruction(
481 InstructionBody::Jnz(JnzInstruction {
482 condition: cell,
483 jump_offset: deref_or_immediate!(0),
484 }),
485 true,
486 );
487 self.statements.push(Statement::Jump(label.clone(), instruction));
488 self.set_or_test_label_state(label, self.main_state.clone());
489 }
490
491 pub fn label(&mut self, name: String) {
493 if self.reachable {
494 self.set_or_test_label_state(name.clone(), self.main_state.clone());
495 }
496 self.read_labels.insert(name.clone());
497 self.main_state = self
498 .label_state
499 .get(&name)
500 .unwrap_or_else(|| panic!("No known value for state on reaching {name}."))
501 .clone();
502 self.statements.push(Statement::Label(name, 0));
503 self.reachable = true;
504 }
505
506 pub fn future_label(&mut self, name: String, offset: usize) {
509 self.statements.push(Statement::Label(name, offset));
510 }
511
512 pub fn rescope<const VAR_COUNT: usize>(&mut self, vars: [(Var, Var); VAR_COUNT]) {
515 self.main_state.validate_finality();
516 let values =
517 vars.map(|(new_var, value_var)| (new_var, self.main_state.get_adjusted(value_var)));
518 self.main_state.ap_change = 0;
519 self.main_state.allocated = 0;
520 self.main_state.vars.clear();
521 self.main_state.vars.extend(values);
522 }
523
524 pub fn call(&mut self, label: String) {
527 self.main_state.validate_finality();
528 let mut function_vars = OrderedHashMap::<Var, CellExpression>::default();
530 let mut main_vars = OrderedHashMap::<Var, CellExpression>::default();
532 let ap_change = self.main_state.ap_change;
533 let cell_to_var_flags = |cell: &CellRef| {
534 if cell.register == Register::AP { (true, false) } else { (false, true) }
535 };
536 for (var, value) in self.main_state.vars.iter() {
537 let (function_var, main_var) = match value {
538 CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
539 cell_to_var_flags(cell)
540 }
541 CellExpression::Immediate(_) => (true, true),
542 CellExpression::BinOp { op: _, a, b } => match b {
543 DerefOrImmediate::Deref(cell) => {
544 if a.register == cell.register {
545 cell_to_var_flags(cell)
546 } else {
547 (false, false)
549 }
550 }
551 DerefOrImmediate::Immediate(_) => (true, true),
552 },
553 };
554 if function_var {
555 let mut value = value.clone().unchecked_apply_known_ap_change(ap_change + 2);
558 match &mut value {
559 CellExpression::DoubleDeref(cell, _) | CellExpression::Deref(cell) => {
560 cell.register = Register::FP
561 }
562 CellExpression::Immediate(_) => {}
563 CellExpression::BinOp { a, b, .. } => {
564 a.register = Register::FP;
565 match b {
566 DerefOrImmediate::Deref(cell) => cell.register = Register::FP,
567 DerefOrImmediate::Immediate(_) => {}
568 }
569 }
570 }
571 function_vars.insert(*var, value);
572 }
573 if main_var {
574 main_vars.insert(*var, value.clone());
575 }
576 }
577
578 let instruction = self.next_instruction(
579 InstructionBody::Call(CallInstruction {
580 relative: true,
581 target: deref_or_immediate!(0),
582 }),
583 false,
584 );
585 self.statements.push(Statement::Jump(label.clone(), instruction));
586
587 self.main_state.vars = main_vars;
588 self.main_state.allocated = 0;
589 self.main_state.ap_change = 0;
590 let function_state = State { vars: function_vars, ..Default::default() };
591 self.set_or_test_label_state(label, function_state);
592 }
593
594 pub fn ret(&mut self) {
596 self.main_state.validate_finality();
597 let instruction = self.next_instruction(InstructionBody::Ret(RetInstruction {}), false);
598 self.statements.push(Statement::Final(instruction));
599 self.reachable = false;
600 }
601
602 pub fn steps(&self) -> usize {
604 self.main_state.steps
605 }
606
607 pub fn reset_steps(&mut self) {
609 self.main_state.steps = 0;
610 }
611
612 pub fn fail(&mut self) {
614 let cell = CellRef { offset: -1, register: Register::FP };
615 let instruction = self.next_instruction(
616 InstructionBody::AssertEq(AssertEqInstruction {
617 a: cell,
618 b: ResOperand::BinOp(BinOpOperand {
619 op: Operation::Add,
620 a: cell,
621 b: DerefOrImmediate::Immediate(BigInt::one().into()),
622 }),
623 }),
624 false,
625 );
626 self.statements.push(Statement::Final(instruction));
627 self.mark_unreachable();
628 }
629
630 pub fn mark_unreachable(&mut self) {
633 self.reachable = false;
634 }
635
636 pub fn get_value(&self, var: Var, adjust_ap: bool) -> CellExpression {
638 if adjust_ap { self.main_state.get_adjusted(var) } else { self.main_state.get_value(var) }
639 }
640
641 fn as_cell_ref(&self, var: Var, adjust_ap: bool) -> CellRef {
643 extract_matches!(self.get_value(var, adjust_ap), CellExpression::Deref)
644 }
645
646 fn as_deref_or_imm(&self, var: Var, adjust_ap: bool) -> DerefOrImmediate {
648 match self.get_value(var, adjust_ap) {
649 CellExpression::Deref(cell) => DerefOrImmediate::Deref(cell),
650 CellExpression::Immediate(imm) => DerefOrImmediate::Immediate(imm.into()),
651 CellExpression::DoubleDeref(_, _) | CellExpression::BinOp { .. } => {
652 panic!("wrong usage.")
653 }
654 }
655 }
656
657 fn as_cell_ref_plus_const(
660 &self,
661 var: Var,
662 additional_offset: i16,
663 adjust_ap: bool,
664 ) -> (CellRef, i16) {
665 match self.get_value(var, adjust_ap) {
666 CellExpression::Deref(cell) => (cell, additional_offset),
667 CellExpression::BinOp {
668 op: CellOperator::Add,
669 a,
670 b: DerefOrImmediate::Immediate(imm),
671 } => (
672 a,
673 (imm.value + additional_offset).try_into().expect("Offset too large for deref."),
674 ),
675 _ => panic!("Not a valid ptr."),
676 }
677 }
678
679 fn next_instruction(&mut self, body: InstructionBody, inc_ap_supported: bool) -> Instruction {
682 assert!(self.reachable, "Cannot add instructions at unreachable code.");
683 let inc_ap =
684 inc_ap_supported && self.main_state.allocated as usize > self.main_state.ap_change;
685 if inc_ap {
686 self.main_state.ap_change += 1;
687 }
688 self.main_state.steps += 1;
689 let mut hints = vec![];
690 core::mem::swap(&mut hints, &mut self.current_hints);
691 Instruction { body, inc_ap, hints }
692 }
693}
694
695impl Default for CasmBuilder {
696 fn default() -> Self {
697 Self {
698 label_state: Default::default(),
699 read_labels: Default::default(),
700 main_state: Default::default(),
701 statements: Default::default(),
702 current_hints: Default::default(),
703 var_count: Default::default(),
704 reachable: true,
705 }
706 }
707}
708
709#[macro_export]
710macro_rules! casm_build_extend {
711 ($builder:expr,) => {};
712 ($builder:expr, tempvar $var:ident; $($tok:tt)*) => {
713 let $var = $builder.alloc_var(false);
714 $crate::casm_build_extend!($builder, $($tok)*)
715 };
716 ($builder:expr, localvar $var:ident; $($tok:tt)*) => {
717 let $var = $builder.alloc_var(true);
718 $crate::casm_build_extend!($builder, $($tok)*)
719 };
720 ($builder:expr, ap += $value:expr; $($tok:tt)*) => {
721 $builder.add_ap($value);
722 $crate::casm_build_extend!($builder, $($tok)*)
723 };
724 ($builder:expr, const $imm:ident = $value:expr; $($tok:tt)*) => {
725 let $imm = $builder.add_var($crate::cell_expression::CellExpression::Immediate(($value).into()));
726 $crate::casm_build_extend!($builder, $($tok)*)
727 };
728 ($builder:expr, assert $dst:ident = $res:ident; $($tok:tt)*) => {
729 $builder.assert_vars_eq($dst, $res);
730 $crate::casm_build_extend!($builder, $($tok)*)
731 };
732 ($builder:expr, assert $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
733 {
734 let __sum = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
735 $builder.assert_vars_eq($dst, __sum);
736 }
737 $crate::casm_build_extend!($builder, $($tok)*)
738 };
739 ($builder:expr, assert $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
740 {
741 let __product = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
742 $builder.assert_vars_eq($dst, __product);
743 }
744 $crate::casm_build_extend!($builder, $($tok)*)
745 };
746 ($builder:expr, assert $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
747 {
748 let __diff = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
749 $builder.assert_vars_eq($dst, __diff);
750 }
751 $crate::casm_build_extend!($builder, $($tok)*)
752 };
753 ($builder:expr, assert $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
754 {
755 let __division = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
756 $builder.assert_vars_eq($dst, __division);
757 }
758 $crate::casm_build_extend!($builder, $($tok)*)
759 };
760 ($builder:expr, assert $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
761 {
762 let __deref = $builder.double_deref($buffer, $offset);
763 $builder.assert_vars_eq($dst, __deref);
764 }
765 $crate::casm_build_extend!($builder, $($tok)*)
766 };
767 ($builder:expr, assert $dst:ident = * $buffer:ident; $($tok:tt)*) => {
768 {
769 let __deref = $builder.double_deref($buffer, 0);
770 $builder.assert_vars_eq($dst, __deref);
771 }
772 $crate::casm_build_extend!($builder, $($tok)*)
773 };
774 ($builder:expr, assert $value:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
775 $builder.buffer_write_and_inc($buffer, $value);
776 $crate::casm_build_extend!($builder, $($tok)*)
777 };
778 ($builder:expr, tempvar $var:ident = $value:ident; $($tok:tt)*) => {
779 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $value; $($tok)*);
780 };
781 ($builder:expr, 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:expr, 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:expr, tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
788 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs - $rhs; $($tok)*);
789 };
790 ($builder:expr, tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
791 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $lhs / $rhs; $($tok)*);
792 };
793 ($builder:expr, tempvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
794 $crate::casm_build_extend!($builder, tempvar $var; assert $var = *($buffer++); $($tok)*);
795 };
796 ($builder:expr, tempvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
797 $crate::casm_build_extend!($builder, tempvar $var; assert $var = $buffer[$offset]; $($tok)*);
798 };
799 ($builder:expr, tempvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
800 $crate::casm_build_extend!($builder, tempvar $var; assert $var = *$buffer; $($tok)*);
801 };
802 ($builder:expr, maybe_tempvar $var:ident = $value:ident; $($tok:tt)*) => {
803 let $var = $builder.maybe_add_tempvar($value);
804 $crate::casm_build_extend!($builder, $($tok)*);
805 };
806 ($builder:expr, maybe_tempvar $var:ident = $lhs:ident + $rhs:ident; $($tok:tt)*) => {
807 $crate::casm_build_extend! {$builder,
808 let $var = $lhs + $rhs;
809 maybe_tempvar $var = $var;
810 $($tok)*
811 };
812 };
813 ($builder:expr, maybe_tempvar $var:ident = $lhs:ident * $rhs:ident; $($tok:tt)*) => {
814 $crate::casm_build_extend! {$builder,
815 let $var = $lhs * $rhs;
816 maybe_tempvar $var = $var;
817 $($tok)*
818 };
819 };
820 ($builder:expr, maybe_tempvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
821 $crate::casm_build_extend! {$builder,
822 let $var = $lhs - $rhs;
823 maybe_tempvar $var = $var;
824 $($tok)*
825 };
826 };
827 ($builder:expr, maybe_tempvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
828 $crate::casm_build_extend! {$builder,
829 let $var = $lhs / $rhs;
830 maybe_tempvar $var = $var;
831 $($tok)*
832 };
833 };
834 ($builder:expr, localvar $var:ident = $value:ident; $($tok:tt)*) => {
835 $crate::casm_build_extend!($builder, localvar $var; assert $var = $value; $($tok)*);
836 };
837 ($builder:expr, 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:expr, 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:expr, localvar $var:ident = $lhs:ident - $rhs:ident; $($tok:tt)*) => {
844 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs - $rhs; $($tok)*);
845 };
846 ($builder:expr, localvar $var:ident = $lhs:ident / $rhs:ident; $($tok:tt)*) => {
847 $crate::casm_build_extend!($builder, localvar $var; assert $var = $lhs / $rhs; $($tok)*);
848 };
849 ($builder:expr, localvar $var:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
850 $crate::casm_build_extend!($builder, localvar $var; assert $var = *($buffer++); $($tok)*);
851 };
852 ($builder:expr, localvar $var:ident = $buffer:ident [ $offset:expr ]; $($tok:tt)*) => {
853 $crate::casm_build_extend!($builder, localvar $var; assert $var = $buffer[$offset]; $($tok)*);
854 };
855 ($builder:expr, localvar $var:ident = * $buffer:ident ; $($tok:tt)*) => {
856 $crate::casm_build_extend!($builder, localvar $var; assert $var = *$buffer; $($tok)*);
857 };
858 ($builder:expr, let $dst:ident = $a:ident + $b:ident; $($tok:tt)*) => {
859 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Add, $a, $b);
860 $crate::casm_build_extend!($builder, $($tok)*)
861 };
862 ($builder:expr, let $dst:ident = $a:ident * $b:ident; $($tok:tt)*) => {
863 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Mul, $a, $b);
864 $crate::casm_build_extend!($builder, $($tok)*)
865 };
866 ($builder:expr, let $dst:ident = $a:ident - $b:ident; $($tok:tt)*) => {
867 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Sub, $a, $b);
868 $crate::casm_build_extend!($builder, $($tok)*)
869 };
870 ($builder:expr, let $dst:ident = $a:ident / $b:ident; $($tok:tt)*) => {
871 let $dst = $builder.bin_op($crate::cell_expression::CellOperator::Div, $a, $b);
872 $crate::casm_build_extend!($builder, $($tok)*)
873 };
874 ($builder:expr, let $dst:ident = * ( $buffer:ident ++ ); $($tok:tt)*) => {
875 let $dst = $builder.get_ref_and_inc($buffer);
876 $crate::casm_build_extend!($builder, $($tok)*)
877 };
878 ($builder:expr, let $dst:ident = $buffer:ident [ $offset:expr ] ; $($tok:tt)*) => {
879 let $dst = $builder.double_deref($buffer, $offset);
880 $crate::casm_build_extend!($builder, $($tok)*)
881 };
882 ($builder:expr, let $dst:ident = *$buffer:ident; $($tok:tt)*) => {
883 let $dst = $builder.double_deref($buffer, 0);
884 $crate::casm_build_extend!($builder, $($tok)*)
885 };
886 ($builder:expr, let $dst:ident = $src:ident; $($tok:tt)*) => {
887 let $dst = $builder.duplicate_var($src);
888 $crate::casm_build_extend!($builder, $($tok)*)
889 };
890 ($builder:expr, jump $target:ident; $($tok:tt)*) => {
891 $builder.jump($crate::builder::ToOwned::to_owned(core::stringify!($target)));
892 $crate::casm_build_extend!($builder, $($tok)*)
893 };
894 ($builder:expr, jump $target:ident if $condition:ident != 0; $($tok:tt)*) => {
895 $builder.jump_nz($condition, $crate::builder::ToOwned::to_owned(core::stringify!($target)));
896 $crate::casm_build_extend!($builder, $($tok)*)
897 };
898 ($builder:expr, let ($($var_name:ident),*) = call $target:ident; $($tok:tt)*) => {
899 $builder.call($crate::builder::ToOwned::to_owned(core::stringify!($target)));
900
901 let __var_count = {0i16 $(+ (stringify!($var_name), 1i16).1)*};
902 let mut __var_index = 0;
903 $(
904 let $var_name = $builder.add_var($crate::cell_expression::CellExpression::Deref($crate::operand::CellRef {
905 offset: __var_index - __var_count,
906 register: $crate::operand::Register::AP,
907 }));
908 __var_index += 1;
909 )*
910 $crate::casm_build_extend!($builder, $($tok)*)
911 };
912 ($builder:expr, ret; $($tok:tt)*) => {
913 $builder.ret();
914 $crate::casm_build_extend!($builder, $($tok)*)
915 };
916 ($builder:expr, $label:ident: $($tok:tt)*) => {
917 $builder.label($crate::builder::ToOwned::to_owned(core::stringify!($label)));
918 $crate::casm_build_extend!($builder, $($tok)*)
919 };
920 ($builder:expr, fail; $($tok:tt)*) => {
921 $builder.fail();
922 $crate::casm_build_extend!($builder, $($tok)*)
923 };
924 ($builder:expr, unsatisfiable_assert $dst:ident = $res:ident; $($tok:tt)*) => {
925 $crate::casm_build_extend!($builder, assert $dst = $res;);
926 $builder.mark_unreachable();
927 $crate::casm_build_extend!($builder, $($tok)*)
928 };
929 ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)+ {
930 $($input_name:ident $(: $input_value:ident)?),*
931 } into {
932 $($output_name:ident $(: $output_value:ident)?),*
933 }; $($tok:tt)*) => {
934 $builder.add_hint(
935 |[$($input_name),*], [$($output_name),*]| $hint_head$(::$hint_tail)+ {
936 $($input_name,)* $($output_name,)*
937 },
938 [$($crate::casm_build_hint_param_value!($input_name $(: $input_value)?),)*],
939 [$($crate::casm_build_hint_param_value!($output_name $(: $output_value)?),)*],
940 );
941 $crate::casm_build_extend!($builder, $($tok)*)
942 };
943 ($builder:expr, hint $hint_name:ident { $($inputs:tt)* } into { $($outputs:tt)* }; $($tok:tt)*) => {
944 $crate::casm_build_extend! {$builder,
945 hint $crate::hints::CoreHint::$hint_name { $($inputs)* } into { $($outputs)* };
946 $($tok)*
947 }
948 };
949 ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)* { $($inputs:tt)* }; $($tok:tt)*) => {
950 $crate::casm_build_extend! {$builder,
951 hint $hint_head$(::$hint_tail)* { $($inputs)* } into {};
952 $($tok)*
953 }
954 };
955 ($builder:expr, hint $hint_head:ident$(::$hint_tail:ident)* into { $($outputs:tt)* }; $($tok:tt)*) => {
956 $crate::casm_build_extend! {$builder,
957 hint $hint_head$(::$hint_tail)* {} into { $($outputs)* };
958 $($tok)*
959 }
960 };
961 ($builder:expr, rescope { $($new_var:ident = $value_var:ident),* }; $($tok:tt)*) => {
962 $builder.rescope([$(($new_var, $value_var)),*]);
963 $crate::casm_build_extend!($builder, $($tok)*)
964 };
965 ($builder:expr, #{ validate steps == $count:expr; } $($tok:tt)*) => {
966 assert_eq!($builder.steps(), $count);
967 $crate::casm_build_extend!($builder, $($tok)*)
968 };
969 ($builder:expr, #{ steps = 0; } $($tok:tt)*) => {
970 $builder.reset_steps();
971 $crate::casm_build_extend!($builder, $($tok)*)
972 };
973 ($builder:expr, #{ $counter:ident += steps; steps = 0; } $($tok:tt)*) => {
974 $counter += $builder.steps() as i32;
975 $builder.reset_steps();
976 $crate::casm_build_extend!($builder, $($tok)*)
977 };
978}
979
980#[macro_export]
981macro_rules! casm_build_hint_param_value {
982 ($_name:ident : $value:ident) => {
983 $value
984 };
985 ($name:ident) => {
986 $name
987 };
988}