From 625a5bad028ab3d6752026b5e5532dfabdbef89e Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Thu, 28 Nov 2024 09:33:31 +0000 Subject: [PATCH] refactor(minifier): improve ast passes (#7518) --- .../collapse_variable_declarations.rs | 6 +- .../src/ast_passes/exploit_assigns.rs | 6 +- crates/oxc_minifier/src/ast_passes/mod.rs | 173 ++++++++++++------ .../src/ast_passes/peephole_fold_constants.rs | 6 +- .../peephole_minimize_conditions.rs | 6 +- .../ast_passes/peephole_remove_dead_code.rs | 6 +- .../peephole_replace_known_methods.rs | 6 +- .../peephole_substitute_alternate_syntax.rs | 6 +- .../src/ast_passes/remove_syntax.rs | 4 - .../src/ast_passes/statement_fusion.rs | 6 +- crates/oxc_minifier/src/compressor.rs | 27 +-- tasks/minsize/minsize.snap | 12 +- 12 files changed, 141 insertions(+), 123 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs index a33dd08bcc246..5ec9c3e9f66bc 100644 --- a/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs +++ b/crates/oxc_minifier/src/ast_passes/collapse_variable_declarations.rs @@ -9,14 +9,10 @@ use crate::CompressorPass; /// `var a; var b = 1; var c = 2` => `var a, b = 1; c = 2` /// pub struct CollapseVariableDeclarations { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for CollapseVariableDeclarations { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs index 06d51fff3821f..9128ade05e5c6 100644 --- a/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs +++ b/crates/oxc_minifier/src/ast_passes/exploit_assigns.rs @@ -7,14 +7,10 @@ use crate::CompressorPass; /// /// pub struct ExploitAssigns { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for ExploitAssigns { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index 7d303aec7974b..1db4468f53b54 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -23,13 +23,46 @@ use oxc_ast::ast::*; use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx}; pub trait CompressorPass<'a>: Traverse<'a> { - fn changed(&self) -> bool; - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>); } +// See `peepholeOptimizationsOnce` + +// For pass: +// ``` +// if (options.collapseVariableDeclarations) { +// passes.maybeAdd(exploitAssign); +// passes.maybeAdd(collapseVariableDeclarations); +// } +// ``` +pub struct CollapsePass { + _x0_exploit_assigns: ExploitAssigns, + x1_collapse_variable_declarations: CollapseVariableDeclarations, +} + +impl CollapsePass { + pub fn new() -> Self { + Self { + _x0_exploit_assigns: ExploitAssigns::new(), + x1_collapse_variable_declarations: CollapseVariableDeclarations::new(), + } + } +} + +impl<'a> CompressorPass<'a> for CollapsePass { + fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { + traverse_mut_with_ctx(self, program, ctx); + } +} + +impl<'a> Traverse<'a> for CollapsePass { + fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + self.x1_collapse_variable_declarations.enter_statements(stmts, ctx); + } +} + // See `latePeepholeOptimizations` -pub struct EarlyPass { +pub struct LatePeepholeOptimizations { x0_statement_fusion: StatementFusion, x1_peephole_remove_dead_code: PeepholeRemoveDeadCode, // TODO: MinimizeExitPoints @@ -37,10 +70,9 @@ pub struct EarlyPass { x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax, x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods, x5_peephole_fold_constants: PeepholeFoldConstants, - changed: bool, } -impl EarlyPass { +impl LatePeepholeOptimizations { pub fn new() -> Self { Self { x0_statement_fusion: StatementFusion::new(), @@ -51,29 +83,55 @@ impl EarlyPass { ), x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(), x5_peephole_fold_constants: PeepholeFoldConstants::new(), - changed: false, } } -} -impl<'a> CompressorPass<'a> for EarlyPass { + fn reset_changed(&mut self) { + self.x0_statement_fusion.changed = false; + self.x1_peephole_remove_dead_code.changed = false; + self.x2_peephole_minimize_conditions.changed = false; + self.x3_peephole_substitute_alternate_syntax.changed = false; + self.x4_peephole_replace_known_methods.changed = false; + self.x5_peephole_fold_constants.changed = false; + } + fn changed(&self) -> bool { - self.changed + self.x0_statement_fusion.changed + || self.x1_peephole_remove_dead_code.changed + || self.x2_peephole_minimize_conditions.changed + || self.x3_peephole_substitute_alternate_syntax.changed + || self.x4_peephole_replace_known_methods.changed + || self.x5_peephole_fold_constants.changed + } + + pub fn run_in_loop<'a>( + &mut self, + program: &mut Program<'a>, + ctx: &mut ReusableTraverseCtx<'a>, + ) { + let mut i = 0; + loop { + self.reset_changed(); + self.build(program, ctx); + if !self.changed() { + break; + } + if i > 10 { + debug_assert!(false, "Ran loop more than 10 times."); + break; + } + i += 1; + } } +} +impl<'a> CompressorPass<'a> for LatePeepholeOptimizations { fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; traverse_mut_with_ctx(self, program, ctx); - self.changed = self.x0_statement_fusion.changed() - || self.x1_peephole_remove_dead_code.changed() - || self.x2_peephole_minimize_conditions.changed() - || self.x3_peephole_substitute_alternate_syntax.changed() - || self.x4_peephole_replace_known_methods.changed() - || self.x5_peephole_fold_constants.changed(); } } -impl<'a> Traverse<'a> for EarlyPass { +impl<'a> Traverse<'a> for LatePeepholeOptimizations { fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { self.x1_peephole_remove_dead_code.enter_statement(stmt, ctx); } @@ -141,49 +199,56 @@ impl<'a> Traverse<'a> for EarlyPass { } } -// Passes listed in `getFinalization` in `DefaultPassConfig` -pub struct LatePass { - x0_exploit_assigns: ExploitAssigns, - x1_collapse_variable_declarations: CollapseVariableDeclarations, - x2_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax, - changed: bool, +// See `createPeepholeOptimizationsPass` +pub struct PeepholeOptimizations { + // TODO: MinimizeExitPoints + x2_peephole_minimize_conditions: PeepholeMinimizeConditions, + x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax, + x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods, + x5_peephole_remove_dead_code: PeepholeRemoveDeadCode, + x6_peephole_fold_constants: PeepholeFoldConstants, } -impl LatePass { +impl PeepholeOptimizations { pub fn new() -> Self { Self { - x0_exploit_assigns: ExploitAssigns::new(), - x1_collapse_variable_declarations: CollapseVariableDeclarations::new(), - x2_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new( + x2_peephole_minimize_conditions: PeepholeMinimizeConditions::new(), + x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new( /* in_fixed_loop */ false, ), - changed: false, + x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(), + x5_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(), + x6_peephole_fold_constants: PeepholeFoldConstants::new(), } } } -impl<'a> CompressorPass<'a> for LatePass { - fn changed(&self) -> bool { - self.changed - } - +impl<'a> CompressorPass<'a> for PeepholeOptimizations { fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { - self.changed = false; traverse_mut_with_ctx(self, program, ctx); - self.changed = self.x0_exploit_assigns.changed() - || self.x0_exploit_assigns.changed() - || self.x1_collapse_variable_declarations.changed() - || self.x2_peephole_substitute_alternate_syntax.changed(); } } -impl<'a> Traverse<'a> for LatePass { - fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { - self.x1_collapse_variable_declarations.enter_statements(stmts, ctx); +impl<'a> Traverse<'a> for PeepholeOptimizations { + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x5_peephole_remove_dead_code.enter_statement(stmt, ctx); + } + + fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_minimize_conditions.exit_statement(stmt, ctx); + self.x5_peephole_remove_dead_code.exit_statement(stmt, ctx); + } + + fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.x5_peephole_remove_dead_code.exit_program(program, ctx); + } + + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + self.x5_peephole_remove_dead_code.exit_statements(stmts, ctx); } fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); + self.x3_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); } fn enter_variable_declaration( @@ -191,23 +256,27 @@ impl<'a> Traverse<'a> for LatePass { decl: &mut VariableDeclaration<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x2_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx); + self.x3_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, ctx); } - fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_substitute_alternate_syntax.enter_call_expression(expr, ctx); + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_peephole_substitute_alternate_syntax.enter_expression(expr, ctx); + self.x4_peephole_replace_known_methods.enter_expression(expr, ctx); } - fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx); + fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_minimize_conditions.exit_expression(expr, ctx); + self.x3_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); + self.x5_peephole_remove_dead_code.exit_expression(expr, ctx); + self.x6_peephole_fold_constants.exit_expression(expr, ctx); } - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_substitute_alternate_syntax.enter_expression(expr, ctx); + fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_peephole_substitute_alternate_syntax.enter_call_expression(expr, ctx); } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { - self.x2_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); + fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_peephole_substitute_alternate_syntax.exit_call_expression(expr, ctx); } fn enter_binary_expression( @@ -215,6 +284,6 @@ impl<'a> Traverse<'a> for LatePass { expr: &mut BinaryExpression<'a>, ctx: &mut TraverseCtx<'a>, ) { - self.x2_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx); + self.x3_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx); } } diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index de5114922f958..732680df010ae 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -16,14 +16,10 @@ use crate::{node_util::Ctx, CompressorPass}; /// /// pub struct PeepholeFoldConstants { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for PeepholeFoldConstants { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs index 61028193e2f25..d29c6c8341f03 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs @@ -13,14 +13,10 @@ use crate::CompressorPass; /// /// pub struct PeepholeMinimizeConditions { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for PeepholeMinimizeConditions { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 90e039e154382..47e769c2d05e5 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -14,14 +14,10 @@ use crate::{keep_var::KeepVar, CompressorPass}; /// See `KeepVar` at the end of this file for `var` hoisting logic. /// pub struct PeepholeRemoveDeadCode { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for PeepholeRemoveDeadCode { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs index 66a1595ca8da5..98d8dd7fc446f 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_replace_known_methods.rs @@ -11,14 +11,10 @@ use crate::{node_util::Ctx, CompressorPass}; /// Minimize With Known Methods /// pub struct PeepholeReplaceKnownMethods { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for PeepholeReplaceKnownMethods { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs index 91ada095c7dc5..701a15c4b3116 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs @@ -24,14 +24,10 @@ pub struct PeepholeSubstituteAlternateSyntax { // states in_define_export: bool, - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for PeepholeSubstituteAlternateSyntax { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/ast_passes/remove_syntax.rs b/crates/oxc_minifier/src/ast_passes/remove_syntax.rs index 9de2b974bb685..4d4063b1d202b 100644 --- a/crates/oxc_minifier/src/ast_passes/remove_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/remove_syntax.rs @@ -15,10 +15,6 @@ pub struct RemoveSyntax { } impl<'a> CompressorPass<'a> for RemoveSyntax { - fn changed(&self) -> bool { - false - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { traverse_mut_with_ctx(self, program, ctx); } diff --git a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs index 95ddc736ebea8..2d5c16f6e124c 100644 --- a/crates/oxc_minifier/src/ast_passes/statement_fusion.rs +++ b/crates/oxc_minifier/src/ast_passes/statement_fusion.rs @@ -12,14 +12,10 @@ use crate::CompressorPass; /// /// pub struct StatementFusion { - changed: bool, + pub(crate) changed: bool, } impl<'a> CompressorPass<'a> for StatementFusion { - fn changed(&self) -> bool { - self.changed - } - fn build(&mut self, program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { self.changed = false; traverse_mut_with_ctx(self, program, ctx); diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index b465b48fbce2c..c5d72fc7b610c 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -5,8 +5,8 @@ use oxc_traverse::ReusableTraverseCtx; use crate::{ ast_passes::{ - EarlyPass, LatePass, PeepholeFoldConstants, PeepholeMinimizeConditions, - PeepholeRemoveDeadCode, RemoveSyntax, + CollapsePass, LatePeepholeOptimizations, PeepholeFoldConstants, PeepholeMinimizeConditions, + PeepholeOptimizations, PeepholeRemoveDeadCode, RemoveSyntax, }, CompressOptions, CompressorPass, }; @@ -41,25 +41,10 @@ impl<'a> Compressor<'a> { return; } - let mut i = 0; - loop { - let mut changed = false; - let mut pass = EarlyPass::new(); - pass.build(program, &mut ctx); - if pass.changed() { - changed = true; - } - if !changed { - break; - } - if i > 50 { - debug_assert!(false, "Ran in a infinite loop."); - break; - } - i += 1; - } - - LatePass::new().build(program, &mut ctx); + PeepholeOptimizations::new().build(program, &mut ctx); + CollapsePass::new().build(program, &mut ctx); + LatePeepholeOptimizations::new().run_in_loop(program, &mut ctx); + PeepholeOptimizations::new().build(program, &mut ctx); } fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut ReusableTraverseCtx<'a>) { diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index f32a5418b69f6..0c43bcb8b5eaa 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -4,7 +4,7 @@ Original | Minified | esbuild | Gzip | esbuild 173.90 kB | 61.16 kB | 59.82 kB | 19.62 kB | 19.33 kB | moment.js -287.63 kB | 91.70 kB | 90.07 kB | 32.35 kB | 31.95 kB | jquery.js +287.63 kB | 91.70 kB | 90.07 kB | 32.34 kB | 31.95 kB | jquery.js 342.15 kB | 120.23 kB | 118.14 kB | 44.72 kB | 44.37 kB | vue.js @@ -12,15 +12,15 @@ Original | Minified | esbuild | Gzip | esbuild 555.77 kB | 275.23 kB | 270.13 kB | 91.33 kB | 90.80 kB | d3.js -1.01 MB | 464.89 kB | 458.89 kB | 127.05 kB | 126.71 kB | bundle.min.js +1.01 MB | 464.89 kB | 458.89 kB | 127.04 kB | 126.71 kB | bundle.min.js -1.25 MB | 660.45 kB | 646.76 kB | 164.57 kB | 163.73 kB | three.js +1.25 MB | 660.45 kB | 646.76 kB | 164.46 kB | 163.73 kB | three.js 2.14 MB | 739.98 kB | 724.14 kB | 181.63 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 333.05 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 333.04 kB | 331.56 kB | echarts.js -6.69 MB | 2.39 MB | 2.31 MB | 496.76 kB | 488.28 kB | antd.js +6.69 MB | 2.39 MB | 2.31 MB | 496.74 kB | 488.28 kB | antd.js -10.95 MB | 3.55 MB | 3.49 MB | 913.60 kB | 915.50 kB | typescript.js +10.95 MB | 3.55 MB | 3.49 MB | 913.59 kB | 915.50 kB | typescript.js