Skip to content

Commit 6dcfa70

Browse files
authored
feat(es/minifier): Optimize number to int (#10294)
**Related issue:** - Closes #10281
1 parent 1d1ff9e commit 6dcfa70

File tree

13 files changed

+167
-32
lines changed

13 files changed

+167
-32
lines changed

.changeset/famous-dragons-divide.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_ecma_minifier: minor
3+
swc_core: minor
4+
---
5+
6+
feat(es/minifier): Optimize number to int

crates/swc_ecma_minifier/src/compress/pure/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ impl VisitMut for Pure<'_> {
612612
}
613613

614614
self.eval_member_expr(e);
615+
616+
self.optimize_to_int(e);
615617
}
616618

617619
fn visit_mut_expr_or_spreads(&mut self, nodes: &mut Vec<ExprOrSpread>) {

crates/swc_ecma_minifier/src/compress/pure/numbers.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use swc_common::{util::take::Take, EqIgnoreSpan, DUMMY_SP};
1+
use swc_common::{util::take::Take, EqIgnoreSpan, Spanned, DUMMY_SP};
22
use swc_ecma_ast::*;
33
use swc_ecma_utils::num_from_str;
44

@@ -138,4 +138,109 @@ impl Pure<'_> {
138138
_ => (),
139139
}
140140
}
141+
142+
pub(super) fn optimize_to_int(&mut self, e: &mut Expr) {
143+
let span = e.span();
144+
145+
match e {
146+
Expr::Bin(bin) => match (bin.op, &mut *bin.left, &mut *bin.right) {
147+
(op!("|"), Expr::Bin(bin_inner), Expr::Lit(Lit::Num(n)))
148+
| (op!("|"), Expr::Lit(Lit::Num(n)), Expr::Bin(bin_inner))
149+
if matches!(
150+
bin_inner.op,
151+
op!("<<") | op!(">>") | op!(">>>") | op!("|") | op!("^") | op!("&")
152+
) && n.value == 0.0 =>
153+
{
154+
report_change!("numbers: Turn '(a & b) | 0' into 'a & b'");
155+
self.changed = true;
156+
157+
let value = bin_inner.take();
158+
159+
*bin = BinExpr { span, ..value };
160+
}
161+
162+
(
163+
op!("|"),
164+
Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
165+
Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
166+
)
167+
| (
168+
op!("|"),
169+
Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
170+
Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
171+
) => {
172+
report_change!("numbers: Turn '~a | 0' into '~a'");
173+
self.changed = true;
174+
175+
let value = u.take();
176+
177+
*e = Expr::Unary(value);
178+
}
179+
180+
(
181+
op!("<<") | op!(">>") | op!("^"),
182+
_,
183+
Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
184+
) => {
185+
report_change!("numbers: Turn 'a << 0' into 'a | 0'");
186+
self.changed = true;
187+
bin.op = op!("|");
188+
}
189+
(
190+
op!("&"),
191+
_,
192+
Expr::Lit(Lit::Num(
193+
n @ Number {
194+
value: 4294967295.0, // 2^32 - 1
195+
..
196+
},
197+
)),
198+
)
199+
| (
200+
op!("&"),
201+
Expr::Lit(Lit::Num(
202+
n @ Number {
203+
value: 4294967295.0, // 2^32 - 1
204+
..
205+
},
206+
)),
207+
_,
208+
) => {
209+
report_change!("numbers: Turn 'a & 0XFFFFFFFF' into 'a | 0'");
210+
self.changed = true;
211+
bin.op = op!("|");
212+
n.value = 0.0;
213+
n.raw = None;
214+
}
215+
216+
_ => (),
217+
},
218+
219+
Expr::Assign(
220+
a @ AssignExpr {
221+
op: op!("<<=") | op!(">>=") | op!(">>>=") | op!("|=") | op!("^=") | op!("&="),
222+
..
223+
},
224+
) => {
225+
if let Expr::Bin(BinExpr {
226+
op: op!("|"),
227+
left,
228+
right,
229+
..
230+
}) = &mut *a.right
231+
{
232+
if let (e, Expr::Lit(Lit::Num(Number { value: 0.0, .. })))
233+
| (Expr::Lit(Lit::Num(Number { value: 0.0, .. })), e) =
234+
(&mut **left, &mut **right)
235+
{
236+
report_change!("numbers: Turn 'a |= b | 0' into 'a |= b'");
237+
self.changed = true;
238+
let value = e.take();
239+
*a.right = value;
240+
}
241+
}
242+
}
243+
_ => (),
244+
}
245+
}
141246
}

crates/swc_ecma_minifier/tests/benches-full/d3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3207,7 +3207,7 @@ function(global, factory) {
32073207
for(// Special case for the first row (y = -1, t2 = t3 = 0).
32083208
x = y = -1, cases[(t1 = values[0] >= value) << 1].forEach(stitch); ++x < dx - 1;)cases[(t0 = t1) | (t1 = values[x + 1] >= value) << 1].forEach(stitch);
32093209
// General case for the intermediate rows.
3210-
for(cases[t1 << 0].forEach(stitch); ++y < dy - 1;){
3210+
for(cases[0 | t1].forEach(stitch); ++y < dy - 1;){
32113211
for(x = -1, cases[(t1 = values[y * dx + dx] >= value) << 1 | (t2 = values[y * dx] >= value) << 2].forEach(stitch); ++x < dx - 1;)t0 = t1, t1 = values[y * dx + dx + x + 1] >= value, t3 = t2, cases[t0 | t1 << 1 | (t2 = values[y * dx + x + 1] >= value) << 2 | t3 << 3].forEach(stitch);
32123212
cases[t1 | t2 << 3].forEach(stitch);
32133213
}

crates/swc_ecma_minifier/tests/benches-full/three.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,15 +1683,15 @@ function(global, factory) {
16831683
}
16841684
var _proto = Layers.prototype;
16851685
return _proto.set = function(channel) {
1686-
this.mask = 1 << channel | 0;
1686+
this.mask = 1 << channel;
16871687
}, _proto.enable = function(channel) {
1688-
this.mask |= 1 << channel | 0;
1688+
this.mask |= 1 << channel;
16891689
}, _proto.enableAll = function() {
16901690
this.mask = -1;
16911691
}, _proto.toggle = function(channel) {
1692-
this.mask ^= 1 << channel | 0;
1692+
this.mask ^= 1 << channel;
16931693
}, _proto.disable = function(channel) {
1694-
this.mask &= ~(1 << channel | 0);
1694+
this.mask &= ~(1 << channel);
16951695
}, _proto.disableAll = function() {
16961696
this.mask = 0;
16971697
}, _proto.test = function(layers) {
@@ -2356,7 +2356,7 @@ function(global, factory) {
23562356
}, _proto.convertLinearToSRGB = function() {
23572357
return this.copyLinearToSRGB(this), this;
23582358
}, _proto.getHex = function() {
2359-
return 255 * this.r << 16 ^ 255 * this.g << 8 ^ 255 * this.b << 0;
2359+
return 255 * this.r << 16 ^ 255 * this.g << 8 ^ (255 * this.b | 0);
23602360
}, _proto.getHexString = function() {
23612361
return ('000000' + this.getHex().toString(16)).slice(-6);
23622362
}, _proto.getHSL = function(target) {
@@ -6337,15 +6337,15 @@ function(global, factory) {
63376337
]), 3));
63386338
var fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical), scope = this;
63396339
function getDepthMaterialVariant(useMorphing, useSkinning, useInstancing) {
6340-
var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2, material = _depthMaterials[index];
6340+
var index = 0 | useMorphing | useSkinning << 1 | useInstancing << 2, material = _depthMaterials[index];
63416341
return void 0 === material && (material = new MeshDepthMaterial({
63426342
depthPacking: 3201,
63436343
morphTargets: useMorphing,
63446344
skinning: useSkinning
63456345
}), _depthMaterials[index] = material), material;
63466346
}
63476347
function getDistanceMaterialVariant(useMorphing, useSkinning, useInstancing) {
6348-
var index = useMorphing << 0 | useSkinning << 1 | useInstancing << 2, material = _distanceMaterials[index];
6348+
var index = 0 | useMorphing | useSkinning << 1 | useInstancing << 2, material = _distanceMaterials[index];
63496349
return void 0 === material && (material = new MeshDistanceMaterial({
63506350
morphTargets: useMorphing,
63516351
skinning: useSkinning
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function bitwise1(a, b) {
2+
return (a & b) | 0;
3+
}
4+
5+
export function bitwise2(a) {
6+
return ~a | 0;
7+
}
8+
9+
export function bitwise3(a, b) {
10+
a ^= b | 0;
11+
12+
console.log(a | b, a & b);
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function bitwise1(a, b) {
2+
return a & b;
3+
}
4+
export function bitwise2(a) {
5+
return ~a;
6+
}
7+
export function bitwise3(a, b) {
8+
console.log((a ^= b) | b, a & b);
9+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// real life example taken from https://github.com/nodeca/pako/blob/master/lib/zlib/adler32.js#L26
22
export default ((adler, buf, len, pos)=>{
3-
let s1 = 0xffff & adler | 0, s2 = adler >>> 16 & 0xffff | 0, n = 0;
3+
let s1 = 0xffff & adler, s2 = adler >>> 16 & 0xffff, n = 0;
44
for(; 0 !== len;){
55
// Set limit ~ twice less than 5552, to keep
66
// s2 in 31-bits, because we force signed ints.
@@ -10,5 +10,5 @@ export default ((adler, buf, len, pos)=>{
1010
while (--n)
1111
s1 %= 65521, s2 %= 65521;
1212
}
13-
return s1 | s2 << 16 | 0;
13+
return s1 | s2 << 16;
1414
});

crates/swc_ecma_minifier/tests/fixture/next/33265/static/chunks/pages/index-cb36c1bf7f830e3c/output.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6821,15 +6821,15 @@
68216821
checkInt(this, value, offset, byteLength, limit - 1, -limit);
68226822
}
68236823
var i = 0, mul = 1, sub = 0;
6824-
for(this[offset] = 0xff & value; ++i < byteLength && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i - 1] && (sub = 1), this[offset + i] = (value / mul >> 0) - sub & 0xff;
6824+
for(this[offset] = 0xff & value; ++i < byteLength && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i - 1] && (sub = 1), this[offset + i] = (value / mul | 0) - sub & 0xff;
68256825
return offset + byteLength;
68266826
}, Buffer.prototype.writeIntBE = function(value, offset, byteLength, noAssert) {
68276827
if (value *= 1, offset >>>= 0, !noAssert) {
68286828
var limit = Math.pow(2, 8 * byteLength - 1);
68296829
checkInt(this, value, offset, byteLength, limit - 1, -limit);
68306830
}
68316831
var i = byteLength - 1, mul = 1, sub = 0;
6832-
for(this[offset + i] = 0xff & value; --i >= 0 && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i + 1] && (sub = 1), this[offset + i] = (value / mul >> 0) - sub & 0xff;
6832+
for(this[offset + i] = 0xff & value; --i >= 0 && (mul *= 0x100);)value < 0 && 0 === sub && 0 !== this[offset + i + 1] && (sub = 1), this[offset + i] = (value / mul | 0) - sub & 0xff;
68336833
return offset + byteLength;
68346834
}, Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
68356835
return value *= 1, offset >>>= 0, noAssert || checkInt(this, value, offset, 1, 0x7f, -128), value < 0 && (value = 0xff + value + 1), this[offset] = 0xff & value, offset + 1;

crates/swc_ecma_minifier/tests/fixture/next/feedback-util-promisify/chunks/pages/_app-72ad41192608e93a/output.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,15 +600,15 @@
600600
checkInt(this, e, r, t, n - 1, -n);
601601
}
602602
var i = 0, o = 1, u = 0;
603-
for(this[r] = 255 & e; ++i < t && (o *= 256);)e < 0 && 0 === u && 0 !== this[r + i - 1] && (u = 1), this[r + i] = (e / o >> 0) - u & 255;
603+
for(this[r] = 255 & e; ++i < t && (o *= 256);)e < 0 && 0 === u && 0 !== this[r + i - 1] && (u = 1), this[r + i] = (e / o | 0) - u & 255;
604604
return r + t;
605605
}, Buffer.prototype.writeIntBE = function(e, r, t, f) {
606606
if (e *= 1, r >>>= 0, !f) {
607607
var n = Math.pow(2, 8 * t - 1);
608608
checkInt(this, e, r, t, n - 1, -n);
609609
}
610610
var i = t - 1, o = 1, u = 0;
611-
for(this[r + i] = 255 & e; --i >= 0 && (o *= 256);)e < 0 && 0 === u && 0 !== this[r + i + 1] && (u = 1), this[r + i] = (e / o >> 0) - u & 255;
611+
for(this[r + i] = 255 & e; --i >= 0 && (o *= 256);)e < 0 && 0 === u && 0 !== this[r + i + 1] && (u = 1), this[r + i] = (e / o | 0) - u & 255;
612612
return r + t;
613613
}, Buffer.prototype.writeInt8 = function(e, r, t) {
614614
return e *= 1, r >>>= 0, t || checkInt(this, e, r, 1, 127, -128), e < 0 && (e = 255 + e + 1), this[r] = 255 & e, r + 1;

crates/swc_ecma_minifier/tests/fixture/next/react-pdf-renderer/output.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9868,10 +9868,10 @@
98689868
return n;
98699869
}
98709870
return function(r, s) {
9871-
var c = a(s = s || {}, "async", !1), f = {}, p = a(s, e, !1), d = {}, h = a(s, t, "\u00AD"), y = r.patterns.map(l), g = a(s, n, 5) >> 0, v = a(s, "html", !1), b = h + g;
9871+
var c = a(s = s || {}, "async", !1), f = {}, p = a(s, e, !1), d = {}, h = a(s, t, "\u00AD"), y = r.patterns.map(l), g = 0 | a(s, n, 5), v = a(s, "html", !1), b = h + g;
98729872
if (d[b] = u(r, h), f[b] = o(d[b]), c && !("Promise" in i)) throw Error("Failed to create hyphenator: Could not find global Promise object, needed for hyphenator to work in async mode");
98739873
return function(i, l) {
9874-
var s = a(l = l || {}, e, p), b = a(l, t, h), m = a(l, n, g) >> 0, D = b + m;
9874+
var s = a(l = l || {}, e, p), b = a(l, t, h), m = 0 | a(l, n, g), D = b + m;
98759875
return d[D] || (d[D] = u(r, b)), f[D] || (f[D] = o(d[D])), function(e, t, r, n, i, o, a, u) {
98769876
var l, s, c, f, p, d, h = "", y = (l = RegExp.prototype.test.bind(/\s/), f = 0, [
98779877
function() {
@@ -19083,13 +19083,13 @@
1908319083
},
1908419084
796: function(e) {
1908519085
e.exports = function(e, t, r, n) {
19086-
for(var i = 65535 & e | 0, o = e >>> 16 & 65535 | 0, a = 0; 0 !== r;){
19086+
for(var i = 65535 & e, o = e >>> 16 & 65535, a = 0; 0 !== r;){
1908719087
a = r > 2e3 ? 2e3 : r, r -= a;
1908819088
do o = o + (i = i + t[n++] | 0) | 0;
1908919089
while (--a)
1909019090
i %= 65521, o %= 65521;
1909119091
}
19092-
return i | o << 16 | 0;
19092+
return i | o << 16;
1909319093
};
1909419094
},
1909519095
234: function(e) {
@@ -19851,7 +19851,7 @@
1985119851
if (0 === b) break t;
1985219852
b--, D += p[h++] << w, w += 8;
1985319853
}
19854-
if (D !== (4294967295 & f.total)) {
19854+
if (D !== (0 | f.total)) {
1985519855
e.msg = "incorrect length check", f.mode = 30;
1985619856
break;
1985719857
}
@@ -20028,7 +20028,7 @@
2002820028
if (0 === e ? (R = M = f, b = 19) : 1 === e ? (R = i, I -= 257, M = o, N -= 257, b = 256) : (R = a, M = u, b = -1), F = 0, x = 0, _ = S, v = c, T = k, O = 0, y = -1, g = (P = 1 << k) - 1, 1 === e && P > 852 || 2 === e && P > 592) return 1;
2002920029
for(;;){
2003020030
m = _ - O, f[x] < b ? (D = 0, w = f[x]) : f[x] > b ? (D = M[N + f[x]], w = R[I + f[x]]) : (D = 96, w = 0), d = 1 << _ - O, S = h = 1 << T;
20031-
do s[v + (F >> O) + (h -= d)] = m << 24 | D << 16 | w | 0;
20031+
do s[v + (F >> O) + (h -= d)] = m << 24 | D << 16 | w;
2003220032
while (0 !== h)
2003320033
for(d = 1 << _ - 1; F & d;)d >>= 1;
2003420034
if (0 !== d ? (F &= d - 1, F += d) : F = 0, x++, 0 == --j[_]) {
@@ -20038,7 +20038,7 @@
2003820038
if (_ > k && (F & g) !== y) {
2003920039
for(0 === O && (O = k), v += S, C = 1 << (T = _ - O); T + O < A && !((C -= j[T + O]) <= 0);)T++, C <<= 1;
2004020040
if (P += 1 << T, 1 === e && P > 852 || 2 === e && P > 592) return 1;
20041-
s[y = F & g] = k << 24 | T << 16 | v - c | 0;
20041+
s[y = F & g] = k << 24 | T << 16 | v - c;
2004220042
}
2004320043
}
2004420044
return 0 !== F && (s[v + F] = _ - O << 24 | 4194304), p.bits = k, 0;
@@ -20839,15 +20839,15 @@
2083920839
D(this, e, t, r, i - 1, -i);
2084020840
}
2084120841
var o = 0, a = 1, u = 0;
20842-
for(this[t] = 255 & e; ++o < r && (a *= 256);)e < 0 && 0 === u && 0 !== this[t + o - 1] && (u = 1), this[t + o] = (e / a >> 0) - u & 255;
20842+
for(this[t] = 255 & e; ++o < r && (a *= 256);)e < 0 && 0 === u && 0 !== this[t + o - 1] && (u = 1), this[t + o] = (e / a | 0) - u & 255;
2084320843
return t + r;
2084420844
}, u.prototype.writeIntBE = function(e, t, r, n) {
2084520845
if (e *= 1, t >>>= 0, !n) {
2084620846
var i = Math.pow(2, 8 * r - 1);
2084720847
D(this, e, t, r, i - 1, -i);
2084820848
}
2084920849
var o = r - 1, a = 1, u = 0;
20850-
for(this[t + o] = 255 & e; --o >= 0 && (a *= 256);)e < 0 && 0 === u && 0 !== this[t + o + 1] && (u = 1), this[t + o] = (e / a >> 0) - u & 255;
20850+
for(this[t + o] = 255 & e; --o >= 0 && (a *= 256);)e < 0 && 0 === u && 0 !== this[t + o + 1] && (u = 1), this[t + o] = (e / a | 0) - u & 255;
2085120851
return t + r;
2085220852
}, u.prototype.writeInt8 = function(e, t, r) {
2085320853
return e *= 1, t >>>= 0, r || D(this, e, t, 1, 127, -128), e < 0 && (e = 255 + e + 1), this[t] = 255 & e, t + 1;

0 commit comments

Comments
 (0)