perf(minifier): fuse ast passes (#7493)

This commit is contained in:
Boshen 2024-11-26 14:30:04 +00:00
parent 58a125ff88
commit c133693e5c
4 changed files with 204 additions and 31 deletions

View file

@ -18,7 +18,8 @@ pub use peephole_substitute_alternate_syntax::PeepholeSubstituteAlternateSyntax;
pub use remove_syntax::RemoveSyntax; pub use remove_syntax::RemoveSyntax;
pub use statement_fusion::StatementFusion; pub use statement_fusion::StatementFusion;
use oxc_ast::ast::Program; use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx}; use oxc_traverse::{Traverse, TraverseCtx};
pub trait CompressorPass<'a>: Traverse<'a> { 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>); 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);
}
}

View file

@ -56,7 +56,7 @@ impl<'a> Traverse<'a> for PeepholeRemoveDeadCode {
self.dead_code_elimination(stmts, Ctx(ctx)); 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); let ctx = Ctx(ctx);
if let Some(folded_expr) = match expr { if let Some(folded_expr) = match expr {
Expression::ConditionalExpression(e) => Self::try_fold_conditional_expression(e, ctx), Expression::ConditionalExpression(e) => Self::try_fold_conditional_expression(e, ctx),

View file

@ -5,9 +5,8 @@ use oxc_traverse::TraverseCtx;
use crate::{ use crate::{
ast_passes::{ ast_passes::{
CollapseVariableDeclarations, ExploitAssigns, PeepholeFoldConstants, EarlyPass, LatePass, PeepholeFoldConstants, PeepholeMinimizeConditions,
PeepholeMinimizeConditions, PeepholeRemoveDeadCode, PeepholeReplaceKnownMethods, PeepholeRemoveDeadCode, RemoveSyntax,
PeepholeSubstituteAlternateSyntax, RemoveSyntax, StatementFusion,
}, },
CompressOptions, CompressorPass, CompressOptions, CompressorPass,
}; };
@ -42,25 +41,13 @@ impl<'a> Compressor<'a> {
return; 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; let mut i = 0;
loop { loop {
let mut changed = false; let mut changed = false;
for pass in &mut passes { let mut pass = EarlyPass::new();
pass.build(program, &mut ctx); pass.build(program, &mut ctx);
if pass.changed() { if pass.changed() {
changed = true; changed = true;
}
} }
if !changed { if !changed {
break; break;
@ -72,12 +59,7 @@ impl<'a> Compressor<'a> {
i += 1; i += 1;
} }
// Passes listed in `getFinalization` in `DefaultPassConfig` LatePass::new().build(program, &mut ctx);
ExploitAssigns::new().build(program, &mut ctx);
CollapseVariableDeclarations::new().build(program, &mut ctx);
// Late latePeepholeOptimizations
PeepholeSubstituteAlternateSyntax::new(/* in_fixed_loop */ false).build(program, &mut ctx);
} }
fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {

View file

@ -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.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 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