Skip to content

Commit 7600935

Browse files
committed
Merge pull request #2016 from rfk/rfk/minify-names-separately
Split name-minification into a separate pass from registerization.
2 parents c7cb560 + 375761d commit 7600935

File tree

4 files changed

+105
-95
lines changed

4 files changed

+105
-95
lines changed

emcc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1999,7 +1999,7 @@ try:
19991999
js_optimizer_queue += ['registerize']
20002000

20012001
if opt_level > 0:
2002-
if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue = map(lambda p: p if p != 'registerize' else 'registerizeAndMinify', js_optimizer_queue)
2002+
if debug_level < 2 and shared.Settings.ASM_JS: js_optimizer_queue += ['minifyNames']
20032003
if debug_level == 0: js_optimizer_queue += ['minifyWhitespace']
20042004

20052005
if closure and shared.Settings.ASM_JS:

tests/test_other.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1721,7 +1721,7 @@ def test_js_optimizer(self):
17211721
(path_from_root('tools', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-output.js')).read(),
17221722
['asm', 'registerize']),
17231723
(path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(),
1724-
['asm', 'registerize']),
1724+
['asm', 'registerize', 'minifyLocals']),
17251725
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
17261726
['asm', 'simplifyExpressions']),
17271727
(path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(),

tools/js-optimizer.js

+100-90
Original file line numberDiff line numberDiff line change
@@ -1773,9 +1773,7 @@ function ensureMinifiedNames(n) { // make sure the nth index in minifiedNames ex
17731773
}
17741774
}
17751775

1776-
// Very simple 'registerization', coalescing of variables into a smaller number,
1777-
// as part of minification. Globals-level minification began in a previous pass,
1778-
// we receive extraInfo which tells us how to rename globals. (Only in asm.js.)
1776+
// Very simple 'registerization', coalescing of variables into a smaller number.
17791777
//
17801778
// We do not optimize when there are switches, so this pass only makes sense with
17811779
// relooping.
@@ -1811,6 +1809,7 @@ function registerize(ast) {
18111809
// Replace all var definitions with assignments; we will add var definitions at the top after we registerize
18121810
// We also mark local variables - i.e., having a var definition
18131811
var localVars = {};
1812+
var allVars = {};
18141813
var hasSwitch = false; // we cannot optimize variables if there is a switch, unless in asm mode
18151814
traverse(fun, function(node, type) {
18161815
if (type === 'var') {
@@ -1823,74 +1822,25 @@ function registerize(ast) {
18231822
}
18241823
} else if (type === 'switch') {
18251824
hasSwitch = true;
1825+
} else if (type === 'name') {
1826+
allVars[node[1]] = 1;
18261827
}
18271828
});
18281829
vacuum(fun);
1829-
if (extraInfo && extraInfo.globals) {
1830-
assert(asm);
1831-
var usedGlobals = {};
1832-
var nextLocal = 0;
1833-
// Minify globals using the mapping we were given
1834-
traverse(fun, function(node, type) {
1835-
if (type === 'name') {
1836-
var name = node[1];
1837-
var minified = extraInfo.globals[name];
1838-
if (minified) {
1839-
assert(!localVars[name], name); // locals must not shadow globals, or else we don't know which is which
1840-
if (localVars[minified]) {
1841-
// trying to minify a global into a name used locally. rename all the locals
1842-
var newName = '$_newLocal_' + (nextLocal++);
1843-
assert(!localVars[newName]);
1844-
if (params[minified]) {
1845-
params[newName] = 1;
1846-
delete params[minified];
1847-
}
1848-
localVars[newName] = 1;
1849-
delete localVars[minified];
1850-
asmData.vars[newName] = asmData.vars[minified];
1851-
delete asmData.vars[minified];
1852-
asmData.params[newName] = asmData.params[minified];
1853-
delete asmData.params[minified];
1854-
traverse(fun, function(node, type) {
1855-
if (type === 'name' && node[1] === minified) {
1856-
node[1] = newName;
1857-
}
1858-
});
1859-
if (fun[2]) {
1860-
for (var i = 0; i < fun[2].length; i++) {
1861-
if (fun[2][i] === minified) fun[2][i] = newName;
1862-
}
1863-
}
1864-
}
1865-
node[1] = minified;
1866-
usedGlobals[minified] = 1;
1867-
}
1868-
}
1869-
});
1870-
if (fun[1] in extraInfo.globals) { // if fun was created by a previous optimization pass, it will not be here
1871-
fun[1] = extraInfo.globals[fun[1]];
1872-
assert(fun[1]);
1873-
}
1874-
var nextRegName = 0;
1875-
}
18761830
var regTypes = {};
18771831
function getNewRegName(num, name) {
1878-
if (!asm) return 'r' + num;
1879-
var type = asmData.vars[name];
1880-
if (!extraInfo || !extraInfo.globals) {
1881-
var ret = (type ? 'd' : 'i') + num;
1832+
var ret;
1833+
if (!asm) {
1834+
ret = 'r' + num;
1835+
} else {
1836+
var type = asmData.vars[name];
1837+
ret = (type ? 'd' : 'i') + num;
18821838
regTypes[ret] = type;
1883-
return ret;
18841839
}
1885-
// find the next free minified name that is not used by a global that shows up in this function
1886-
while (1) {
1887-
ensureMinifiedNames(nextRegName);
1888-
var ret = minifiedNames[nextRegName++];
1889-
if (!usedGlobals[ret]) {
1890-
regTypes[ret] = type;
1891-
return ret;
1892-
}
1840+
if (ret in allVars) {
1841+
assert(ret in localVars, 'register shadows non-local name');
18931842
}
1843+
return ret;
18941844
}
18951845
// Find the # of uses of each variable.
18961846
// While doing so, check if all a variable's uses are dominated in a simple
@@ -2111,33 +2061,6 @@ function registerize(ast) {
21112061
}
21122062
}
21132063
denormalizeAsm(fun, finalAsmData);
2114-
if (extraInfo && extraInfo.globals) {
2115-
// minify in asm var definitions, that denormalizeAsm just generated
2116-
function minify(value) {
2117-
if (value && value[0] === 'call' && value[1][0] === 'name') {
2118-
var name = value[1][1];
2119-
var minified = extraInfo.globals[name];
2120-
if (minified) {
2121-
value[1][1] = minified;
2122-
}
2123-
}
2124-
}
2125-
var stats = fun[3];
2126-
for (var i = 0; i < stats.length; i++) {
2127-
var line = stats[i];
2128-
if (i >= fun[2].length && line[0] !== 'var') break; // when we pass the arg and var coercions, break
2129-
if (line[0] === 'stat') {
2130-
assert(line[1][0] === 'assign');
2131-
minify(line[1][3]);
2132-
} else {
2133-
assert(line[0] === 'var');
2134-
var pairs = line[1];
2135-
for (var j = 0; j < pairs.length; j++) {
2136-
minify(pairs[j][1]);
2137-
}
2138-
}
2139-
}
2140-
}
21412064
}
21422065
});
21432066
}
@@ -2913,6 +2836,92 @@ function minifyGlobals(ast) {
29132836
suffix = '// EXTRA_INFO:' + JSON.stringify(minified);
29142837
}
29152838

2839+
2840+
function minifyLocals(ast) {
2841+
assert(asm)
2842+
assert(extraInfo && extraInfo.globals)
2843+
2844+
traverseGeneratedFunctions(ast, function(fun, type) {
2845+
2846+
// Analyse the asmjs to figure out local variable names,
2847+
// but operate on the original source tree so that we don't
2848+
// miss any global names in e.g. variable initializers.
2849+
var asmData = normalizeAsm(fun); denormalizeAsm(fun, asmData);
2850+
var newNames = {};
2851+
var usedNames = {};
2852+
2853+
// Find all the globals that we need to minify using
2854+
// pre-assigned names. Don't actually minify them yet
2855+
// as that might interfere with local variable names.
2856+
function isLocalName(name) {
2857+
return name in asmData.vars || name in asmData.params;
2858+
}
2859+
traverse(fun, function(node, type) {
2860+
if (type === 'name') {
2861+
var name = node[1];
2862+
if (!isLocalName(name)) {
2863+
var minified = extraInfo.globals[name];
2864+
if (minified){
2865+
newNames[name] = minified;
2866+
usedNames[minified] = 1;
2867+
}
2868+
}
2869+
}
2870+
});
2871+
2872+
// Traverse and minify all names.
2873+
// The first time we encounter a local name, we assign it a
2874+
// minified name that's not currently in use. Allocating on
2875+
// demand means they're processed in a predicatable order,
2876+
// which is very handy for testing/debugging purposes.
2877+
var nextMinifiedName = 0;
2878+
function getNextMinifiedName() {
2879+
var minified;
2880+
while (1) {
2881+
ensureMinifiedNames(nextMinifiedName);
2882+
minified = minifiedNames[nextMinifiedName++];
2883+
// TODO: we can probably remove !isLocalName here
2884+
if (!usedNames[minified] && !isLocalName(minified)) {
2885+
return minified;
2886+
}
2887+
}
2888+
}
2889+
if (fun[1] in extraInfo.globals) {
2890+
fun[1] = extraInfo.globals[fun[1]];
2891+
assert(fun[1]);
2892+
}
2893+
if (fun[2]) {
2894+
for (var i = 0; i < fun[2].length; i++) {
2895+
var minified = getNextMinifiedName();
2896+
newNames[fun[2][i]] = minified;
2897+
fun[2][i] = minified;
2898+
}
2899+
}
2900+
traverse(fun[3], function(node, type) {
2901+
if (type === 'name') {
2902+
var name = node[1];
2903+
var minified = newNames[name];
2904+
if (minified) {
2905+
node[1] = minified;
2906+
} else if (isLocalName(name)) {
2907+
minified = getNextMinifiedName();
2908+
newNames[name] = minified;
2909+
node[1] = minified;
2910+
}
2911+
} else if (type === 'var') {
2912+
node[1].forEach(function(defn) {
2913+
var name = defn[0];
2914+
if (!(name in newNames)) {
2915+
newNames[name] = getNextMinifiedName();
2916+
}
2917+
defn[0] = newNames[name];
2918+
});
2919+
}
2920+
});
2921+
2922+
});
2923+
}
2924+
29162925
// Relocation pass for a shared module (for the functions part of the module)
29172926
//
29182927
// 1. Replace function names with alternate names as defined (to avoid colliding with
@@ -3984,6 +3993,7 @@ var passes = {
39843993
eliminateMemSafe: eliminateMemSafe,
39853994
aggressiveVariableElimination: aggressiveVariableElimination,
39863995
minifyGlobals: minifyGlobals,
3996+
minifyLocals: minifyLocals,
39873997
relocate: relocate,
39883998
outline: outline,
39893999
minifyWhitespace: function() { minifyWhitespace = true },

tools/js_optimizer.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class Minifier:
2626
'''
2727
asm.js minification support. We calculate minification of
2828
globals here, then pass that into the parallel js-optimizer.js runners which
29-
during registerize perform minification of locals.
29+
perform minification of locals.
3030
'''
3131

3232
def __init__(self, js, js_engine):
@@ -117,9 +117,9 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=
117117

118118
know_generated = suffix or start_funcs >= 0
119119

120-
minify_globals = 'registerizeAndMinify' in passes and 'asm' in passes
120+
minify_globals = 'minifyNames' in passes and 'asm' in passes
121121
if minify_globals:
122-
passes = map(lambda p: p if p != 'registerizeAndMinify' else 'registerize', passes)
122+
passes = map(lambda p: p if p != 'minifyNames' else 'minifyLocals', passes)
123123
start_asm = js.find(start_asm_marker)
124124
end_asm = js.rfind(end_asm_marker)
125125
assert (start_asm >= 0) == (end_asm >= 0)

0 commit comments

Comments
 (0)