perf(minifier): try merging before creating new expression statements (#26556)

Add `push_new_expression_stmt_to_result` to attempt merging expressions
into existing sequence statements before pushing new ones.

```js
// antd.js
// 1:
var r = n(`react`), i = n(`./node_modules/@ant-design/icons-svg/es/asn/PieChartFilled.js`);
// 2: this iteration is removed
n(`react`); n(`./node_modules/@ant-design/icons-svg/es/asn/AccountBookFilled.js`);
// 3:
n(`react`), n(`./node_modules/@ant-design/icons-svg/es/asn/AccountBookFilled.js`);
```
This commit is contained in:
Armano
2026-09-12 16:52:56 +02:00
committed by GitHub
parent eadf4db25e
commit 0bc1661f40
3 changed files with 40 additions and 39 deletions
@@ -169,6 +169,26 @@ impl<'a> PeepholeOptimizations {
}
}
/// Merge `expr` expression with the previous expression statement or emit as a new one.
fn push_new_expression_stmt_to_result(
expr: Expression<'a>,
result: &mut ArenaVec<'a, Statement<'a>>,
ctx: &mut TraverseCtx<'a>,
) {
if ctx.options().sequences
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
{
ctx.replace_expression_with(&mut prev_expr_stmt.expression, |a, ctx| {
Self::join_sequence(a, expr, ctx)
});
} else {
result.push(Statement::new_expression_statement(expr.span(), expr, ctx));
ctx.notice_change();
}
}
/// Fold `target` expression into previous expression as sequence
/// `a; b` -> `a, b`.
fn merge_last_expression_into_sequence(
target: &mut Expression<'a>,
result: &mut ArenaVec<'a, Statement<'a>>,
@@ -262,7 +282,7 @@ impl<'a> PeepholeOptimizations {
if Self::remove_unused_expression(&mut init, ctx) {
ctx.drop_expression(&init);
} else {
result.push(Statement::new_expression_statement(init.span(), init, ctx));
Self::push_new_expression_stmt_to_result(init, result, ctx);
}
}
// Walk the rest of the dropped declarator (binding pattern +
@@ -516,11 +536,7 @@ impl<'a> PeepholeOptimizations {
}
if !discriminant.is_literal() {
result.push(Statement::new_expression_statement(
discriminant.span(),
discriminant,
ctx,
));
Self::push_new_expression_stmt_to_result(discriminant, result, ctx);
}
result.push(block_stmt);
@@ -660,19 +676,9 @@ impl<'a> PeepholeOptimizations {
{
let argument = ret_stmt.argument.take().unwrap();
if argument.may_have_side_effects(ctx) {
if ctx.options().sequences
&& let Some(Statement::ExpressionStatement(prev_expr_stmt)) = result.last_mut()
{
// `x; return a,void 0;` -> `x,a,void 0; return;`
ctx.replace_expression_with(&mut prev_expr_stmt.expression, |a, ctx| {
Self::join_sequence(a, argument, ctx)
});
} else {
// `return a,void 0;` -> `a,void 0; return;`
let span = argument.span();
result.push(Statement::new_expression_statement(span, argument, ctx));
ctx.notice_change();
}
// `x; return a,void 0;` -> `x,a,void 0; return;`
// `return a,void 0;` -> `a,void 0; return;`
Self::push_new_expression_stmt_to_result(argument, result, ctx);
} else {
// `return void 0;` -> `return;`
ctx.drop_expression(&argument);
@@ -698,14 +704,9 @@ impl<'a> PeepholeOptimizations {
ctx.notice_change();
let prev_stmt = result.pop().unwrap();
let Statement::IfStatement(prev_if) = prev_stmt else { unreachable!() };
let prev_if = prev_if.unbox();
let test_expr = prev_if.unbox().test;
let test_expr = prev_if.test;
result.push(Statement::new_expression_statement(
test_expr.span(),
test_expr,
ctx,
));
Self::push_new_expression_stmt_to_result(test_expr, result, ctx);
break 'return_loop;
}
+1 -1
View File
@@ -21,7 +21,7 @@ Original | minified | minified | gzip | gzip | Iterations | Fi
3.20 MB | 958.89 kB | 1.01 MB | 317.07 kB | 331.56 kB | 2 | echarts.js
6.69 MB | 2.12 MB | 2.31 MB | 453.42 kB | 488.28 kB | 5 | antd.js
6.69 MB | 2.12 MB | 2.31 MB | 453.42 kB | 488.28 kB | 4 | antd.js
10.95 MB | 3.31 MB | 3.49 MB | 849.97 kB | 915.50 kB | 4 | typescript.js
@@ -5,8 +5,8 @@ checker.ts:
sys deallocs: 122
sys alloc bytes: 14996632 # 15.00 MB
sys peak growth: 10169560 # 10.17 MB
arena allocs: 59908
arena reallocs: 21415
arena allocs: 59899
arena reallocs: 21411
arena size: 16569784 # 16.57 MB
App.tsx:
@@ -38,20 +38,20 @@ pdf.mjs:
sys deallocs: 2427
sys alloc bytes: 5316219 # 5.32 MB
sys peak growth: 3693363 # 3.69 MB
arena allocs: 20927
arena reallocs: 7697
arena allocs: 20924
arena reallocs: 7696
arena size: 5782208 # 5.78 MB
antd.js:
file size: 6686316 # 6.69 MB
sys allocs: 1038
sys allocs: 860
sys reallocs: 56
sys deallocs: 1038
sys deallocs: 860
sys alloc bytes: 29976375 # 29.98 MB
sys peak growth: 19934749 # 19.93 MB
arena allocs: 207313
arena reallocs: 75941
arena size: 45450136 # 45.45 MB
arena allocs: 187331
arena reallocs: 63323
arena size: 43374888 # 43.37 MB
binder.ts:
file size: 193077 # 193.08 kB
@@ -71,6 +71,6 @@ kitchen-sink.tsx:
sys deallocs: 1854
sys alloc bytes: 5304692 # 5.30 MB
sys peak growth: 3707457 # 3.71 MB
arena allocs: 31993
arena reallocs: 12186
arena size: 8689832 # 8.69 MB
arena allocs: 31944
arena reallocs: 12185
arena size: 8688104 # 8.69 MB