aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/js-optimizer.js32
-rw-r--r--tools/js_optimizer.py31
-rw-r--r--tools/shared.py40
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js10
-rw-r--r--tools/test-js-optimizer-asm-pre.js12
-rw-r--r--tools/test-js-optimizer-asm-relocate-output.js8
-rw-r--r--tools/test-js-optimizer-asm-relocate.js10
7 files changed, 107 insertions, 36 deletions
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index d04807a7..1de06e4c 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -405,13 +405,16 @@ function removeUnneededLabelSettings(ast) {
// Various expression simplifications. Pre run before closure (where we still have metadata), Post run after.
var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^');
+var COMPARE_OPS = set('<', '<=', '>', '>=', '==', '===', '!=');
function simplifyExpressionsPre(ast) {
- // Look for (x&A)<<B>>B and replace it with X&A if possible.
- function simplifySignExtends(ast) {
+ // Simplify common expressions used to perform integer conversion operations
+ // in cases where no conversion is needed.
+ function simplifyIntegerConversions(ast) {
traverse(ast, function(node, type) {
if (type === 'binary' && node[1] === '>>' && node[3][0] === 'num' &&
node[2][0] === 'binary' && node[2][1] === '<<' && node[2][3][0] === 'num' && node[3][1] === node[2][3][1]) {
+ // Transform (x&A)<<B>>B to X&A.
var innerNode = node[2][2];
var shifts = node[3][1];
if (innerNode[0] === 'binary' && innerNode[1] === '&' && innerNode[3][0] === 'num') {
@@ -420,6 +423,15 @@ function simplifyExpressionsPre(ast) {
return innerNode;
}
}
+ } else if (type === 'binary' && node[1] === '&' && node[3][0] === 'num') {
+ // Rewrite (X < Y) & 1 to (X < Y)|0. (Subsequent passes will eliminate
+ // the |0 if possible.)
+ var input = node[2];
+ var amount = node[3][1];
+ if (input[0] === 'binary' && (input[1] in COMPARE_OPS) && amount == 1) {
+ node[1] = '|';
+ node[3][1] = 0;
+ }
}
});
}
@@ -763,7 +775,7 @@ function simplifyExpressionsPre(ast) {
}
traverseGeneratedFunctions(ast, function(func) {
- simplifySignExtends(func);
+ simplifyIntegerConversions(func);
simplifyBitops(func);
joinAdditions(func);
// simplifyZeroComp(func); TODO: investigate performance
@@ -2730,8 +2742,9 @@ function relocate(ast) {
assert(asm); // we also assume we are normalized
var replacements = extraInfo.replacements;
- var fBase = extraInfo.fBase;
+ var fBases = extraInfo.fBases;
var hBase = extraInfo.hBase;
+ var m;
traverse(ast, function(node, type) {
switch(type) {
@@ -2743,13 +2756,14 @@ function relocate(ast) {
case 'binary': {
if (node[1] == '+' && node[2][0] == 'name') {
var base = null;
- if (node[2][1] == 'F_BASE') {
- base = fBase;
- } else if (node[2][1] == 'H_BASE') {
+ if (node[2][1] == 'H_BASE') {
base = hBase;
+ } else if (m = /^F_BASE_(\w+)$/.exec(node[2][1])) {
+ base = fBases[m[1]] || 0; // 0 if the parent has no function table for this, but the child does. so no relocation needed
}
- if (base) {
+ if (base !== null) {
var other = node[3];
+ if (base === 0) return other;
if (other[0] == 'num') {
other[1] += base;
return other;
@@ -2887,7 +2901,7 @@ arguments_.slice(1).forEach(function(arg) {
passes[arg](ast);
});
if (asm && last) {
- asmLoopOptimizer(ast);
+ asmLoopOptimizer(ast); // TODO: move out of last, to make last faster when done later (as in side modules)
prepDotZero(ast);
}
var js = astToSrc(ast, minifyWhitespace), old;
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index 905ae835..a4e1ca6c 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -20,6 +20,7 @@ WINDOWS = sys.platform.startswith('win')
DEBUG = os.environ.get('EMCC_DEBUG')
func_sig = re.compile('( *)function ([_\w$]+)\(')
+import_sig = re.compile('var ([_\w$]+) *=[^;]+;')
class Minifier:
'''
@@ -95,6 +96,11 @@ class Minifier:
'globals': self.globs
})
+start_funcs_marker = '// EMSCRIPTEN_START_FUNCS\n'
+end_funcs_marker = '// EMSCRIPTEN_END_FUNCS\n'
+start_asm_marker = '// EMSCRIPTEN_START_ASM\n'
+end_asm_marker = '// EMSCRIPTEN_END_ASM\n'
+
def run_on_chunk(command):
filename = command[2] # XXX hackish
#print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read()
@@ -107,7 +113,7 @@ def run_on_chunk(command):
if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console.
return filename
-def run_on_js(filename, passes, js_engine, jcache, source_map=False):
+def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=None):
if isinstance(jcache, bool) and jcache: jcache = shared.JCache
if jcache: shared.JCache.ensure()
@@ -129,17 +135,14 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False):
generated = set(eval(suffix[len(suffix_marker)+1:]))
# Find markers
- start_funcs_marker = '// EMSCRIPTEN_START_FUNCS\n'
- end_funcs_marker = '// EMSCRIPTEN_END_FUNCS\n'
start_funcs = js.find(start_funcs_marker)
end_funcs = js.rfind(end_funcs_marker)
- #assert (start_funcs >= 0) == (end_funcs >= 0) == (not not suffix)
+
+ know_generated = suffix or start_funcs >= 0
minify_globals = 'registerizeAndMinify' in passes and 'asm' in passes
if minify_globals:
passes = map(lambda p: p if p != 'registerizeAndMinify' else 'registerize', passes)
- start_asm_marker = '// EMSCRIPTEN_START_ASM\n'
- end_asm_marker = '// EMSCRIPTEN_END_ASM\n'
start_asm = js.find(start_asm_marker)
end_asm = js.rfind(end_asm_marker)
assert (start_asm >= 0) == (end_asm >= 0)
@@ -148,14 +151,14 @@ def run_on_js(filename, passes, js_engine, jcache, source_map=False):
if closure:
passes = filter(lambda p: p != 'closure', passes) # we will do it manually
- if not suffix and jcache:
+ if not know_generated and jcache:
# JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered
# This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure
# anyhow (since closure is likely the longest part of the build).
if DEBUG: print >>sys.stderr, 'js optimizer: no metadata, so disabling jcache'
jcache = False
- if suffix:
+ if know_generated:
if not minify_globals:
pre = js[:start_funcs + len(start_funcs_marker)]
post = js[end_funcs + len(end_funcs_marker):]
@@ -189,7 +192,7 @@ EMSCRIPTEN_FUNCS();
minify_info = minifier.serialize()
#if DEBUG: print >> sys.stderr, 'minify info:', minify_info
# remove suffix if no longer needed
- if 'last' in passes:
+ if suffix and 'last' in passes:
suffix_start = post.find(suffix_marker)
suffix_end = post.find('\n', suffix_start)
post = post[:suffix_start] + post[suffix_end:]
@@ -209,7 +212,7 @@ EMSCRIPTEN_FUNCS();
if m:
ident = m.group(2)
else:
- if suffix: continue # ignore whitespace
+ if know_generated: continue # ignore whitespace
ident = 'anon_%d' % i
assert ident
funcs.append((ident, func))
@@ -249,8 +252,12 @@ EMSCRIPTEN_FUNCS();
f.write(chunk)
f.write(suffix_marker)
if minify_globals:
+ assert not extra_info
f.write('\n')
f.write('// EXTRA_INFO:' + minify_info)
+ elif extra_info:
+ f.write('\n')
+ f.write('// EXTRA_INFO:' + json.dumps(extra_info))
f.close()
return temp_file
filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))]
@@ -329,6 +336,6 @@ EMSCRIPTEN_FUNCS();
return filename
-def run(filename, passes, js_engine, jcache, source_map=False):
- return temp_files.run_and_clean(lambda: run_on_js(filename, passes, js_engine, jcache, source_map))
+def run(filename, passes, js_engine, jcache, source_map=False, extra_info=None):
+ return temp_files.run_and_clean(lambda: run_on_js(filename, passes, js_engine, jcache, source_map, extra_info))
diff --git a/tools/shared.py b/tools/shared.py
index 776001cd..d35924c6 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -205,7 +205,7 @@ else:
config_file = '\n'.join(config_file)
# autodetect some default paths
config_file = config_file.replace('{{{ EMSCRIPTEN_ROOT }}}', __rootpath__)
- llvm_root = find_executable('llvm-dis') or '/usr/bin'
+ llvm_root = os.path.dirname(find_executable('llvm-dis') or '/usr/bin/llvm-dis')
config_file = config_file.replace('{{{ LLVM_ROOT }}}', llvm_root)
node = find_executable('node') or find_executable('nodejs') or 'node'
config_file = config_file.replace('{{{ NODE }}}', node)
@@ -400,6 +400,7 @@ EMAR = path_from_root('emar')
EMRANLIB = path_from_root('emranlib')
EMLIBTOOL = path_from_root('emlibtool')
EMCONFIG = path_from_root('em-config')
+EMLINK = path_from_root('emlink.py')
EMMAKEN = path_from_root('tools', 'emmaken.py')
AUTODEBUGGER = path_from_root('tools', 'autodebugger.py')
BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py')
@@ -866,7 +867,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
return generated_libs
@staticmethod
- def link(files, target):
+ def link(files, target, force_archive_contents=False):
actual_files = []
unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol)
resolved_symbols = set()
@@ -917,7 +918,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld)
#print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs
#print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n'
- if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1:
+ if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1 or force_archive_contents:
if Building.is_bitcode(content):
#print >> sys.stderr, ' adding object', content, '\n'
resolved_symbols = resolved_symbols.union(new_symbols.defs)
@@ -1100,6 +1101,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
@staticmethod
def get_safe_internalize():
+ if not Building.can_build_standalone(): return [] # do not internalize anything
exps = expand_response(Settings.EXPORTED_FUNCTIONS)
if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS
exports = ','.join(map(lambda exp: exp[1:], exps))
@@ -1209,8 +1211,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
return opts
@staticmethod
- def js_optimizer(filename, passes, jcache, debug):
- return js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug)
+ def js_optimizer(filename, passes, jcache=False, debug=False, extra_info=None):
+ return js_optimizer.run(filename, passes, listify(NODE_JS), jcache, debug, extra_info)
@staticmethod
def closure_compiler(filename, pretty=True):
@@ -1350,9 +1352,35 @@ JCache = cache.JCache(Cache)
chunkify = cache.chunkify
class JS:
+ memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)'
+ no_memory_initializer_pattern = '/\* no memory initializer \*/'
+
+ memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
+
+ global_initializers_pattern = '/\* global initializers \*/ __ATINIT__.push\((.+)\);'
+
@staticmethod
def to_nice_ident(ident): # limited version of the JS function toNiceIdent
- return ident.replace('%', '$').replace('@', '_');
+ return ident.replace('%', '$').replace('@', '_')
+
+ @staticmethod
+ def make_invoke(sig, named=True):
+ args = ','.join(['a' + str(i) for i in range(1, len(sig))])
+ args = 'index' + (',' if args else '') + args
+ # C++ exceptions are numbers, and longjmp is a string 'longjmp'
+ return '''function%s(%s) {
+ try {
+ %sModule["dynCall_%s"](%s);
+ } catch(e) {
+ if (typeof e !== 'number' && e !== 'longjmp') throw e;
+ asm["setThrew"](1, 0);
+ }
+}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+
+ @staticmethod
+ def align(x, by):
+ while x % by != 0: x += 1
+ return x
# Compression of code and data for smaller downloads
class Compression:
diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js
index 301a2ec8..59a42010 100644
--- a/tools/test-js-optimizer-asm-pre-output.js
+++ b/tools/test-js-optimizer-asm-pre-output.js
@@ -119,6 +119,16 @@ function sign_extension_simplification() {
print(5);
}
}
+function compare_result_simplification() {
+ HEAP32[$4] = HEAP32[$5] < HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] > HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] <= HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] <= HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] == HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] === HEAP32[$6];
+ HEAP32[$4] = HEAP32[$5] != HEAP32[$6];
+ var x = HEAP32[$5] != HEAP32[$6] | 0;
+}
function tempDoublePtr($45, $14, $28, $42) {
$45 = $45 | 0;
$14 = $14 | 0;
diff --git a/tools/test-js-optimizer-asm-pre.js b/tools/test-js-optimizer-asm-pre.js
index c7c92124..7ca941fa 100644
--- a/tools/test-js-optimizer-asm-pre.js
+++ b/tools/test-js-optimizer-asm-pre.js
@@ -122,6 +122,18 @@ function sign_extension_simplification() {
print(5);
}
}
+function compare_result_simplification() {
+ // Eliminate these '&1's.
+ HEAP32[$4] = (HEAP32[$5] < HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] > HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] <= HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] <= HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] == HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] === HEAP32[$6]) & 1;
+ HEAP32[$4] = (HEAP32[$5] != HEAP32[$6]) & 1;
+ // Convert the &1 to |0 here, since we don't get an implicit coersion.
+ var x = (HEAP32[$5] != HEAP32[$6]) & 1;
+}
function tempDoublePtr($45, $14, $28, $42) {
$45 = $45 | 0;
$14 = $14 | 0;
diff --git a/tools/test-js-optimizer-asm-relocate-output.js b/tools/test-js-optimizer-asm-relocate-output.js
index 6a197e81..2f8294c5 100644
--- a/tools/test-js-optimizer-asm-relocate-output.js
+++ b/tools/test-js-optimizer-asm-relocate-output.js
@@ -1,9 +1,9 @@
function leaveMeAlone(c) {}
function fixed(a, b) {}
function a(x, y) {
- fixed(34, 12);
- fixed(34 | 0, 12 | 0);
- leaveMeAlone(10 + x, 33 + y);
- leaveMeAlone(10 + x | 0, 33 + y | 0);
+ fixed(34, 4);
+ fixed(34 | 0, 102 | 0);
+ leaveMeAlone(2 + x, 33 + y);
+ leaveMeAlone(x | 0, 33 + y | 0);
}
diff --git a/tools/test-js-optimizer-asm-relocate.js b/tools/test-js-optimizer-asm-relocate.js
index a45bc2f0..5402c9f6 100644
--- a/tools/test-js-optimizer-asm-relocate.js
+++ b/tools/test-js-optimizer-asm-relocate.js
@@ -3,10 +3,10 @@ function leaveMeAlone(c) {
function replaceMe(a, b) {
}
function a(x, y) {
- replaceMe(H_BASE + 1, F_BASE + 2);
- replaceMe(H_BASE + 1 | 0, F_BASE + 2 | 0);
- leaveMeAlone(F_BASE + x, H_BASE + y);
- leaveMeAlone(F_BASE + x | 0, H_BASE + y | 0);
+ replaceMe(H_BASE + 1, F_BASE_vii + 2);
+ replaceMe(H_BASE + 1 | 0, F_BASE_vi + 2 | 0);
+ leaveMeAlone(F_BASE_vii + x, H_BASE + y);
+ leaveMeAlone(F_BASE_vUNKNOWN + x | 0, H_BASE + y | 0);
}
// EMSCRIPTEN_GENERATED_FUNCTIONS
-// EXTRA_INFO: { "replacements": { "replaceMe": "fixed" }, "hBase": 33, "fBase": 10 }
+// EXTRA_INFO: { "replacements": { "replaceMe": "fixed" }, "hBase": 33, "fBases": { "vii": 2, "vi": 100, "v": 20 } }