From fe748b4f8716cef2d88e188b539c5b45db3596e1 Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 24 Dec 2024 15:37:34 +0800 Subject: [PATCH 1/2] feat(minifier): only constant fold numbers when result is smaller --- .../src/ast_passes/peephole_fold_constants.rs | 48 ++++++++++++++++--- tasks/minsize/minsize.snap | 10 ++-- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index f79336b4b5d2d..0680f32b91683 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -30,6 +30,12 @@ impl<'a> Traverse<'a> for PeepholeFoldConstants { 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::Identifier(ident) => (ident.name == "Infinity") + .then(|| { + ctx.eval_to_number(expr) + .map(|v| ctx.value_to_expr(expr.span(), ConstantValue::Number(v))) + }) + .flatten(), Expression::CallExpression(e) => { Self::try_fold_useless_object_dot_define_properties_call(e, ctx) } @@ -246,9 +252,22 @@ impl<'a, 'b> PeepholeFoldConstants { | BinaryOperator::Division | BinaryOperator::Remainder | BinaryOperator::Multiplication - | BinaryOperator::Exponential => { - ctx.eval_binary_expression(e).map(|v| ctx.value_to_expr(e.span, v)) + | BinaryOperator::Exponential => match (&e.left, &e.right) { + (Expression::NumericLiteral(left), Expression::NumericLiteral(right)) => { + let value = ctx.eval_binary_expression(e)?; + let ConstantValue::Number(num) = value else { return None }; + (num.is_nan() + || num.is_infinite() + || (num.abs() <= f64::powf(2.0, 53.0) + && Self::approximate_printed_int_char_count(num) + <= Self::approximate_printed_int_char_count(left.value) + + Self::approximate_printed_int_char_count(right.value) + + e.operator.as_str().len())) + .then(|| value) + } + _ => ctx.eval_binary_expression(e), } + .map(|v| ctx.value_to_expr(e.span, v)), BinaryOperator::BitwiseAnd | BinaryOperator::BitwiseOR | BinaryOperator::BitwiseXOR => { if let Some(v) = ctx.eval_binary_expression(e) { return Some(ctx.value_to_expr(e.span, v)); @@ -260,6 +279,21 @@ impl<'a, 'b> PeepholeFoldConstants { } } + // https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L1128 + fn approximate_printed_int_char_count(value: f64) -> usize { + let mut count = if value.is_infinite() { + "Infinity".len() + } else if value.is_nan() { + "NaN".len() + } else { + 1 + 0.max(value.abs().log10().floor() as usize) + }; + if value.is_sign_negative() { + count += 1; + } + count + } + fn try_fold_left_child_op( e: &mut BinaryExpression<'a>, ctx: Ctx<'a, '_>, @@ -1310,8 +1344,8 @@ mod test { #[test] fn test_fold_bitwise_op_additional() { test("x = null & 1", "x = 0"); - test("x = (2 ** 31 - 1) | 1", "x = 2147483647"); - test("x = (2 ** 31) | 1", "x = -2147483647"); + test_same("x = (2 ** 31 - 1) | 1"); + test_same("x = (2 ** 31) | 1"); // https://github.com/oxc-project/oxc/issues/7944 test_same("(x - 1) & 1"); @@ -1344,9 +1378,9 @@ mod test { test("x = 10 >>> 1", "x=5"); test("x = 10 >>> 2", "x=2"); test("x = 10 >>> 5", "x=0"); - test("x = -1 >>> 1", "x=2147483647"); // 0x7fffffff - test("x = -1 >>> 0", "x=4294967295"); // 0xffffffff - test("x = -2 >>> 0", "x=4294967294"); // 0xfffffffe + test_same("x = -1 >>> 1"); + test_same("x = -1 >>> 0"); + test_same("x = -2 >>> 0"); test("x = 0x90000000 >>> 28", "x=9"); test("x = 0xffffffff << 0", "x=-1"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index d1d640a2b34bd..daff132d79b1e 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -9,17 +9,17 @@ Original | minified | minified | gzip | gzip | Fixture 342.15 kB | 121.55 kB | 118.14 kB | 44.64 kB | 44.37 kB | vue.js -544.10 kB | 73.32 kB | 72.48 kB | 26.13 kB | 26.20 kB | lodash.js +544.10 kB | 73.31 kB | 72.48 kB | 26.12 kB | 26.20 kB | lodash.js -555.77 kB | 276.06 kB | 270.13 kB | 91.14 kB | 90.80 kB | d3.js +555.77 kB | 275.92 kB | 270.13 kB | 91.13 kB | 90.80 kB | d3.js 1.01 MB | 466.83 kB | 458.89 kB | 126.74 kB | 126.71 kB | bundle.min.js -1.25 MB | 661.47 kB | 646.76 kB | 163.94 kB | 163.73 kB | three.js +1.25 MB | 661.43 kB | 646.76 kB | 163.92 kB | 163.73 kB | three.js -2.14 MB | 740.44 kB | 724.14 kB | 181.35 kB | 181.07 kB | victory.js +2.14 MB | 740.33 kB | 724.14 kB | 181.33 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 332.00 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 331.93 kB | 331.56 kB | echarts.js 6.69 MB | 2.39 MB | 2.31 MB | 495.62 kB | 488.28 kB | antd.js From 22d6072cb825d37c58686bc3b922123f7758dece Mon Sep 17 00:00:00 2001 From: Boshen Date: Tue, 24 Dec 2024 16:14:53 +0800 Subject: [PATCH 2/2] u --- .../src/ast_passes/peephole_fold_constants.rs | 14 ++++++++++++-- tasks/minsize/minsize.snap | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs index 0680f32b91683..91d161baabe11 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs @@ -254,8 +254,17 @@ impl<'a, 'b> PeepholeFoldConstants { | BinaryOperator::Multiplication | BinaryOperator::Exponential => match (&e.left, &e.right) { (Expression::NumericLiteral(left), Expression::NumericLiteral(right)) => { + // Do not fold any division unless rhs is 0. + if e.operator == BinaryOperator::Division + && right.value != 0.0 + && !right.value.is_nan() + && !right.value.is_infinite() + { + return None; + } let value = ctx.eval_binary_expression(e)?; let ConstantValue::Number(num) = value else { return None }; + Self::approximate_printed_int_char_count(num); (num.is_nan() || num.is_infinite() || (num.abs() <= f64::powf(2.0, 53.0) @@ -263,7 +272,7 @@ impl<'a, 'b> PeepholeFoldConstants { <= Self::approximate_printed_int_char_count(left.value) + Self::approximate_printed_int_char_count(right.value) + e.operator.as_str().len())) - .then(|| value) + .then_some(value) } _ => ctx.eval_binary_expression(e), } @@ -280,6 +289,7 @@ impl<'a, 'b> PeepholeFoldConstants { } // https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L1128 + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] fn approximate_printed_int_char_count(value: f64) -> usize { let mut count = if value.is_infinite() { "Infinity".len() @@ -1407,7 +1417,7 @@ mod test { #[test] fn test_fold_arithmetic() { test("x = 10 + 20", "x = 30"); - test("x = 2 / 4", "x = 0.5"); + test_same("x = 2 / 4"); test("x = 2.25 * 3", "x = 6.75"); test_same("z = x * y"); test_same("x = y * 5"); diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index daff132d79b1e..3419202067295 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -11,15 +11,15 @@ Original | minified | minified | gzip | gzip | Fixture 544.10 kB | 73.31 kB | 72.48 kB | 26.12 kB | 26.20 kB | lodash.js -555.77 kB | 275.92 kB | 270.13 kB | 91.13 kB | 90.80 kB | d3.js +555.77 kB | 275.71 kB | 270.13 kB | 91.08 kB | 90.80 kB | d3.js 1.01 MB | 466.83 kB | 458.89 kB | 126.74 kB | 126.71 kB | bundle.min.js -1.25 MB | 661.43 kB | 646.76 kB | 163.92 kB | 163.73 kB | three.js +1.25 MB | 661.30 kB | 646.76 kB | 163.92 kB | 163.73 kB | three.js -2.14 MB | 740.33 kB | 724.14 kB | 181.33 kB | 181.07 kB | victory.js +2.14 MB | 740.13 kB | 724.14 kB | 181.28 kB | 181.07 kB | victory.js -3.20 MB | 1.02 MB | 1.01 MB | 331.93 kB | 331.56 kB | echarts.js +3.20 MB | 1.02 MB | 1.01 MB | 331.88 kB | 331.56 kB | echarts.js 6.69 MB | 2.39 MB | 2.31 MB | 495.62 kB | 488.28 kB | antd.js