From 2736ae3647c4d9de6cdc025acbf59054895c33c8 Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 26 Nov 2024 21:28:55 +0800 Subject: [PATCH] perf(minifier): fuse ast passes --- crates/oxc_minifier/src/ast_passes/mod.rs | 193 +++++++++++++++++- .../ast_passes/peephole_remove_dead_code.rs | 2 +- crates/oxc_minifier/src/compressor.rs | 32 +-- tasks/minsize/minsize.snap | 8 +- 4 files changed, 204 insertions(+), 31 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index 8f413b9c0b6469..b902ded0383411 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -18,7 +18,8 @@ pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax; pub use remove_syntax::RemoveSyntax; pub use statement_fusion::StatementFusion; -use oxc_ast::ast::Program; +use oxc_allocator::Vec; +use oxc_ast::ast::*; use oxc_traverse::{Traverse, TraverseCtx}; pub trait CompressorPass<'a>: Traverse<'a> { @@ -26,3 +27,193 @@ pub trait CompressorPass<'a>: Traverse<'a> { fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>); } + +// See `latePeepholeOptimizations` +pub struct EarlyPass { + x0_statement_fusion: StatementFusion, + x1_peephole_remove_dead_code: PeepholeRemoveDeadCode, + // TODO: MinimizeExitPoints + x2_peephole_minimize_conditions: PeepholeMinimizeConditions, + x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax, + x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods, + x5_peephole_fold_constants: PeepholeFoldConstants, + changed: bool, +} + +impl EarlyPass { + pub fn new() -> Self { + Self { + x0_statement_fusion: StatementFusion::new(), + x1_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(), + x2_peephole_minimize_conditions: PeepholeMinimizeConditions::new(), + x3_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new( + /* in_fixed_loop */ true, + ), + x4_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(), + x5_peephole_fold_constants: PeepholeFoldConstants::new(), + changed: false, + } + } +} + +impl<'a> CompressorPass<'a> for EarlyPass { + fn changed(&self) -> bool { + self.changed + } + + fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.changed = false; + oxc_traverse::walk_program(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 { + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x1_peephole_remove_dead_code.enter_statement(stmt, ctx); + } + + fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x1_peephole_remove_dead_code.exit_statement(stmt, ctx); + } + + fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.x0_statement_fusion.exit_program(program, ctx); + self.x1_peephole_remove_dead_code.exit_program(program, ctx); + } + + fn exit_function_body(&mut self, body: &mut FunctionBody<'a>, ctx: &mut TraverseCtx<'a>) { + self.x0_statement_fusion.exit_function_body(body, ctx); + } + + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) { + self.x1_peephole_remove_dead_code.exit_statements(stmts, ctx); + } + + fn exit_block_statement(&mut self, block: &mut BlockStatement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x0_statement_fusion.exit_block_statement(block, ctx); + } + + fn exit_return_statement(&mut self, stmt: &mut ReturnStatement<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_peephole_substitute_alternate_syntax.exit_return_statement(stmt, ctx); + } + + fn enter_variable_declaration( + &mut self, + decl: &mut VariableDeclaration<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + self.x3_peephole_substitute_alternate_syntax.enter_variable_declaration(decl, 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_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x1_peephole_remove_dead_code.exit_expression(expr, ctx); + self.x2_peephole_minimize_conditions.exit_expression(expr, ctx); + self.x3_peephole_substitute_alternate_syntax.exit_expression(expr, ctx); + self.x5_peephole_fold_constants.exit_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_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( + &mut self, + expr: &mut BinaryExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + self.x3_peephole_substitute_alternate_syntax.enter_binary_expression(expr, ctx); + } +} + +// 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, +} + +impl LatePass { + pub fn new() -> Self { + Self { + x0_exploit_assigns: ExploitAssigns::new(), + x1_collapse_variable_declarations: CollapseVariableDeclarations::new(), + x2_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new( + /* in_fixed_loop */ false, + ), + changed: false, + } + } +} + +impl<'a> CompressorPass<'a> for LatePass { + fn changed(&self) -> bool { + self.changed + } + + fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.changed = false; + oxc_traverse::walk_program(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); + } + + 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); + } + + fn enter_variable_declaration( + &mut self, + decl: &mut VariableDeclaration<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + self.x2_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 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 enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_peephole_substitute_alternate_syntax.enter_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 enter_binary_expression( + &mut self, + expr: &mut BinaryExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + self.x2_peephole_substitute_alternate_syntax.enter_binary_expression(expr, 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 e92be6327d3b7f..822e45fbec6f82 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 @@ -56,7 +56,7 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode { self.dead_code_elimination(stmts, Ctx(ctx)); } - fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { let ctx = Ctx(ctx); if let Some(folded_expr) = match expr { Expression::ConditionalExpression(e) => Self::try_fold_conditional_expression(e, ctx), diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index c7a2bc90b8b8db..8270821ce50598 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -5,9 +5,8 @@ use oxc_traverse::TraverseCtx; use crate::{ ast_passes::{ - CollapseVariableDeclarations, ExploitAssigns, PeepholeFoldConstants, - PeepholeMinimizeConditions, PeepholeRemoveDeadCode, PeepholeReplaceKnownMethods, - PeepholeSubstituteAlternateSyntax, RemoveSyntax, StatementFusion, + EarlyPass, LatePass, PeepholeFoldConstants, PeepholeMinimizeConditions, + PeepholeRemoveDeadCode, RemoveSyntax, }, CompressOptions, CompressorPass, }; @@ -42,25 +41,13 @@ impl<'a> Compressor<'a> { return; } - // See `latePeepholeOptimizations` - let mut passes: [&mut dyn CompressorPass; 6] = [ - &mut StatementFusion::new(), - &mut PeepholeRemoveDeadCode::new(), - // TODO: MinimizeExitPoints - &mut PeepholeMinimizeConditions::new(), - &mut PeepholeSubstituteAlternateSyntax::new(/* in_fixed_loop */ true), - &mut PeepholeReplaceKnownMethods::new(), - &mut PeepholeFoldConstants::new(), - ]; - let mut i = 0; loop { let mut changed = false; - for pass in &mut passes { - pass.build(program, &mut ctx); - if pass.changed() { - changed = true; - } + let mut pass = EarlyPass::new(); + pass.build(program, &mut ctx); + if pass.changed() { + changed = true; } if !changed { break; @@ -72,12 +59,7 @@ impl<'a> Compressor<'a> { i += 1; } - // Passes listed in `getFinalization` in `DefaultPassConfig` - ExploitAssigns::new().build(program, &mut ctx); - CollapseVariableDeclarations::new().build(program, &mut ctx); - - // Late latePeepholeOptimizations - PeepholeSubstituteAlternateSyntax::new(/* in_fixed_loop */ false).build(program, &mut ctx); + LatePass::new().build(program, &mut ctx); } fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 3b04256db6ca84..083cd7b84cfdff 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -14,13 +14,13 @@ Original | Minified | esbuild | Gzip | esbuild 1.01 MB | 467.59 kB | 458.89 kB | 126.73 kB | 126.71 kB | bundle.min.js -1.25 MB | 662.86 kB | 646.76 kB | 164.00 kB | 163.73 kB | three.js +1.25 MB | 662.83 kB | 646.76 kB | 164.00 kB | 163.73 kB | three.js 2.14 MB | 741.55 kB | 724.14 kB | 181.45 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 332.01 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 332.02 kB | 331.56 kB | echarts.js -6.69 MB | 2.39 MB | 2.31 MB | 496.10 kB | 488.28 kB | antd.js +6.69 MB | 2.39 MB | 2.31 MB | 496.11 kB | 488.28 kB | antd.js -10.95 MB | 3.56 MB | 3.49 MB | 911.24 kB | 915.50 kB | typescript.js +10.95 MB | 3.56 MB | 3.49 MB | 911.20 kB | 915.50 kB | typescript.js