cairo_lang_lowering/optimizations/
strategy.rs

1use cairo_lang_diagnostics::Maybe;
2use cairo_lang_utils::{Intern, define_short_id};
3use salsa::Database;
4
5use super::cse::cse;
6use super::dedup_blocks::dedup_blocks;
7use super::early_unsafe_panic::early_unsafe_panic;
8use super::gas_redeposit::gas_redeposit;
9use super::trim_unreachable::trim_unreachable;
10use super::validate::validate;
11use crate::Lowered;
12use crate::db::LoweringGroup;
13use crate::ids::ConcreteFunctionWithBodyId;
14use crate::implicits::lower_implicits;
15use crate::inline::apply_inlining;
16use crate::optimizations::branch_inversion::branch_inversion;
17use crate::optimizations::cancel_ops::cancel_ops;
18use crate::optimizations::config::Optimizations;
19use crate::optimizations::const_folding::const_folding;
20use crate::optimizations::match_optimizer::optimize_matches;
21use crate::optimizations::remappings::optimize_remappings;
22use crate::optimizations::reorder_statements::reorder_statements;
23use crate::optimizations::return_optimization::return_optimization;
24use crate::optimizations::split_structs::split_structs;
25use crate::reorganize_blocks::reorganize_blocks;
26
27/// Enum of the optimization phases that can be used in a strategy.
28#[derive(Clone, Debug, Eq, Hash, PartialEq)]
29pub enum OptimizationPhase<'db> {
30    ApplyInlining {
31        enable_const_folding: bool,
32    },
33    BranchInversion,
34    CancelOps,
35    ConstFolding,
36    Cse,
37    DedupBlocks,
38    EarlyUnsafePanic,
39    OptimizeMatches,
40    OptimizeRemappings,
41    ReorderStatements,
42    ReorganizeBlocks,
43    ReturnOptimization,
44    SplitStructs,
45    TrimUnreachable,
46    GasRedeposit,
47    /// The following is not really an optimization but we want to apply optimizations before and
48    /// after it, so it is convenient to treat it as an optimization.
49    LowerImplicits,
50    /// A validation phase that checks the lowering is valid. Used for debugging purposes.
51    Validate,
52    /// A phase that iteratively a set of optimizations to the lowering.
53    /// Stops after a certain number of iterations, or when no more changes are made.
54    SubStrategy {
55        /// The id of the optimization strategy to apply.
56        strategy: OptimizationStrategyId<'db>,
57        /// The number of times to apply the strategy.
58        iterations: usize,
59    },
60}
61
62impl<'db> OptimizationPhase<'db> {
63    /// Applies the optimization phase to the lowering.
64    ///
65    /// Assumes `lowered` is a lowering of `function`.
66    pub fn apply(
67        self,
68        db: &'db dyn Database,
69        function: ConcreteFunctionWithBodyId<'db>,
70        lowered: &mut Lowered<'db>,
71    ) -> Maybe<()> {
72        match self {
73            OptimizationPhase::ApplyInlining { enable_const_folding } => {
74                apply_inlining(db, function, lowered, enable_const_folding)?
75            }
76            OptimizationPhase::BranchInversion => branch_inversion(db, lowered),
77            OptimizationPhase::CancelOps => cancel_ops(lowered),
78            OptimizationPhase::ConstFolding => const_folding(db, function, lowered),
79            OptimizationPhase::Cse => cse(lowered),
80            OptimizationPhase::EarlyUnsafePanic => early_unsafe_panic(db, lowered),
81            OptimizationPhase::DedupBlocks => dedup_blocks(lowered),
82            OptimizationPhase::OptimizeMatches => optimize_matches(lowered),
83            OptimizationPhase::OptimizeRemappings => optimize_remappings(lowered),
84            OptimizationPhase::ReorderStatements => reorder_statements(db, lowered),
85            OptimizationPhase::ReorganizeBlocks => reorganize_blocks(lowered),
86            OptimizationPhase::ReturnOptimization => return_optimization(db, lowered),
87            OptimizationPhase::SplitStructs => split_structs(lowered),
88            OptimizationPhase::TrimUnreachable => trim_unreachable(db, lowered),
89            OptimizationPhase::LowerImplicits => lower_implicits(db, function, lowered),
90            OptimizationPhase::GasRedeposit => gas_redeposit(db, function, lowered),
91            OptimizationPhase::Validate => validate(lowered).unwrap_or_else(|err| {
92                panic!(
93                    "Failed validation for function {}: {:?}",
94                    function.full_path(db),
95                    err.to_message()
96                )
97            }),
98            OptimizationPhase::SubStrategy { strategy, iterations } => {
99                for _ in 1..iterations {
100                    let before = lowered.clone();
101                    strategy.apply_strategy(db, function, lowered)?;
102                    if *lowered == before {
103                        return Ok(());
104                    }
105                }
106                strategy.apply_strategy(db, function, lowered)?
107            }
108        }
109        Ok(())
110    }
111}
112
113define_short_id!(OptimizationStrategyId, OptimizationStrategy<'db>);
114
115/// A strategy is a sequence of optimization phases.
116#[derive(Clone, Debug, Eq, Hash, PartialEq)]
117pub struct OptimizationStrategy<'db>(pub Vec<OptimizationPhase<'db>>);
118
119impl<'db> OptimizationStrategyId<'db> {
120    /// Applies the optimization strategy phase to the lowering.
121    ///
122    /// Assumes `lowered` is a lowering of `function`.
123    pub fn apply_strategy(
124        self,
125        db: &'db dyn Database,
126        function: ConcreteFunctionWithBodyId<'db>,
127        lowered: &mut Lowered<'db>,
128    ) -> Maybe<()> {
129        for phase in self.long(db).0.clone() {
130            phase.apply(db, function, lowered)?;
131        }
132
133        Ok(())
134    }
135}
136
137/// Query implementation of [crate::db::LoweringGroup::baseline_optimization_strategy].
138#[salsa::tracked]
139pub fn baseline_optimization_strategy<'db>(db: &'db dyn Database) -> OptimizationStrategyId<'db> {
140    match db.optimizations() {
141        Optimizations::Enabled(_) => {
142            OptimizationStrategy(vec![
143                // Must be right before inlining.
144                OptimizationPhase::ReorganizeBlocks,
145                OptimizationPhase::ApplyInlining { enable_const_folding: true },
146                OptimizationPhase::ReturnOptimization,
147                OptimizationPhase::ReorganizeBlocks,
148                OptimizationPhase::ReorderStatements,
149                OptimizationPhase::BranchInversion,
150                OptimizationPhase::CancelOps,
151                // Must be right before const folding.
152                OptimizationPhase::ReorganizeBlocks,
153                OptimizationPhase::ConstFolding,
154                OptimizationPhase::OptimizeMatches,
155                OptimizationPhase::SplitStructs,
156                OptimizationPhase::ReorganizeBlocks,
157                OptimizationPhase::ReorderStatements,
158                OptimizationPhase::OptimizeMatches,
159                OptimizationPhase::ReorganizeBlocks,
160                OptimizationPhase::CancelOps,
161                OptimizationPhase::ReorganizeBlocks,
162                // Performing CSE here after blocks are the most contiguous, to reach maximum
163                // effect.
164                OptimizationPhase::Cse,
165                OptimizationPhase::DedupBlocks,
166                // Re-run ReturnOptimization to eliminate harmful merges introduced by DedupBlocks.
167                OptimizationPhase::ReturnOptimization,
168                OptimizationPhase::ReorderStatements,
169                OptimizationPhase::ReorganizeBlocks,
170            ])
171        }
172        Optimizations::Disabled => OptimizationStrategy(vec![OptimizationPhase::ApplyInlining {
173            enable_const_folding: false,
174        }]),
175    }
176    .intern(db)
177}
178
179/// Query implementation of [crate::db::LoweringGroup::final_optimization_strategy].
180#[salsa::tracked]
181pub fn final_optimization_strategy<'db>(db: &'db dyn Database) -> OptimizationStrategyId<'db> {
182    match db.optimizations() {
183        Optimizations::Enabled(_) => {
184            OptimizationStrategy(vec![
185                OptimizationPhase::GasRedeposit,
186                OptimizationPhase::EarlyUnsafePanic,
187                // Apply `TrimUnreachable` here to remove unreachable `redeposit_gas` and
188                // `unsafe_panic` calls.
189                OptimizationPhase::TrimUnreachable,
190                OptimizationPhase::LowerImplicits,
191                OptimizationPhase::ReorganizeBlocks,
192            ])
193        }
194        Optimizations::Disabled => OptimizationStrategy(vec![
195            OptimizationPhase::LowerImplicits,
196            OptimizationPhase::ReorganizeBlocks,
197        ]),
198    }
199    .intern(db)
200}