1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::collections::HashMap;
use ap_change_info::ApChangeInfo;
use cairo_lang_sierra::extensions::core::{CoreLibfunc, CoreType};
use cairo_lang_sierra::extensions::ConcreteType;
use cairo_lang_sierra::ids::{ConcreteTypeId, FunctionId};
use cairo_lang_sierra::program::{Program, StatementIdx};
use cairo_lang_sierra::program_registry::{ProgramRegistry, ProgramRegistryError};
use generate_equations::{Effects, Var};
use thiserror::Error;
pub mod ap_change_info;
pub mod core_libfunc_ap_change;
mod generate_equations;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ApChange {
Unknown,
Known(usize),
FromMetadata,
AtLocalsFinalizationByTypeSize(ConcreteTypeId),
KnownByTypeSize(ConcreteTypeId),
FunctionCall(FunctionId),
FinalizeLocals,
}
#[derive(Error, Debug, Eq, PartialEq)]
pub enum ApChangeError {
#[error("error from the program registry")]
ProgramRegistryError(#[from] Box<ProgramRegistryError>),
#[error("found an illegal statement index during ap change calculations")]
StatementOutOfBounds(StatementIdx),
#[error("found an illegal statement index during ap change calculations")]
StatementOutOfOrder(StatementIdx),
#[error("found an illegal invocation during cost calculations")]
IllegalInvocation(StatementIdx),
#[error("failed solving the ap changes")]
SolvingApChangeEquationFailed,
}
pub fn calc_ap_changes(program: &Program) -> Result<ApChangeInfo, ApChangeError> {
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(program)?;
let equations = generate_equations::generate_equations(program, |libfunc_id| {
let libfunc = registry.get_libfunc(libfunc_id)?;
core_libfunc_ap_change::core_libfunc_ap_change(libfunc)
.into_iter()
.map(|ap_change| {
Ok(match ap_change {
ApChange::KnownByTypeSize(ty) => Effects {
ap_change: ApChange::Known(registry.get_type(&ty)?.info().size as usize),
locals: 0,
},
ApChange::AtLocalsFinalizationByTypeSize(ty) => Effects {
ap_change: ApChange::Known(0),
locals: registry.get_type(&ty)?.info().size as usize,
},
_ => Effects { ap_change, locals: 0 },
})
})
.collect::<Result<Vec<_>, _>>()
})?;
let solution = cairo_lang_eq_solver::try_solve_equations(equations)
.ok_or(ApChangeError::SolvingApChangeEquationFailed)?;
let mut variable_values = HashMap::<StatementIdx, usize>::default();
let mut function_ap_change = HashMap::<cairo_lang_sierra::ids::FunctionId, usize>::default();
for (var, value) in solution {
match var {
Var::LibfuncImplicitApChangeVariable(idx) => {
variable_values.insert(idx, value as usize)
}
Var::FunctionApChange(func_id) => function_ap_change.insert(func_id, value as usize),
};
}
Ok(ApChangeInfo { variable_values, function_ap_change })
}