perf(minfier): update expressions in place when substituting alternate syntax (#26460)

- `substitute_typeof_undefined`: do not recreate binnary expression and
instead just update right/left argument and operator
- `substitute_rotate_binary_expression`: replace `take_in*2` and
reassign with `std::mem::swap`
- `substitute_object_or_array_constructor`,
`substitute_global_new_expression`: introduce new helper
`transform_new_expr_to_call_expr` to convert new to call expr
- `substitute_iife_call`: use array to alloc all items in sequence
without reallocs
This commit is contained in:
Armano
2026-09-10 03:44:47 +02:00
committed by GitHub
parent 0df2b6c197
commit 029c84bcda
2 changed files with 45 additions and 57 deletions
@@ -245,21 +245,14 @@ impl<'a> PeepholeOptimizations {
let new_value = if let Expression::Identifier(ident) = &unary_expr.argument
&& ctx.is_global_reference(ident)
{
let left = e.left.take_in(ctx);
let right = Expression::new_string_literal(e.right.span(), "u", None, ctx);
Expression::new_binary_expression(e.span, left, new_comp_op, right, ctx)
e.operator = new_comp_op;
Expression::new_string_literal(e.right.span(), "u", None, ctx)
} else {
let span = e.span;
let Expression::UnaryExpression(unary_expr) = &mut e.left else { return };
Expression::new_binary_expression(
span,
unary_expr.take_in(ctx).argument,
new_eq_op,
Expression::new_void_0(e.right.span(), ctx),
ctx,
)
e.operator = new_eq_op;
ctx.replace_expression_with(&mut e.left, Self::unwrap_unary);
Expression::new_void_0(e.right.span(), ctx)
};
ctx.replace_expression(expr, new_value);
ctx.replace_expression(&mut e.right, new_value);
}
/// Remove unary `+` if `ToNumber` conversion is done by the parent expression
@@ -435,10 +428,8 @@ impl<'a> PeepholeOptimizations {
&& !right.left.may_have_side_effects(ctx)
&& !right.right.may_have_side_effects(ctx)
{
let left = e.left.take_in(ctx);
let right = e.right.take_in(ctx);
e.right = left;
e.left = right;
let binary_expr = e.as_mut();
std::mem::swap(&mut binary_expr.left, &mut binary_expr.right);
ctx.notice_change();
}
}
@@ -1206,14 +1197,14 @@ impl<'a> PeepholeOptimizations {
_ => return,
};
let Some(name) = Self::get_fold_constructor_name(callee, ctx) else { return };
let (span, callee, args, is_new_expr) = match expr {
let (span, args, is_new_expr) = match expr {
Expression::NewExpression(e) => {
let NewExpression { span, callee, arguments, .. } = e.as_mut();
(span, callee, arguments, true)
let NewExpression { span, arguments, .. } = e.as_mut();
(span, arguments, true)
}
Expression::CallExpression(e) => {
let CallExpression { span, callee, arguments, .. } = e.as_mut();
(span, callee, arguments, false)
let CallExpression { span, arguments, .. } = e.as_mut();
(span, arguments, false)
}
_ => return,
};
@@ -1257,12 +1248,10 @@ impl<'a> PeepholeOptimizations {
}
}
if is_new_expr {
let callee = callee.take_in(ctx);
let args = args.take_in(ctx);
let new_value = Expression::new_call_expression(
*span, callee, None, args, false, ctx,
ctx.replace_expression_with(
expr,
Self::transform_new_expr_to_call_expr,
);
ctx.replace_expression(expr, new_value);
}
}
// `new Array(literal)` -> `[literal]`
@@ -1276,11 +1265,7 @@ impl<'a> PeepholeOptimizations {
}
// `new Array(x)` -> `Array(x)`
else if is_new_expr {
let callee = callee.take_in(ctx);
let args = args.take_in(ctx);
let new_value =
Expression::new_call_expression(*span, callee, None, args, false, ctx);
ctx.replace_expression(expr, new_value);
ctx.replace_expression_with(expr, Self::transform_new_expr_to_call_expr);
}
} else {
// `Array` has special length-constructor behavior only when it receives
@@ -1350,16 +1335,7 @@ impl<'a> PeepholeOptimizations {
_ if Self::is_native_error_name(name) => true,
_ => unreachable!(),
} {
let new_value = Expression::new_call_expression_with_pure(
e.span,
e.callee.take_in(ctx),
None,
e.arguments.take_in(ctx),
false,
e.pure,
ctx,
);
ctx.replace_expression(expr, new_value);
ctx.replace_expression_with(expr, Self::transform_new_expr_to_call_expr);
}
}
@@ -1983,12 +1959,7 @@ impl<'a> PeepholeOptimizations {
{
Expression::new_sequence_expression(
expr_stmt.span,
{
let mut sequence = ArenaVec::new_in(ctx);
sequence.push(taken);
sequence.push(Expression::new_void_0(call_expr.span, ctx));
sequence
},
[taken, Expression::new_void_0(call_expr.span, ctx)],
ctx,
)
} else {
@@ -2085,6 +2056,23 @@ impl<'a> PeepholeOptimizations {
}
Some(taken)
}
fn transform_new_expr_to_call_expr(
expr: Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let Expression::NewExpression(new_expr) = expr else { unreachable!() };
let e = new_expr.unbox();
Expression::new_call_expression_with_pure(
e.span,
e.callee,
None,
e.arguments,
false,
e.pure,
ctx,
)
}
}
struct ClassPropertyKeyParent {
@@ -5,7 +5,7 @@ checker.ts:
sys deallocs: 150
sys alloc bytes: 14996632 # 15.00 MB
sys peak growth: 10169560 # 10.17 MB
arena allocs: 72029
arena allocs: 72024
arena reallocs: 28269
arena size: 17401952 # 17.40 MB
@@ -16,7 +16,7 @@ App.tsx:
sys deallocs: 61
sys alloc bytes: 2128563 # 2.13 MB
sys peak growth: 1612427 # 1.61 MB
arena allocs: 8372
arena allocs: 8350
arena reallocs: 2709
arena size: 2719488 # 2.72 MB
@@ -38,9 +38,9 @@ pdf.mjs:
sys deallocs: 2427
sys alloc bytes: 5316219 # 5.32 MB
sys peak growth: 3693363 # 3.69 MB
arena allocs: 21027
arena reallocs: 7699
arena size: 5783984 # 5.78 MB
arena allocs: 20927
arena reallocs: 7697
arena size: 5782208 # 5.78 MB
antd.js:
file size: 6686316 # 6.69 MB
@@ -49,9 +49,9 @@ antd.js:
sys deallocs: 1038
sys alloc bytes: 29976375 # 29.98 MB
sys peak growth: 19934749 # 19.93 MB
arena allocs: 207570
arena allocs: 207313
arena reallocs: 75941
arena size: 45456840 # 45.46 MB
arena size: 45450136 # 45.45 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: 32092
arena allocs: 31992
arena reallocs: 12187
arena size: 8691304 # 8.69 MB
arena size: 8689832 # 8.69 MB