cairo_lang_sierra_to_casm/
references.rs1use cairo_lang_casm::ap_change::ApplyApChange;
2use cairo_lang_casm::cell_expression::CellExpression;
3use cairo_lang_casm::operand::{CellRef, Register};
4use cairo_lang_sierra::ids::{ConcreteTypeId, VarId};
5use cairo_lang_sierra::program::{Function, StatementIdx};
6use cairo_lang_sierra_type_size::TypeSizeMap;
7use cairo_lang_utils::casts::IntoOrPanic;
8use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
9use cairo_lang_utils::write_comma_separated;
10use thiserror::Error;
11
12use crate::invocations::InvocationError;
13
14#[derive(Error, Debug, Eq, PartialEq)]
15pub enum ReferencesError {
16 #[error("Invalid function declaration.")]
17 InvalidFunctionDeclaration(Function),
18 #[error(
19 "One of the arguments does not match the expected type of the libfunc or return statement."
20 )]
21 InvalidReferenceTypeForArgument,
22 #[error("Unknown type `{0}`.")]
23 UnknownType(ConcreteTypeId),
24}
25
26pub type StatementRefs = OrderedHashMap<VarId, ReferenceValue>;
27
28#[derive(Clone, Debug, Eq, PartialEq)]
31pub struct ReferenceValue {
32 pub expression: ReferenceExpression,
33 pub ty: ConcreteTypeId,
34 pub stack_idx: Option<usize>,
37 pub introduction_point: IntroductionPoint,
39}
40impl ReferenceValue {
41 pub fn validate(&self, type_sizes: &TypeSizeMap) {
44 let size = *type_sizes.get(&self.ty).expect("ReferenceValue has unknown type");
45 let actual_size: i16 = self.expression.cells.len().into_or_panic();
46 assert_eq!(actual_size, size, "ReferenceValue type size mismatch.");
47 }
48}
49
50#[derive(Clone, Debug, Eq, PartialEq)]
52pub struct IntroductionPoint {
53 pub source_statement_idx: Option<StatementIdx>,
55 pub destination_statement_idx: StatementIdx,
57 pub output_idx: usize,
59}
60
61impl core::fmt::Display for IntroductionPoint {
62 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
63 if let Some(source_statement_idx) = self.source_statement_idx {
64 write!(
65 f,
66 "#{source_statement_idx}->#{}[{}]",
67 self.destination_statement_idx, self.output_idx
68 )
69 } else {
70 write!(f, "Function@{}[{}]", self.destination_statement_idx, self.output_idx)
71 }
72 }
73}
74
75#[derive(Clone, Debug, Eq, PartialEq)]
78pub struct OutputReferenceValue {
79 pub expression: ReferenceExpression,
80 pub ty: ConcreteTypeId,
81 pub stack_idx: Option<usize>,
83 pub introduction_point: OutputReferenceValueIntroductionPoint,
86}
87
88#[derive(Clone, Debug, Eq, PartialEq)]
90pub enum OutputReferenceValueIntroductionPoint {
91 New(usize),
93 Existing(IntroductionPoint),
95}
96
97#[derive(Clone, Debug, Eq, PartialEq)]
99pub struct ReferenceExpression {
100 pub cells: Vec<CellExpression>,
101}
102
103impl ReferenceExpression {
104 pub fn from_cell(cell_expr: CellExpression) -> Self {
106 Self { cells: vec![cell_expr] }
107 }
108
109 pub fn zero_sized() -> Self {
111 Self { cells: vec![] }
112 }
113
114 pub fn try_unpack<const SIZE: usize>(
116 &self,
117 ) -> Result<&[CellExpression; SIZE], InvocationError> {
118 <&[CellExpression; SIZE]>::try_from(&self.cells[..])
119 .map_err(|_| InvocationError::InvalidReferenceExpressionForArgument)
120 }
121
122 pub fn try_unpack_single(&self) -> Result<&CellExpression, InvocationError> {
124 Ok(&self.try_unpack::<1>()?[0])
125 }
126}
127
128impl ApplyApChange for ReferenceExpression {
129 fn apply_known_ap_change(self, ap_change: usize) -> Option<Self> {
130 Some(ReferenceExpression {
131 cells: self
132 .cells
133 .into_iter()
134 .map(|cell_expr| cell_expr.apply_known_ap_change(ap_change))
135 .collect::<Option<Vec<_>>>()?,
136 })
137 }
138
139 fn can_apply_unknown(&self) -> bool {
140 self.cells.iter().all(|cell| cell.can_apply_unknown())
141 }
142}
143
144impl core::fmt::Display for ReferenceExpression {
145 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146 write!(f, "[")?;
147 write_comma_separated(f, &self.cells)?;
148 write!(f, "]")
149 }
150}
151
152pub fn build_function_parameters_refs(
154 func: &Function,
155 type_sizes: &TypeSizeMap,
156) -> Result<StatementRefs, ReferencesError> {
157 let mut refs = StatementRefs::default();
158 let mut offset = -3_i16;
159 for (param_idx, param) in func.params.iter().rev().enumerate() {
160 let size = type_sizes
161 .get(¶m.ty)
162 .ok_or_else(|| ReferencesError::UnknownType(param.ty.clone()))?;
163 if refs
164 .insert(param.id.clone(), ReferenceValue {
165 expression: ReferenceExpression {
166 cells: ((offset - size + 1)..(offset + 1))
167 .map(|i| {
168 CellExpression::Deref(CellRef { register: Register::FP, offset: i })
169 })
170 .collect(),
171 },
172 ty: param.ty.clone(),
173 stack_idx: None,
174 introduction_point: IntroductionPoint {
175 source_statement_idx: None,
176 destination_statement_idx: func.entry_point,
177 output_idx: param_idx,
178 },
179 })
180 .is_some()
181 {
182 return Err(ReferencesError::InvalidFunctionDeclaration(func.clone()));
183 }
184 offset -= size;
185 }
186 Ok(refs)
187}
188
189pub fn check_types_match(
191 refs: &[ReferenceValue],
192 types: &[ConcreteTypeId],
193) -> Result<(), ReferencesError> {
194 if itertools::equal(types.iter(), refs.iter().map(|r| &r.ty)) {
195 Ok(())
196 } else {
197 Err(ReferencesError::InvalidReferenceTypeForArgument)
198 }
199}