implement compress option reduce_funcs

- Allows single-use function declarations to be inlined
  as function expressions when permissible.
- Enabled by default.
- Option depends on `reduce_vars` being enabled.
- For speed critical code this option should be disabled.
This commit is contained in:
kzc 2017-11-10 11:12:40 -05:00
parent 94525d859f
commit bfcaa4ad83
11 changed files with 173 additions and 16 deletions

View File

@ -689,6 +689,11 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
Specify `"strict"` to treat `foo.bar` as side-effect-free only when
`foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions
to be inlined as function expressions when permissible.
Enabled by default. Option depends on `reduce_vars` being enabled.
For speed critical code this option should be disabled.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values.

View File

@ -75,6 +75,7 @@ function Compressor(options, false_by_default) {
properties : !false_by_default,
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_funcs : !false_by_default,
reduce_vars : !false_by_default,
sequences : !false_by_default,
side_effects : !false_by_default,
@ -320,7 +321,7 @@ merge(Compressor.prototype, {
value = node.fixed_value();
if (value && ref_once(d)) {
d.single_use = value instanceof AST_Lambda
|| d.scope === node.scope && value.is_constant_expression();
|| d.scope === node.scope && value.is_constant_expression(compressor);
} else {
d.single_use = false;
}
@ -2187,15 +2188,16 @@ merge(Compressor.prototype, {
// determine if expression is constant
(function(def){
function all(list) {
function all(compressor, list) {
for (var i = list.length; --i >= 0;)
if (!list[i].is_constant_expression())
if (!list[i].is_constant_expression(compressor))
return false;
return true;
}
def(AST_Node, return_false);
def(AST_Constant, return_true);
def(AST_Lambda, function(scope){
def(AST_Lambda, function(compressor, scope){
if (!compressor.option("reduce_funcs")) return false;
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
@ -2218,20 +2220,21 @@ merge(Compressor.prototype, {
}));
return result;
});
def(AST_Unary, function(){
return this.expression.is_constant_expression();
def(AST_Unary, function(compressor){
return this.expression.is_constant_expression(compressor);
});
def(AST_Binary, function(){
return this.left.is_constant_expression() && this.right.is_constant_expression();
def(AST_Binary, function(compressor){
return this.left.is_constant_expression(compressor)
&& this.right.is_constant_expression(compressor);
});
def(AST_Array, function(){
return all(this.elements);
def(AST_Array, function(compressor){
return all(compressor, this.elements);
});
def(AST_Object, function(){
return all(this.properties);
def(AST_Object, function(compressor){
return all(compressor, this.properties);
});
def(AST_ObjectProperty, function(){
return this.value.is_constant_expression();
def(AST_ObjectProperty, function(compressor){
return this.value.is_constant_expression(compressor);
});
})(function(node, func){
node.DEFMETHOD("is_constant_expression", func);
@ -3533,7 +3536,7 @@ merge(Compressor.prototype, {
var stat = fn instanceof AST_Function && fn.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat.value;
if (!value || value.is_constant_expression()) {
if (!value || value.is_constant_expression(compressor)) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).optimize(compressor);
}
@ -4260,7 +4263,7 @@ merge(Compressor.prototype, {
if (d.escaped && d.scope !== self.scope || recursive_ref(compressor, d)) {
d.single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
d.single_use = fixed.is_constant_expression(self.scope);
d.single_use = fixed.is_constant_expression(compressor, self.scope);
if (d.single_use == "f") {
var scope = self.scope;
do {

View File

@ -3037,6 +3037,7 @@ issue_2437: {
inline: true,
join_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
sequences: true,

View File

@ -734,6 +734,7 @@ assign_chain: {
issue_1583: {
options = {
keep_fargs: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -1114,6 +1115,7 @@ issue_2105_1: {
collapse_vars: true,
inline: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@ -1156,6 +1158,7 @@ issue_2105_2: {
passes: 3,
properties: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unsafe: true,

View File

@ -663,6 +663,7 @@ call_args: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
}
@ -686,6 +687,7 @@ call_args_drop_param: {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,

View File

@ -154,6 +154,7 @@ function_returning_constant_literal: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -305,6 +306,7 @@ issue_2084: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@ -515,6 +517,7 @@ issue_2428: {
inline: true,
passes: 3,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,

View File

@ -3,6 +3,7 @@ issue_2377_1: {
evaluate: true,
inline: true,
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -35,6 +36,7 @@ issue_2377_2: {
inline: true,
hoist_props: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -66,6 +68,7 @@ issue_2377_3: {
inline: true,
hoist_props: true,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -163,6 +166,7 @@ direct_access_3: {
single_use: {
options = {
hoist_props: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -293,6 +297,7 @@ contains_this_1: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -318,6 +323,7 @@ contains_this_2: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -345,6 +351,7 @@ contains_this_3: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -376,6 +383,7 @@ new_this: {
hoist_props: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,

View File

@ -2,6 +2,7 @@ unary_prefix: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@ -3,6 +3,7 @@ collapse_vars_constants: {
collapse_vars: true,
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -240,6 +241,7 @@ negate_iife_issue_1073: {
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
unused: true,
@ -267,6 +269,7 @@ issue_1288_side_effects: {
evaluate: true,
inline: true,
negate_iife: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@ -299,6 +302,7 @@ inner_var_for_in_1: {
options = {
evaluate: true,
inline: true,
reduce_funcs: true,
reduce_vars: true,
}
input: {
@ -330,6 +334,7 @@ issue_1595_3: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}

View File

@ -833,6 +833,7 @@ lhs_prop_2: {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@ -879,6 +880,7 @@ prop_side_effects_1: {
evaluate: true,
inline: true,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -915,6 +917,7 @@ prop_side_effects_2: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,

View File

@ -1351,6 +1351,7 @@ defun_inline_3: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
unused: true,
@ -1373,6 +1374,7 @@ defun_inline_3: {
defun_call: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -1400,6 +1402,7 @@ defun_call: {
defun_redefine: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -1433,6 +1436,7 @@ defun_redefine: {
func_inline: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -1460,6 +1464,7 @@ func_inline: {
func_modified: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -2041,6 +2046,7 @@ redefine_arguments_2: {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -2079,6 +2085,7 @@ redefine_arguments_3: {
inline: true,
keep_fargs: false,
passes: 3,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -2154,6 +2161,7 @@ redefine_farg_2: {
evaluate: true,
inline: true,
keep_fargs: false,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -2192,6 +2200,7 @@ redefine_farg_3: {
inline: true,
keep_fargs: false,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
@ -2648,6 +2657,7 @@ boolean_binary_assign: {
cond_assign: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -2671,6 +2681,7 @@ cond_assign: {
iife_assign: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -2701,6 +2712,7 @@ iife_assign: {
issue_1850_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: false,
unused: true,
@ -2724,6 +2736,7 @@ issue_1850_1: {
issue_1850_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: "funcs",
unused: true,
@ -2746,6 +2759,7 @@ issue_1850_2: {
issue_1850_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: "vars",
unused: true,
@ -2769,6 +2783,7 @@ issue_1850_3: {
issue_1850_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -2792,6 +2807,7 @@ issue_1850_4: {
issue_1865: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
unsafe: true,
}
@ -2895,6 +2911,7 @@ accessor_2: {
options = {
collapse_vars: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -2920,6 +2937,7 @@ accessor_2: {
for_in_prop: {
options = {
reduce_funcs: true,
reduce_vars: true,
}
input: {
@ -2947,6 +2965,7 @@ obj_var_1: {
options = {
evaluate: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -2976,6 +2995,7 @@ obj_var_2: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3002,6 +3022,7 @@ obj_arg_1: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3033,6 +3054,7 @@ obj_arg_2: {
inline: true,
passes: 2,
properties: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3060,6 +3082,7 @@ func_arg_1: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3084,6 +3107,7 @@ func_arg_2: {
evaluate: true,
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3105,6 +3129,7 @@ func_arg_2: {
regex_loop: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3241,6 +3266,7 @@ const_expr_1: {
const_expr_2: {
options = {
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
@ -3277,6 +3303,7 @@ escaped_prop_1: {
evaluate: true,
inline: true,
pure_getters: "strict",
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3302,6 +3329,7 @@ escaped_prop_1: {
escaped_prop_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3336,6 +3364,7 @@ escaped_prop_2: {
issue_2420_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3378,6 +3407,7 @@ issue_2420_1: {
issue_2420_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3420,6 +3450,7 @@ issue_2420_2: {
issue_2423_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3444,6 +3475,7 @@ issue_2423_1: {
issue_2423_2: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3467,6 +3499,7 @@ issue_2423_2: {
issue_2423_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3485,6 +3518,7 @@ issue_2423_3: {
issue_2423_4: {
options = {
inline: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3505,6 +3539,7 @@ issue_2423_5: {
options = {
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3543,6 +3578,7 @@ issue_2423_6: {
options = {
inline: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
@ -3583,6 +3619,7 @@ issue_2423_6: {
issue_2440_eval_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3613,6 +3650,7 @@ issue_2440_eval_1: {
issue_2440_eval_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3643,6 +3681,7 @@ issue_2440_eval_2: {
issue_2440_with_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3669,6 +3708,7 @@ issue_2440_with_1: {
issue_2440_with_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3695,6 +3735,7 @@ issue_2440_with_2: {
issue_2442: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3709,6 +3750,7 @@ issue_2442: {
recursive_inlining_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3729,6 +3771,7 @@ recursive_inlining_1: {
recursive_inlining_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3751,6 +3794,7 @@ recursive_inlining_2: {
recursive_inlining_3: {
options = {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3788,6 +3832,7 @@ recursive_inlining_3: {
recursive_inlining_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3834,6 +3879,7 @@ recursive_inlining_4: {
recursive_inlining_5: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3887,6 +3933,7 @@ recursive_inlining_5: {
issue_2450_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3910,6 +3957,7 @@ issue_2450_1: {
issue_2450_2: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3932,6 +3980,7 @@ issue_2450_2: {
issue_2450_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
@ -3962,6 +4011,7 @@ issue_2450_3: {
issue_2450_4: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -3995,6 +4045,7 @@ issue_2450_4: {
issue_2450_5: {
options = {
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -4030,6 +4081,7 @@ issue_2450_5: {
issue_2449: {
options = {
passes: 10,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
@ -4061,3 +4113,74 @@ issue_2449: {
}
expect_stdout: "PASS"
}
perf_1: {
options = {
passes: 10,
reduce_funcs: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo(x, y, z) {
return x < y ? x * y + z : x * z - y;
}
function indirect_foo(x, y, z) {
return foo(x, y, z);
}
var sum = 0;
for (var i = 0; i < 1e7; ++i) {
sum += indirect_foo(i, i+1, i*3);
}
console.log(sum);
}
expect: {
function indirect_foo(x, y, z) {
return function(x, y, z) {
return x < y ? x * y + z : x * z - y;
}(x, y, z);
}
var sum = 0;
for (var i = 0; i < 1e7; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "333333483333479600000"
}
perf_2: {
options = {
passes: 10,
reduce_funcs: false,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo(x, y, z) {
return x < y ? x * y + z : x * z - y;
}
function indirect_foo(x, y, z) {
return foo(x, y, z);
}
var sum = 0;
for (var i = 0; i < 1e7; ++i) {
sum += indirect_foo(i, i+1, i*3);
}
console.log(sum);
}
expect: {
function foo(x, y, z) {
return x < y ? x * y + z : x * z - y;
}
function indirect_foo(x, y, z) {
return foo(x, y, z);
}
var sum = 0;
for (var i = 0; i < 1e7; ++i)
sum += indirect_foo(i, i + 1, 3 * i);
console.log(sum);
}
expect_stdout: "333333483333479600000"
}