Skip to content

Commit

Permalink
perf(minifier): fuse ast passes
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Nov 26, 2024
1 parent 4f23689 commit 2736ae3
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 31 deletions.
193 changes: 192 additions & 1 deletion crates/oxc_minifier/src/ast_passes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,202 @@ 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> {
fn changed(&self) -> bool;

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
32 changes: 7 additions & 25 deletions crates/oxc_minifier/src/compressor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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;
Expand All @@ -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>) {
Expand Down
8 changes: 4 additions & 4 deletions tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 2736ae3

Please sign in to comment.