cairo_lang_lowering/optimizations/
config.rs

1use cairo_lang_defs::ids::ExternFunctionId;
2use cairo_lang_semantic::helper::ModuleHelper;
3use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
4use itertools::chain;
5use salsa::Database;
6
7use crate::db::LoweringGroup;
8use crate::utils::InliningStrategy;
9
10/// A configuration that controls occurrences of optimizations and their behavior.
11#[derive(Debug, Eq, PartialEq, Clone)]
12pub enum Optimizations {
13    Disabled,
14    Enabled(OptimizationConfig),
15}
16
17/// A configuration struct that controls the behavior of the optimization passes.
18#[derive(Default, Debug, Eq, PartialEq, Clone)]
19pub struct OptimizationConfig {
20    /// A list of functions that can be moved during the reorder_statements optimization.
21    pub(crate) moveable_functions: Vec<String>,
22    /// Determines whether inlining is disabled.
23    pub(crate) inlining_strategy: InliningStrategy,
24    /// Should const folding be skipped.
25    pub(crate) skip_const_folding: bool,
26}
27
28impl OptimizationConfig {
29    pub fn with_skip_const_folding(mut self, skip_const_folding: bool) -> Self {
30        self.skip_const_folding = skip_const_folding;
31        self
32    }
33}
34
35impl Optimizations {
36    /// Returns enabled optimization with the list of moveable functions set to a default set and
37    /// `inlining_strategy` set to the passed value.
38    pub fn enabled_with_default_movable_functions(inlining_strategy: InliningStrategy) -> Self {
39        Self::Enabled(OptimizationConfig {
40            moveable_functions: default_moveable_functions(),
41            inlining_strategy,
42            skip_const_folding: false,
43        })
44    }
45
46    /// Returns enabled optimization with the list of moveable functions set to a minimal set.
47    /// Useful for testing.
48    pub fn enabled_with_minimal_movable_functions() -> Self {
49        Self::Enabled(OptimizationConfig {
50            moveable_functions: vec!["felt252_sub".to_string()],
51            inlining_strategy: Default::default(),
52            skip_const_folding: false,
53        })
54    }
55
56    /// A slice of function names that can be moved during the reorder_statements optimization.
57    /// If `self` is [`Optimizations::Disabled`] returns an empty slice.
58    pub fn moveable_functions(&self) -> &[String] {
59        if let Self::Enabled(config) = self { &config.moveable_functions } else { &[] }
60    }
61
62    /// Inlining strategy that should be used.
63    /// If `self` is [`Optimizations::Disabled`] returns [`InliningStrategy::Avoid`].
64    pub fn inlining_strategy(&self) -> InliningStrategy {
65        if let Self::Enabled(config) = self {
66            config.inlining_strategy
67        } else {
68            InliningStrategy::Avoid
69        }
70    }
71
72    /// Whether to skip const folding. If `self` is [`Optimizations::Disabled`] returns `true`.
73    pub fn skip_const_folding(&self) -> bool {
74        if let Self::Enabled(config) = self { config.skip_const_folding } else { true }
75    }
76}
77
78#[salsa::tracked(returns(ref))]
79pub fn priv_movable_function_ids<'db>(
80    db: &'db dyn Database,
81) -> UnorderedHashSet<ExternFunctionId<'db>> {
82    db.optimizations()
83        .moveable_functions()
84        .iter()
85        .map(|name: &String| {
86            let mut path_iter = name.split("::");
87
88            let mut module = ModuleHelper::core(db);
89
90            let mut next = path_iter.next();
91            while let Some(path_item) = next {
92                next = path_iter.next();
93                if next.is_some() {
94                    module = module.submodule(path_item);
95                    continue;
96                }
97                return module.extern_function_id(path_item);
98            }
99
100            panic!("Got empty string as movable_function");
101        })
102        .collect()
103}
104
105/// The default list of function names that can be moved during the reorder_statements optimization.
106fn default_moveable_functions() -> Vec<String> {
107    let mut moveable_functions: Vec<String> = chain!(
108        ["bool_not_impl"],
109        ["felt252_add", "felt252_sub", "felt252_mul", "felt252_div"],
110        ["array::array_new", "array::array_append"],
111        ["box::unbox", "box::box_forward_snapshot", "box::into_box"],
112    )
113    .map(|s| s.to_string())
114    .collect();
115
116    for ty in ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"] {
117        moveable_functions.push(format!("integer::{ty}_wide_mul"));
118    }
119    moveable_functions
120}