mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
feat(minifier): minimize bitwise binary expressions (#27107)
implement missing optimizations for bitwise binnary expressions - `(a OP b) | 0` -> `a OP b` - `a & 0xffffffff` -> `a | 0`.
This commit is contained in:
1 parent
b2793fa2d8
commit
1c352ce311
6 files changed
+164
-9
No files matched your search
@@ -0,0 +1,79 @@
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_ecmascript::constant_evaluation::IsInt32OrUint32;
|
||||
use oxc_syntax::operator::BinaryOperator;
|
||||
|
||||
use crate::TraverseCtx;
|
||||
use crate::peephole::PeepholeOptimizations;
|
||||
|
||||
impl<'a> PeepholeOptimizations {
|
||||
pub fn minimize_bitwise_binary_expr(expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
let Expression::BinaryExpression(bin_expr) = expr else { return };
|
||||
if !bin_expr.operator.is_bitwise() {
|
||||
return;
|
||||
}
|
||||
|
||||
match bin_expr.operator {
|
||||
BinaryOperator::BitwiseAnd => {
|
||||
// u32::MAX -> 2^32-1 -> 4_294_967_295
|
||||
if bin_expr.right.is_number_value(4_294_967_295.0)
|
||||
&& let Expression::NumericLiteral(n) = &mut bin_expr.right
|
||||
{
|
||||
// `a & 0xffffffff` -> `a | 0`
|
||||
ctx.notice_change();
|
||||
n.value = 0.0;
|
||||
n.raw = None;
|
||||
bin_expr.operator = BinaryOperator::BitwiseOR;
|
||||
} else if bin_expr.left.is_number_value(4_294_967_295.0)
|
||||
&& let Expression::NumericLiteral(n) = &mut bin_expr.left
|
||||
{
|
||||
// `0xffffffff & a` -> `0 | a`
|
||||
ctx.notice_change();
|
||||
n.value = 0.0;
|
||||
n.raw = None;
|
||||
bin_expr.operator = BinaryOperator::BitwiseOR;
|
||||
}
|
||||
}
|
||||
BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight | BinaryOperator::BitwiseXOR
|
||||
if bin_expr.right.is_number_0() =>
|
||||
{
|
||||
// `a << 0` -> `a | 0`
|
||||
// `a >> 0` -> `a | 0`
|
||||
// `a ^ 0` -> `a | 0`
|
||||
ctx.notice_change();
|
||||
bin_expr.operator = BinaryOperator::BitwiseOR;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if bin_expr.operator == BinaryOperator::BitwiseOR {
|
||||
if bin_expr.right.is_number_0() && Self::is_bitwise_int(&bin_expr.left, ctx) {
|
||||
// `(a OP b) | 0` -> `a OP b`
|
||||
ctx.replace_expression_with(expr, Self::unwrap_left_from_binary_expr);
|
||||
} else if bin_expr.left.is_number_0() && Self::is_bitwise_int(&bin_expr.right, ctx) {
|
||||
// `0 | (a OP b)` -> `a OP b`
|
||||
ctx.replace_expression_with(expr, Self::unwrap_right_from_binary_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_bitwise_int(expr: &Expression<'a>, ctx: &TraverseCtx<'a>) -> bool {
|
||||
let Expression::BinaryExpression(e) = expr else { return false };
|
||||
e.operator != BinaryOperator::ShiftRightZeroFill && e.is_int32_or_uint32(ctx)
|
||||
}
|
||||
|
||||
fn unwrap_left_from_binary_expr(
|
||||
e: Expression<'a>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let Expression::BinaryExpression(e) = e else { unreachable!() };
|
||||
e.unbox().left
|
||||
}
|
||||
|
||||
fn unwrap_right_from_binary_expr(
|
||||
e: Expression<'a>,
|
||||
_ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let Expression::BinaryExpression(e) = e else { unreachable!() };
|
||||
e.unbox().right
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
mod convert_to_dotted_properties;
|
||||
mod fold_constants;
|
||||
mod inline;
|
||||
mod minimize_binary_expression;
|
||||
mod minimize_conditional_expression;
|
||||
mod minimize_conditions;
|
||||
mod minimize_expression_in_boolean_context;
|
||||
@@ -575,6 +576,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
|
||||
Self::fold_sequence_expression(expr, ctx);
|
||||
Self::minimize_loose_boolean(expr, ctx);
|
||||
Self::minimize_binary(expr, ctx);
|
||||
Self::minimize_bitwise_binary_expr(expr, ctx);
|
||||
Self::substitute_loose_equals_undefined(expr, ctx);
|
||||
Self::substitute_typeof_undefined(expr, ctx);
|
||||
Self::substitute_rotate_binary_expression(expr, ctx);
|
||||
|
||||
@@ -4,7 +4,7 @@ static NEG_MAX_SAFE_FLOAT: f64 = -9_007_199_254_740_991_f64;
|
||||
static MAX_SAFE_INT: i64 = 9_007_199_254_740_991_i64;
|
||||
static NEG_MAX_SAFE_INT: i64 = -9_007_199_254_740_991_i64;
|
||||
|
||||
use crate::test;
|
||||
use crate::{test, test_same};
|
||||
|
||||
// wrap with a function call so it doesn't get removed.
|
||||
fn fold(source_text: &str, expected: &str) {
|
||||
@@ -17,10 +17,6 @@ fn fold_same(source_text: &str) {
|
||||
fold(source_text, source_text);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comparison() {
|
||||
fold("(1, 2) !== 2", "!1");
|
||||
@@ -855,12 +851,12 @@ fn test_fold_bitwise_op2() {
|
||||
fold("x = y | 3 | 3", "x = y | 3");
|
||||
fold("x = 3 | y | 3", "x = y | 3");
|
||||
|
||||
fold("x = y ^ 1 ^ 1", "x = y ^ 0");
|
||||
fold("x = y ^ 1 ^ 1", "x = y | 0");
|
||||
fold("x = y ^ 1 ^ 2", "x = y ^ 3");
|
||||
fold("x = y ^ 3 ^ 1", "x = y ^ 2");
|
||||
fold("x = 3 ^ y ^ 1", "x = y ^ 2");
|
||||
fold("x = y ^ 3 ^ 3", "x = y ^ 0");
|
||||
fold("x = 3 ^ y ^ 3", "x = y ^ 0");
|
||||
fold("x = y ^ 3 ^ 3", "x = y | 0");
|
||||
fold("x = 3 ^ y ^ 3", "x = y | 0");
|
||||
|
||||
fold("x = Infinity | NaN", "x=0");
|
||||
fold("x = 12 | NaN", "x=12");
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::test;
|
||||
|
||||
// wrap with a function call so it doesn't get removed.
|
||||
fn fold(source_text: &str, expected: &str) {
|
||||
let source_text = format!("NOOP({source_text})");
|
||||
let expected = format!("NOOP({expected})");
|
||||
test(&source_text, &expected);
|
||||
}
|
||||
|
||||
fn fold_same(source_text: &str) {
|
||||
fold(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimize_bitwise_binary_expr() {
|
||||
// `(a OP b) | 0` -> `a OP b`.
|
||||
fold_same("a << b | 0");
|
||||
fold_same("0 | (a << b)");
|
||||
fold_same("a >> b | 0");
|
||||
fold_same("0 | (a >> b)");
|
||||
fold_same("a >>> b | 0");
|
||||
fold_same("0 | (a >>> b)");
|
||||
fold_same("(-1 >>> a) | b");
|
||||
fold_same("a | b | 0");
|
||||
fold_same("0 | (a | b)");
|
||||
fold_same("a ^ b | 0");
|
||||
fold_same("0 | (a ^ b)");
|
||||
fold_same("a & b | 0");
|
||||
fold_same("0 | (a & b)");
|
||||
|
||||
// `(a | 0) OP b` and `a OP (b | 0)`.
|
||||
fold("((1 | a) | 0) << b", "(a | 1) << b");
|
||||
fold("a << ((1 | b) | 0)", "a << (b | 1)");
|
||||
fold("((1 | a) | 0) >> b", "(a | 1) >> b");
|
||||
fold("a >> ((1 | b) | 0)", "a >> (b | 1)");
|
||||
fold("((1 | a) | 0) >>> b", "(a | 1) >>> b");
|
||||
fold("a >>> ((1 | b) | 0)", "a >>> (b | 1)");
|
||||
fold("((1 | a) | 0) | b", "(a | 1) | b");
|
||||
fold("a | ((1 | b) | 0)", "a | b | 1");
|
||||
fold("((1 | a) | 0) ^ b", "(a | 1) ^ b");
|
||||
fold("a ^ ((1 | b) | 0)", "a ^ (b | 1)");
|
||||
fold("((1 | a) | 0) & b", "(a | 1) & b");
|
||||
fold("a & ((1 | b) | 0)", "a & (b | 1)");
|
||||
|
||||
fold_same("(a | 0) << b");
|
||||
fold_same("a << (b | 0)");
|
||||
fold_same("(a | 0) >> b");
|
||||
fold_same("a >> (b | 0)");
|
||||
fold_same("(a | 0) >>> b");
|
||||
fold_same("a >>> (b | 0)");
|
||||
fold_same("(a | 0) | b");
|
||||
fold("a | (b | 0)", "a | b | 0");
|
||||
fold_same("(a | 0) ^ b");
|
||||
fold_same("a ^ (b | 0)");
|
||||
fold_same("(a | 0) & b");
|
||||
fold_same("a & (b | 0)");
|
||||
|
||||
// `a OP 0` -> `a | 0`
|
||||
fold("a << 0", "a | 0");
|
||||
fold("a >> 0", "a | 0");
|
||||
fold("a ^ 0", "a | 0");
|
||||
|
||||
// `a & 0xffffffff` -> `a | 0`.
|
||||
fold("a & 0xffffffff", "a | 0");
|
||||
fold("4294967295 & a", "0 | a");
|
||||
fold("a & 4294967294.9999999", "a | 0");
|
||||
fold_same("a & 4294967294.999999");
|
||||
|
||||
fold_same("a + 0");
|
||||
fold_same("a | 1");
|
||||
fold_same("a & 1");
|
||||
fold_same("a << 1");
|
||||
fold_same("a | (b + 0)");
|
||||
fold_same("a | ~b");
|
||||
fold_same("~a | 0");
|
||||
fold_same("0 | ~a");
|
||||
}
|
||||
@@ -8,6 +8,7 @@ mod inline;
|
||||
mod inline_single_use_variable;
|
||||
mod manual_pure_functions;
|
||||
mod merge_assignments_to_declarations;
|
||||
mod minimize_binary_expression;
|
||||
mod minimize_conditional_expression;
|
||||
mod minimize_conditions;
|
||||
mod minimize_exit_points;
|
||||
|
||||
@@ -15,7 +15,7 @@ Original | minified | minified | gzip | gzip | Iterations | Fi
|
||||
|
||||
1.01 MB | 435.91 kB | 458.89 kB | 121.78 kB | 126.71 kB | 2 | bundle.min.js
|
||||
|
||||
1.25 MB | 634.24 kB | 646.76 kB | 158.44 kB | 163.73 kB | 2 | three.js
|
||||
1.25 MB | 634.23 kB | 646.76 kB | 158.43 kB | 163.73 kB | 2 | three.js
|
||||
|
||||
2.14 MB | 707.61 kB | 724.14 kB | 160.07 kB | 181.07 kB | 2 | victory.js
|
||||
|
||||
|
||||
Reference in new issue
Block a user