Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(es/minfiier): Compute more with sequential inliner #6169

Merged
merged 29 commits into from Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
//// [compoundAdditionAssignmentLHSCanBeAssigned.ts]
var E, a, b, x1, x2, x3, x4, x6;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b";
}(E || (E = {})), x1 += a, x1 += b, x1 += !0, x1 += 0, x1 += "", x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += b, x2 += !0, x2 += 0, x2 += "", x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += 0, x3 += E.a, x3 += null, x3 += void 0, x4 += a, x4 += 0, x4 += E.a, x4 += null, x4 += void 0, x6 += a, x6 += "";
}(E || (E = {})), E.a, E.a, E.a, E.a;
@@ -1,5 +1,5 @@
//// [compoundAdditionAssignmentWithInvalidOperands.ts]
var E, a, x1, x2, x3, x4, x5;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b";
}(E || (E = {})), x1 += a, x1 += !0, x1 += 0, x1 += E.a, x1 += {}, x1 += null, x1 += void 0, x2 += a, x2 += !0, x2 += 0, x2 += E.a, x2 += {}, x2 += null, x2 += void 0, x3 += a, x3 += !0, x3 += 0, x3 += E.a, x3 += {}, x3 += null, x3 += void 0, x4 += a, x4 += !0, x4 += {}, x5 += a, x5 += !0, x5 += {};
}(E || (E = {})), E.a, E.a, E.a;
@@ -1,5 +1,5 @@
//// [compoundArithmeticAssignmentLHSCanBeAssigned.ts]
var E, a, b, c, x1, x2, x3;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b", E[E.c = 2] = "c";
}(E || (E = {})), x1 *= a, x1 *= b, x1 *= c, x1 *= null, x1 *= void 0, x2 *= a, x2 *= b, x2 *= c, x2 *= null, x2 *= void 0, x3 *= a, x3 *= b, x3 *= c, x3 *= null, x3 *= void 0;
}(E || (E = {}));
@@ -1,5 +1,5 @@
//// [compoundArithmeticAssignmentWithInvalidOperands.ts]
var E, a, b, x1, x2, x3, x4, x5, x6;
var E;
!function(E) {
E[E.a = 0] = "a", E[E.b = 1] = "b";
}(E || (E = {})), x1 *= a, x1 *= b, x1 *= !0, x1 *= 0, x1 *= "", x1 *= E.a, x1 *= {}, x1 *= null, x1 *= void 0, x2 *= a, x2 *= b, x2 *= !0, x2 *= 0, x2 *= "", x2 *= E.a, x2 *= {}, x2 *= null, x2 *= void 0, x3 *= a, x3 *= b, x3 *= !0, x3 *= 0, x3 *= "", x3 *= E.a, x3 *= {}, x3 *= null, x3 *= void 0, x4 *= a, x4 *= b, x4 *= !0, x4 *= 0, x4 *= "", x4 *= E.a, x4 *= {}, x4 *= null, x4 *= void 0, x5 *= b, x5 *= !0, x5 *= "", x5 *= {}, x6 *= b, x6 *= !0, x6 *= "", x6 *= {};
}(E || (E = {})), E.a, E.a, E.a, E.a;
@@ -1,4 +1,4 @@
//// [destructuringControlFlow.ts]
(0, [
[
"foo"
][1]).toUpperCase();
][1].toUpperCase();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that it matters in this specific case, but this has changed the this context of the method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, I'll add a test case for it.

@@ -1,3 +1,3 @@
//// [symbolType12.ts]
var s = Symbol.for("assign"), str = "";
s *= s, s *= 0, s /= s, s /= 0, s %= s, s %= 0, s += s, s += 0, s += "", str += s, s -= s, s -= 0, s <<= s, s <<= 0, s >>= s, s >>= 0, s >>>= s, s >>>= 0, s &= s, s &= 0, s ^= s, s ^= 0, s |= s, s |= 0, str += s || str;
s *= 0, s /= s, s /= 0, s %= s, s %= 0, str += s = s + 0 + "", s -= s, s -= 0, s <<= s, s <<= 0, s >>= s, s >>= 0, s >>>= s, s >>>= 0, s &= s, s &= 0, s ^= s, s ^= 0, s |= s, s |= 0, str += s || str;
26 changes: 26 additions & 0 deletions crates/swc_ecma_minifier/src/compress/optimize/inline.rs
@@ -1,7 +1,9 @@
use swc_atoms::js_word;
use swc_common::{util::take::Take, EqIgnoreSpan, Spanned};
use swc_ecma_ast::*;
use swc_ecma_transforms_optimization::simplify::expr_simplifier;
use swc_ecma_utils::{class_has_side_effect, find_pat_ids, ExprExt};
use swc_ecma_visit::VisitMutWith;

use super::Optimizer;
use crate::{
Expand Down Expand Up @@ -680,6 +682,30 @@ where

/// Actually inlines variables.
pub(super) fn inline(&mut self, e: &mut Expr) {
if let Expr::Member(me) = e {
if let MemberProp::Computed(ref mut prop) = me.prop {
if let Expr::Lit(Lit::Num(..)) = &*prop.expr {
if let Expr::Ident(obj) = &*me.obj {
let new = self.vars.lits_for_array_access.get(&obj.to_id());

if let Some(new) = new {
report_change!("inline: Inlined array access");
self.changed = true;

me.obj = new.clone();
// TODO(kdy1): Optimize performance by skipping visiting of children
// nodes.
e.visit_mut_with(&mut expr_simplifier(
self.marks.unresolved_mark,
Default::default(),
));
}
return;
}
}
}
}

if let Expr::Ident(i) = e {
let id = i.to_id();
if let Some(value) = self
Expand Down
92 changes: 70 additions & 22 deletions crates/swc_ecma_minifier/src/compress/optimize/sequences.rs
Expand Up @@ -1665,6 +1665,20 @@ where
};

if !self.is_skippable_for_seq(Some(a), &Expr::Ident(b_left.clone())) {
// Let's be safe
if IdentUsageFinder::find(&b_left.to_id(), &b_assign.right) {
return Ok(false);
}

// As we are not *skipping* lhs, we can inline here
if let Some(a_id) = a.id() {
if a_id == b_left.to_id() {
if self.replace_seq_assignment(a, b)? {
return Ok(true);
}
}
}

return Ok(false);
}

Expand Down Expand Up @@ -2202,7 +2216,7 @@ where
}

macro_rules! take_a {
($force_drop:expr) => {
($force_drop:expr, $drop_op:expr) => {
match a {
Mergable::Var(a) => {
if self.options.unused {
Expand All @@ -2224,15 +2238,17 @@ where
.unwrap_or_else(|| undefined(DUMMY_SP))
}
Mergable::Expr(a) => {
if can_remove {
if can_remove || $force_drop {
if let Expr::Assign(e) = a {
report_change!(
"sequences: Dropping assignment as we are going to drop the \
variable declaration. ({})",
left_id
);

**a = *e.right.take();
if e.op == op!("=") || $drop_op {
report_change!(
"sequences: Dropping assignment as we are going to drop \
the variable declaration. ({})",
left_id
);

**a = *e.right.take();
}
}
}

Expand All @@ -2256,7 +2272,10 @@ where
Expr::Assign(b @ AssignExpr { op: op!("="), .. }) => {
if let Some(b_left) = b.left.as_ident() {
if b_left.to_id() == left_id.to_id() {
let mut a_expr = take_a!(true);
report_change!("sequences: Merged assignment into another assignment");
self.changed = true;

let mut a_expr = take_a!(true, false);
let a_expr = self.ignore_return_value(&mut a_expr);

if let Some(a) = a_expr {
Expand All @@ -2271,19 +2290,35 @@ where
}
Expr::Assign(b) => {
if let Some(b_left) = b.left.as_ident() {
if b_left.to_id() == left_id.to_id() {
if let Some(bin_op) = b.op.to_update() {
b.op = op!("=");
let a_op = match a {
Mergable::Var(_) => Some(op!("=")),
Mergable::Expr(Expr::Assign(AssignExpr { op: a_op, .. })) => Some(*a_op),
Mergable::FnDecl(_) => Some(op!("=")),
_ => None,
};

let to = take_a!(true);
if let Some(a_op) = a_op {
if can_drop_op_for(a_op, b.op) {
if b_left.to_id() == left_id.to_id() {
if let Some(bin_op) = b.op.to_update() {
report_change!(
"sequences: Merged assignment into another (op) assignment"
);
self.changed = true;

b.right = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
op: bin_op,
left: to,
right: b.right.take(),
}));
return Ok(true);
b.op = op!("=");

let to = take_a!(true, true);

b.right = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
op: bin_op,
left: to,
right: b.right.take(),
}));
return Ok(true);
}
}
}
}
}
Expand Down Expand Up @@ -2319,7 +2354,7 @@ where
left_id.span.ctxt
);

let to = take_a!(false);
let to = take_a!(false, false);

replace_id_with_expr(b, left_id.to_id(), to);

Expand Down Expand Up @@ -2449,3 +2484,16 @@ pub(crate) fn is_trivial_lit(e: &Expr) -> bool {
_ => false,
}
}

/// This assumes `a.left.to_id() == b.left.to_id()`
fn can_drop_op_for(a: AssignOp, b: AssignOp) -> bool {
if a == op!("=") {
return true;
}

if a == b {
return matches!(a, op!("+=") | op!("*="));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just looking for associative property? We should be able to merge /=, etc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried -= but (number = BigInt(number)) > max && (number -= max, number -= max, number -= BigInt(2)); became (number = BigInt(number)) > max && (number = max - max - BigInt(2));. Maybe we can still optimize it by

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This first one should be prefixed with - so I just excluded it

}

false
}
12 changes: 6 additions & 6 deletions crates/swc_ecma_minifier/tests/benches-full/echarts.js
Expand Up @@ -5298,7 +5298,7 @@
var width = data[i++], height = data[i++];
if (x1 = x0 + width, y1 = y0 + height, isStroke) {
if (containStroke(x0, y0, x1, y0, lineWidth, x, y) || containStroke(x1, y0, x1, y1, lineWidth, x, y) || containStroke(x1, y1, x0, y1, lineWidth, x, y) || containStroke(x0, y1, x0, y0, lineWidth, x, y)) return !0;
} else w += windingLine(x1, y0, x1, y1, x, y), w += windingLine(x0, y1, x0, y0, x, y);
} else w = windingLine(x1, y0, x1, y1, x, y) + windingLine(x0, y1, x0, y0, x, y);
break;
case CMD$1.Z:
if (isStroke) {
Expand Down Expand Up @@ -12349,7 +12349,7 @@
function decodePolygon(coordinate, encodeOffsets, encodeScale) {
for(var result = [], prevX = encodeOffsets[0], prevY = encodeOffsets[1], i = 0; i < coordinate.length; i += 2){
var x = coordinate.charCodeAt(i) - 64, y = coordinate.charCodeAt(i + 1) - 64;
x = x >> 1 ^ -(1 & x), y = y >> 1 ^ -(1 & y), x += prevX, y += prevY, prevX = x, prevY = y, result.push([
y = y >> 1 ^ -(1 & y), x = (x >> 1 ^ -(1 & x)) + prevX, y += prevY, prevX = x, prevY = y, result.push([
x / encodeScale,
y / encodeScale
]);
Expand Down Expand Up @@ -15423,17 +15423,17 @@
case 'half-year':
case 'quarter':
case 'month':
approxInterval1 = approxInterval, interval = (approxInterval1 /= 2592000000) > 6 ? 6 : approxInterval1 > 3 ? 3 : approxInterval1 > 2 ? 2 : 1, getterName = monthGetterName(isUTC), setterName = monthSetterName(isUTC);
interval = (approxInterval1 = approxInterval / 2592000000) > 6 ? 6 : approxInterval1 > 3 ? 3 : approxInterval1 > 2 ? 2 : 1, getterName = monthGetterName(isUTC), setterName = monthSetterName(isUTC);
break;
case 'week':
case 'half-week':
case 'day':
approxInterval2 = approxInterval, interval = (approxInterval2 /= 86400000) > 16 ? 16 : approxInterval2 > 7.5 ? 7 : approxInterval2 > 3.5 ? 4 : approxInterval2 > 1.5 ? 2 : 1, getterName = dateGetterName(isUTC), setterName = dateSetterName(isUTC);
interval = (approxInterval2 = approxInterval / 86400000) > 16 ? 16 : approxInterval2 > 7.5 ? 7 : approxInterval2 > 3.5 ? 4 : approxInterval2 > 1.5 ? 2 : 1, getterName = dateGetterName(isUTC), setterName = dateSetterName(isUTC);
break;
case 'half-day':
case 'quarter-day':
case 'hour':
approxInterval3 = approxInterval, interval = (approxInterval3 /= 3600000) > 12 ? 12 : approxInterval3 > 6 ? 6 : approxInterval3 > 3.5 ? 4 : approxInterval3 > 2 ? 2 : 1, getterName = hoursGetterName(isUTC), setterName = hoursSetterName(isUTC);
interval = (approxInterval3 = approxInterval / 3600000) > 12 ? 12 : approxInterval3 > 6 ? 6 : approxInterval3 > 3.5 ? 4 : approxInterval3 > 2 ? 2 : 1, getterName = hoursGetterName(isUTC), setterName = hoursSetterName(isUTC);
break;
case 'minute':
interval = getMinutesAndSecondsInterval(approxInterval, !0), getterName = minutesGetterName(isUTC), setterName = minutesSetterName(isUTC);
Expand Down Expand Up @@ -34777,7 +34777,7 @@
var blockMetaList = result1.meta, buttonContainer = document.createElement('div');
buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;';
var buttonStyle = "float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px", closeButton = document.createElement('div'), refreshButton = document.createElement('div');
buttonStyle += ';background-color:' + model.get('buttonColor'), buttonStyle += ';color:' + model.get('buttonTextColor');
buttonStyle = ';background-color:' + model.get('buttonColor') + ';color:' + model.get('buttonTextColor');
var self1 = this;
function close() {
container.removeChild(root), self1._dom = null;
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/tests/benches-full/moment.js
Expand Up @@ -799,7 +799,7 @@
getParsingFlags(config).invalidFormat = !0, config._d = new Date(NaN);
return;
}
for(i = 0; i < config._f.length; i++)currentScore = 0, validFormatFound = !1, tempConfig = copyConfig({}, config), null != config._useUTC && (tempConfig._useUTC = config._useUTC), tempConfig._f = config._f[i], configFromStringAndFormat(tempConfig), isValid(tempConfig) && (validFormatFound = !0), currentScore += getParsingFlags(tempConfig).charsLeftOver, currentScore += 10 * getParsingFlags(tempConfig).unusedTokens.length, getParsingFlags(tempConfig).score = currentScore, bestFormatIsValid ? currentScore < scoreToBeat && (scoreToBeat = currentScore, bestMoment = tempConfig) : (null == scoreToBeat || currentScore < scoreToBeat || validFormatFound) && (scoreToBeat = currentScore, bestMoment = tempConfig, validFormatFound && (bestFormatIsValid = !0));
for(i = 0; i < config._f.length; i++)currentScore = 0, validFormatFound = !1, tempConfig = copyConfig({}, config), null != config._useUTC && (tempConfig._useUTC = config._useUTC), tempConfig._f = config._f[i], configFromStringAndFormat(tempConfig), isValid(tempConfig) && (validFormatFound = !0), currentScore = getParsingFlags(tempConfig).charsLeftOver + 10 * getParsingFlags(tempConfig).unusedTokens.length, getParsingFlags(tempConfig).score = currentScore, bestFormatIsValid ? currentScore < scoreToBeat && (scoreToBeat = currentScore, bestMoment = tempConfig) : (null == scoreToBeat || currentScore < scoreToBeat || validFormatFound) && (scoreToBeat = currentScore, bestMoment = tempConfig, validFormatFound && (bestFormatIsValid = !0));
extend(config, bestMoment || tempConfig);
}(config) : format ? configFromStringAndFormat(config) : isUndefined(input = (config1 = config)._i) ? config1._d = new Date(hooks.now()) : isDate(input) ? config1._d = new Date(input.valueOf()) : 'string' == typeof input ? function(config) {
var matched = aspNetJsonRegex.exec(config._i);
Expand Down
4 changes: 2 additions & 2 deletions crates/swc_ecma_minifier/tests/benches-full/terser.js
Expand Up @@ -1912,8 +1912,8 @@
base && base.PROPS && (props = props.concat(base.PROPS));
for(var code = "return function AST_" + type + "(props){ if (props) { ", i = props.length; --i >= 0;)code += "this." + props[i] + " = props." + props[i] + ";";
const proto = base && Object.create(base.prototype);
(proto && proto.initialize || methods && methods.initialize) && (code += "this.initialize();"), code += "}", code += "this.flags = 0;";
var ctor = Function(code += "}")();
(proto && proto.initialize || methods && methods.initialize) && (code += "this.initialize();");
var ctor = Function(code = "}this.flags = 0;}")();
if (proto && (ctor.prototype = proto, ctor.BASE = base), base && base.SUBCLASSES.push(ctor), ctor.prototype.CTOR = ctor, ctor.prototype.constructor = ctor, ctor.PROPS = props || null, ctor.SELF_PROPS = self_props, ctor.SUBCLASSES = [], type && (ctor.prototype.TYPE = ctor.TYPE = type), methods) for(i in methods)HOP(methods, i) && ("$" === i[0] ? ctor[i.substr(1)] = methods[i] : ctor.prototype[i] = methods[i]);
return ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method;
Expand Down
Expand Up @@ -6035,7 +6035,7 @@
if (number != number) return "NaN";
if (number <= -1000000000000000000000 || number >= 1e21) return String(number);
if (number < 0 && (sign = "-", number = -number), number > 1e-21) {
if (z = (e = log(number * pow(2, 69, 1)) - 69) < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1), z *= 0x10000000000000, (e = 52 - e) > 0) {
if (z = ((e = log(number * pow(2, 69, 1)) - 69) < 0 ? number * pow(2, -e, 1) : number / pow(2, e, 1)) * 0x10000000000000, (e = 52 - e) > 0) {
for(multiply(data, 0, z), j = fractDigits; j >= 7;)multiply(data, 1e7, 0), j -= 7;
for(multiply(data, pow(10, j, 1), 0), j = e - 1; j >= 23;)divide(data, 8388608), j -= 23;
divide(data, 1 << j), multiply(data, 1, 1), divide(data, 2), result = dataToString(data);
Expand Down Expand Up @@ -16028,7 +16028,7 @@
}, _proto.componentWillReceiveProps = function(nextProps) {
if (this.props.value !== nextProps.value) {
var changedBits, oldValue = this.props.value, newValue = nextProps.value;
(oldValue === newValue ? 0 !== oldValue || 1 / oldValue == 1 / newValue : oldValue != oldValue && newValue != newValue) ? changedBits = 0 : (changedBits = "function" == typeof calculateChangedBits ? calculateChangedBits(oldValue, newValue) : 1073741823, 0 != (changedBits |= 0) && this.emitter.set(nextProps.value, changedBits));
(oldValue === newValue ? 0 !== oldValue || 1 / oldValue == 1 / newValue : oldValue != oldValue && newValue != newValue) ? changedBits = 0 : 0 != (changedBits = ("function" == typeof calculateChangedBits ? calculateChangedBits(oldValue, newValue) : 1073741823) | 0) && this.emitter.set(nextProps.value, changedBits);
}
}, _proto.render = function() {
return this.props.children;
Expand Down
Expand Up @@ -1287,7 +1287,7 @@
d = !0;
}
d || (b1 = "", xa(c, function(c, d) {
b1 += d, b1 += ":", b1 += c, b1 += "\r\n";
b1 = d + ":" + c + "\r\n";
}), c = b1, "string" == typeof a ? null != c && encodeURIComponent(String(c)) : R(a, b, c));
}
function Hd(a, b, c) {
Expand Down