mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
perf(minifier): fuse ast passes (#7493)
This commit is contained in:
parent
58a125ff88
commit
c133693e5c
4 changed files with 204 additions and 31 deletions
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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>) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue