feat(minifier): implement try_fold_shift (#478)

* feat: implement `try_fold_shift`

* update minsize
This commit is contained in:
阿良仔 2023-06-27 04:44:23 +08:00 committed by GitHub
parent f12166c92f
commit 1182985bb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 3 deletions

View file

@ -108,6 +108,14 @@ impl<'a> Compressor<'a> {
&binary_expr.left,
&binary_expr.right,
),
BinaryOperator::ShiftLeft
| BinaryOperator::ShiftRight
| BinaryOperator::ShiftRightZeroFill => self.try_fold_shift(
binary_expr.span,
binary_expr.operator,
&binary_expr.left,
&binary_expr.right,
),
_ => None,
},
Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
@ -513,4 +521,63 @@ impl<'a> Compressor<'a> {
}
None
}
/// ported from [closure-compiler](https://github.com/google/closure-compiler/blob/a4c880032fba961f7a6c06ef99daa3641810bfdd/src/com/google/javascript/jscomp/PeepholeFoldConstants.java#L1114-L1162)
#[allow(clippy::cast_possible_truncation)]
fn try_fold_shift<'b>(
&mut self,
span: Span,
op: BinaryOperator,
left: &'b Expression<'a>,
right: &'b Expression<'a>,
) -> Option<Expression<'a>> {
let left_num = get_side_free_number_value(left);
let right_num = get_side_free_number_value(right);
if let Some(NumberValue::Number(left_val)) = left_num &&
let Some(NumberValue::Number(right_val)) = right_num {
if left_val.fract() != 0.0 || right_val.fract() != 0.0 {
return None;
}
// only the lower 5 bits are used when shifting, so don't do anything
// if the shift amount is outside [0,32)
if !(0.0..32.0).contains(&right_val) {
return None;
}
let right_val_int = right_val as i32;
let bits = NumberLiteral::ecmascript_to_int32(left_val);
let result_val: f64 = match op {
BinaryOperator::ShiftLeft => {
f64::from(bits << right_val_int)
}
BinaryOperator::ShiftRight => {
f64::from(bits >> right_val_int)
}
BinaryOperator::ShiftRightZeroFill => {
// JavaScript always treats the result of >>> as unsigned.
// We must force Rust to do the same here.
#[allow(clippy::cast_sign_loss)]
let res = bits as u32 >> right_val_int as u32;
f64::from(res)
}
_ => unreachable!("Unknown binary operator {:?}", op)
};
let value_raw = self.hir.new_str(result_val.to_string().as_str());
let number_literal = self.hir.number_literal(
span,
result_val,
value_raw,
NumberBase::Decimal,
);
return Some(self.hir.literal_number_expression(number_literal));
}
None
}
}

View file

@ -234,3 +234,38 @@ fn test_fold_void() {
test("void x", "void 0");
test_same("void x()");
}
#[test]
fn test_fold_bit_shift() {
test("x = 1 << 0", "x=1");
test("x = -1 << 0", "x=-1");
test("x = 1 << 1", "x=2");
test("x = 3 << 1", "x=6");
test("x = 1 << 8", "x=256");
test("x = 1 >> 0", "x=1");
test("x = -1 >> 0", "x=-1");
test("x = 1 >> 1", "x=0");
test("x = 2 >> 1", "x=1");
test("x = 5 >> 1", "x=2");
test("x = 127 >> 3", "x=15");
test("x = 3 >> 1", "x=1");
test("x = 3 >> 2", "x=0");
test("x = 10 >> 1", "x=5");
test("x = 10 >> 2", "x=2");
test("x = 10 >> 5", "x=0");
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("x = 0x90000000 >>> 28", "x=9");
test("x = 0xffffffff << 0", "x=-1");
test("x = 0xffffffff << 4", "x=-16");
test("1 << 32", "1<<32");
test("1 << -1", "1<<-1");
test("1 >> 32", "1>>32");
}

View file

@ -11,15 +11,15 @@ Original -> Minified -> Gzip Brotli
555.77 kB -> 274.89 kB -> 91.40 kB 76.86 kB d3.js
977.19 kB -> 456.44 kB -> 123.71 kB 107.10 kB bundle.min.js
977.19 kB -> 456.43 kB -> 123.71 kB 107.19 kB bundle.min.js
1.25 MB -> 677.74 kB -> 166.58 kB 134.57 kB three.js
2.14 MB -> 743.86 kB -> 181.82 kB 138.89 kB victory.js
3.20 MB -> 1.03 MB -> 332.93 kB 268.96 kB echarts.js
3.20 MB -> 1.03 MB -> 332.93 kB 269.04 kB echarts.js
6.69 MB -> 2.40 MB -> 498.41 kB 390.35 kB antd.js
10.82 MB -> 3.53 MB -> 903.10 kB 692.91 kB typescript.js
10.82 MB -> 3.53 MB -> 903.09 kB 693.53 kB typescript.js