From 029c84bcda6d313a32a187afcfa3ed3da709a992 Mon Sep 17 00:00:00 2001 From: Armano Date: Thu, 10 Sep 2026 03:44:47 +0200 Subject: [PATCH] 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 --- .../peephole/substitute_alternate_syntax.rs | 84 ++++++++----------- .../allocs_minifier.yaml | 18 ++-- 2 files changed, 45 insertions(+), 57 deletions(-) diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index e2e95b2982..bae06bc4fd 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -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 { diff --git a/tasks/track_memory_allocations/allocs_minifier.yaml b/tasks/track_memory_allocations/allocs_minifier.yaml index f80483d5cf..3121b228d4 100644 --- a/tasks/track_memory_allocations/allocs_minifier.yaml +++ b/tasks/track_memory_allocations/allocs_minifier.yaml @@ -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