boa_engine/optimizer/
mod.rs1pub(crate) mod pass;
2pub(crate) mod walker;
3
4use self::{
5 pass::{ConstantFolding, DeadCodeElimination, StrengthReduction},
6 walker::Walker,
7};
8use crate::Context;
9use bitflags::bitflags;
10use boa_ast::{
11 Expression, StatementList,
12 statement::Statement,
13 visitor::{VisitWith, VisitorMut},
14};
15use std::{fmt, ops::ControlFlow};
16
17bitflags! {
18 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20 pub struct OptimizerOptions: u8 {
21 const STATISTICS = 0b0000_0001;
23 const CONSTANT_FOLDING = 0b0000_0010;
25 const STRENGTH_REDUCTION = 0b0000_0100;
27 const DEAD_CODE_ELIMINATION = 0b0000_1000;
29 const OPTIMIZE_ALL = Self::CONSTANT_FOLDING.bits()
31 | Self::STRENGTH_REDUCTION.bits()
32 | Self::DEAD_CODE_ELIMINATION.bits();
33 }
34}
35
36#[derive(Debug)]
38pub(crate) enum PassAction<T> {
39 Keep,
41 Modified,
43 Replace(T),
45}
46
47#[derive(Debug, Default, Clone, Copy)]
49pub struct OptimizerStatistics {
50 pub constant_folding_run_count: usize,
52 pub constant_folding_pass_count: usize,
54 pub strength_reduction_run_count: usize,
56 pub strength_reduction_pass_count: usize,
58 pub dead_code_elimination_count: usize,
60}
61
62impl fmt::Display for OptimizerStatistics {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 writeln!(f, "Optimizer {{")?;
65 writeln!(
66 f,
67 " constant folding: {} run(s), {} pass(es) ({} mutating, {} checking)",
68 self.constant_folding_run_count,
69 self.constant_folding_pass_count,
70 self.constant_folding_pass_count
71 .saturating_sub(self.constant_folding_run_count),
72 self.constant_folding_run_count
73 )?;
74 writeln!(
75 f,
76 " strength reduction: {} run(s), {} pass(es)",
77 self.strength_reduction_run_count, self.strength_reduction_pass_count,
78 )?;
79 writeln!(
80 f,
81 " dead code elimination: {} elimination(s)",
82 self.dead_code_elimination_count,
83 )?;
84 writeln!(f, "}}")?;
85 Ok(())
86 }
87}
88
89#[derive(Debug)]
91pub(crate) struct Optimizer<'context> {
92 statistics: OptimizerStatistics,
93 context: &'context mut Context,
94}
95
96impl<'context> Optimizer<'context> {
97 pub(crate) fn new(context: &'context mut Context) -> Self {
99 Self {
100 statistics: OptimizerStatistics::default(),
101 context,
102 }
103 }
104
105 fn run_constant_folding_pass(&mut self, expr: &mut Expression) -> bool {
107 self.statistics.constant_folding_run_count += 1;
108
109 let mut has_changes = false;
110 for _ in 0..Self::MAX_PASS_ITERATIONS {
111 self.statistics.constant_folding_pass_count += 1;
112 let mut walker = Walker::new(|expr| -> PassAction<Expression> {
113 ConstantFolding::fold_expression(expr, self.context)
114 });
115 walker.walk_expression_postorder(expr);
118 if !walker.changed() {
119 break;
120 }
121 has_changes = true;
122 }
123 has_changes
124 }
125
126 const MAX_PASS_ITERATIONS: usize = 10;
129
130 fn run_strength_reduction_pass(&mut self, expr: &mut Expression) -> bool {
132 self.statistics.strength_reduction_run_count += 1;
133
134 let mut has_changes = false;
135 for _ in 0..Self::MAX_PASS_ITERATIONS {
136 self.statistics.strength_reduction_pass_count += 1;
137 let mut walker = Walker::new(|expr| -> PassAction<Expression> {
138 StrengthReduction::reduce_expression(expr)
139 });
140 walker.walk_expression_postorder(expr);
141 if !walker.changed() {
142 break;
143 }
144 has_changes = true;
145 }
146 has_changes
147 }
148
149 fn run_all(&mut self, expr: &mut Expression) {
150 if self
151 .context
152 .optimizer_options()
153 .contains(OptimizerOptions::CONSTANT_FOLDING)
154 {
155 self.run_constant_folding_pass(expr);
156 }
157 if self
158 .context
159 .optimizer_options()
160 .contains(OptimizerOptions::STRENGTH_REDUCTION)
161 {
162 self.run_strength_reduction_pass(expr);
163 }
164 }
165
166 pub(crate) fn apply(&mut self, statement_list: &mut StatementList) -> OptimizerStatistics {
168 let _ = self.visit_statement_list_mut(statement_list);
169
170 #[allow(clippy::print_stdout)]
171 if self
172 .context
173 .optimizer_options()
174 .contains(OptimizerOptions::STATISTICS)
175 {
176 println!("{}", self.statistics);
177 }
178 self.statistics
179 }
180}
181
182impl<'ast> VisitorMut<'ast> for Optimizer<'_> {
183 type BreakTy = ();
184
185 fn visit_expression_mut(&mut self, node: &'ast mut Expression) -> ControlFlow<Self::BreakTy> {
186 self.run_all(node);
187 ControlFlow::Continue(())
188 }
189
190 fn visit_statement_mut(&mut self, node: &'ast mut Statement) -> ControlFlow<Self::BreakTy> {
191 node.visit_with_mut(self)?;
194
195 if self
197 .context
198 .optimizer_options()
199 .contains(OptimizerOptions::DEAD_CODE_ELIMINATION)
200 {
201 match node {
202 Statement::If(if_stmt) => {
203 if let PassAction::Replace(replacement) =
204 DeadCodeElimination::try_eliminate_if(if_stmt)
205 {
206 *node = replacement;
207 self.statistics.dead_code_elimination_count += 1;
208 }
209 }
210 Statement::WhileLoop(while_loop) => {
211 if let PassAction::Replace(replacement) =
212 DeadCodeElimination::try_eliminate_while(while_loop)
213 {
214 *node = replacement;
215 self.statistics.dead_code_elimination_count += 1;
216 }
217 }
218 Statement::ForLoop(for_loop) => {
219 if let PassAction::Replace(replacement) =
220 DeadCodeElimination::try_eliminate_for(for_loop)
221 {
222 *node = replacement;
223 self.statistics.dead_code_elimination_count += 1;
224 }
225 }
226 _ => {}
227 }
228 }
229
230 ControlFlow::Continue(())
231 }
232}