drop unused assignment based on reduce_vars

This commit is contained in:
alexlamsl 2018-01-03 17:53:17 +08:00
parent 14778e049b
commit 1467126f2a
4 changed files with 141 additions and 15 deletions

View File

@ -311,6 +311,7 @@ merge(Compressor.prototype, {
def(AST_Node, noop); def(AST_Node, noop);
function reset_def(compressor, def) { function reset_def(compressor, def) {
def.assignments = 0;
def.direct_access = false; def.direct_access = false;
def.escaped = false; def.escaped = false;
if (def.scope.uses_eval || def.scope.uses_with) { if (def.scope.uses_eval || def.scope.uses_with) {
@ -364,6 +365,7 @@ merge(Compressor.prototype, {
} }
function safe_to_assign(tw, def, value) { function safe_to_assign(tw, def, value) {
if (def.fixed === undefined) return true;
if (def.fixed === null && def.safe_ids) { if (def.fixed === null && def.safe_ids) {
def.safe_ids[def.id] = false; def.safe_ids[def.id] = false;
delete def.safe_ids; delete def.safe_ids;
@ -372,7 +374,7 @@ merge(Compressor.prototype, {
if (!HOP(tw.safe_ids, def.id)) return false; if (!HOP(tw.safe_ids, def.id)) return false;
if (!safe_to_read(tw, def)) return false; if (!safe_to_read(tw, def)) return false;
if (def.fixed === false) return false; if (def.fixed === false) return false;
if (def.fixed != null && (!value || def.references.length > 0)) return false; if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
return all(def.orig, function(sym) { return all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolDefun return !(sym instanceof AST_SymbolDefun
|| sym instanceof AST_SymbolLambda); || sym instanceof AST_SymbolLambda);
@ -477,6 +479,7 @@ merge(Compressor.prototype, {
var d = node.left.definition(); var d = node.left.definition();
if (safe_to_assign(tw, d, node.right)) { if (safe_to_assign(tw, d, node.right)) {
d.references.push(node.left); d.references.push(node.left);
d.assignments++;
d.fixed = function() { d.fixed = function() {
return node.right; return node.right;
}; };
@ -662,7 +665,7 @@ merge(Compressor.prototype, {
def(AST_VarDef, function(tw, descend) { def(AST_VarDef, function(tw, descend) {
var node = this; var node = this;
var d = node.name.definition(); var d = node.name.definition();
if (d.fixed === undefined || safe_to_assign(tw, d, node.value)) { if (safe_to_assign(tw, d, node.value)) {
if (node.value) { if (node.value) {
d.fixed = function() { d.fixed = function() {
return node.value; return node.value;
@ -2727,6 +2730,7 @@ merge(Compressor.prototype, {
} }
var var_defs_by_id = new Dictionary(); var var_defs_by_id = new Dictionary();
var initializations = new Dictionary(); var initializations = new Dictionary();
var fixed_values = Object.create(null);
// pass 1: find out which symbols are directly used in // pass 1: find out which symbols are directly used in
// this scope (not in nested scopes). // this scope (not in nested scopes).
var scope = this; var scope = this;
@ -2763,6 +2767,10 @@ merge(Compressor.prototype, {
if (def.value.has_side_effects(compressor)) { if (def.value.has_side_effects(compressor)) {
def.value.walk(tw); def.value.walk(tw);
} }
var fixed = def.name.fixed_value();
if (fixed === def.value) {
fixed_values[node_def.id] = fixed;
}
} }
}); });
return true; return true;
@ -2786,12 +2794,16 @@ merge(Compressor.prototype, {
var parent = tt.parent(); var parent = tt.parent();
if (drop_vars) { if (drop_vars) {
var sym = assign_as_unused(node); var sym = assign_as_unused(node);
if (sym instanceof AST_SymbolRef if (sym instanceof AST_SymbolRef) {
&& !(sym.definition().id in in_use_ids)) { var def = sym.definition();
var in_use = def.id in in_use_ids;
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt)); if (!in_use
} || def.id in fixed_values
return make_node(AST_Number, node, { && fixed_values[def.id] !== node.right) {
return maintain_this_binding(parent, node, node.right.transform(tt));
}
} else if (!in_use) return make_node(AST_Number, node, {
value: 0 value: 0
}); });
} }
@ -2851,13 +2863,16 @@ merge(Compressor.prototype, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name), left: make_node(AST_SymbolRef, def.name, def.name),
right: def.value right: def.value
})); }).transform(tt));
} }
remove(var_defs, def); remove(var_defs, def);
sym.eliminated++; sym.eliminated++;
return; return;
} }
} }
if (def.value && sym.id in fixed_values && fixed_values[sym.id] !== def.value) {
def.value = def.value.drop_side_effect_free(compressor);
}
if (def.value) { if (def.value) {
if (side_effects.length > 0) { if (side_effects.length > 0) {
if (tail.length > 0) { if (tail.length > 0) {
@ -2962,14 +2977,20 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
function scan_ref_scoped(node, descend) { function scan_ref_scoped(node, descend) {
var sym; var node_def, sym = assign_as_unused(node);
if ((sym = assign_as_unused(node)) instanceof AST_SymbolRef if (sym instanceof AST_SymbolRef
&& self.variables.get(sym.name) === sym.definition()) { && self.variables.get(sym.name) === (node_def = sym.definition())) {
if (node instanceof AST_Assign) node.right.walk(tw); if (node instanceof AST_Assign) {
node.right.walk(tw);
var fixed = node.left.fixed_value();
if (fixed === node.right) {
fixed_values[node_def.id] = fixed;
}
}
return true; return true;
} }
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var node_def = node.definition(); node_def = node.definition();
if (!(node_def.id in in_use_ids)) { if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true; in_use_ids[node_def.id] = true;
in_use.push(node_def); in_use.push(node_def);

View File

@ -151,7 +151,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.references = []; node.references = [];
} }
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
defun.def_function(node); defun.def_function(node, defun);
} }
else if (node instanceof AST_SymbolDefun) { else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is // Careful here, the scope where this should be defined is

View File

@ -1522,3 +1522,108 @@ issue_2665: {
} }
expect_stdout: "-1" expect_stdout: "-1"
} }
double_assign_1: {
options = {
passes: 2,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
function f1() {
var a = {};
var a = [];
return a;
}
function f2() {
var a = {};
a = [];
return a;
}
function f3() {
a = {};
var a = [];
return a;
}
function f4(a) {
a = {};
a = [];
return a;
}
function f5(a) {
var a = {};
a = [];
return a;
}
function f6(a) {
a = {};
var a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect: {
function f1() {
return [];
}
function f2() {
var a;
a = [];
return a;
}
function f3() {
return [];
}
function f4(a) {
a = [];
return a;
}
function f5(a) {
a = [];
return a;
}
function f6(a) {
a = [];
return a;
}
console.log(f1(), f2(), f3(), f4(), f5(), f6());
}
expect_stdout: true
}
double_assign_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = {}, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
void 0, a = {}, console.log(a);
var a;
}
}
double_assign_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
expect: {
for (var i = 0; i < 2; i++)
a = void 0, a = { a: a }, console.log(a);
var a;
}
}

View File

@ -299,7 +299,7 @@ unsafe_evaluate_modified: {
console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }()); console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }()); console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }()); console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }()); console.log(function(){ var o; o = {}; console.log(o.p); return o.p; }());
console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }()); console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }());
function inc() { this.p++; } function inc() { this.p++; }
console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }()); console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }());