From 919556db48740ba33ecc9fd2656a468bcf2d56c7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 22 Aug 2014 13:07:02 -0700 Subject: [PATCH] add --closure 2 option, to run closure even on the asm.js output --- emcc | 24 +++++++--- emscripten.py | 47 ++++++++++++++----- site/build/text/docs/tools_reference/emcc.txt | 13 +++-- site/source/docs/tools_reference/emcc.rst | 5 +- tests/test_other.py | 13 +++-- 5 files changed, 71 insertions(+), 31 deletions(-) diff --git a/emcc b/emcc index 305d3a28e..65c990608 100755 --- a/emcc +++ b/emcc @@ -920,8 +920,11 @@ try: assert not (shared.Settings.NO_DYNAMIC_EXECUTION and closure), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time' if closure: - shared.Settings.CLOSURE_COMPILER = 1 + shared.Settings.CLOSURE_COMPILER = closure assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER) + if closure == 2 and shared.Settings.ASM_JS == 1: + logging.warning('not all asm.js optimizations are possible with --closure 2, disabling those') + shared.Settings.ASM_JS = 2 assert shared.LLVM_TARGET in shared.COMPILER_OPTS if shared.LLVM_TARGET == 'i386-pc-linux-gnu': @@ -1352,7 +1355,7 @@ try: js_optimizer_extra_info = {} if opt_level >= 1 and js_opts: - logging.debug('running pre-closure post-opts') + logging.debug('running js post-opts') if DEBUG == '2': # Clean up the syntax a bit @@ -1399,28 +1402,37 @@ try: js_optimizer_queue += ['outline'] js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT - if opt_level >= 2 and (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3: + if opt_level >= 2 and (not closure or shared.Settings.ASM_JS or closure == 2) and shared.Settings.RELOOP and debug_level < 3: if shared.Settings.ASM_JS and opt_level >= 3: js_optimizer_queue += ['registerizeHarder'] else: js_optimizer_queue += ['registerize'] if opt_level >= 2: - if debug_level < 2 and shared.Settings.ASM_JS: + if debug_level < 2 and shared.Settings.ASM_JS and not closure == 2: js_optimizer_queue += ['minifyNames'] if emit_symbol_map: js_optimizer_queue += ['symbolMap='+target+'.symbols'] if debug_level == 0: js_optimizer_queue += ['minifyWhitespace'] if shared.Settings.ASM_JS: - if closure: + if closure == 1: js_optimizer_queue += ['closure'] - elif debug_level <= 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE: + elif debug_level <= 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE and not closure: js_optimizer_queue += ['cleanup'] if not shared.Settings.SIDE_MODULE: js_optimizer_queue += ['last'] # side modules are not finalized until after relocation flush_js_optimizer_queue() + if shared.Settings.ASM_JS and closure == 2: + flush_js_optimizer_queue() + + logging.debug('running closure') + # no need to add this to js_transform_tempfiles, because closure and + # debug_level > 0 are never simultaneously true + final = shared.Building.closure_compiler(final, pretty=debug_level >= 1) + if DEBUG: save_intermediate('closure') + log_time('js opts') # Remove some trivial whitespace # TODO: do not run when compress has already been done on all parts of the code diff --git a/emscripten.py b/emscripten.py index 1dc021eb4..bf008b921 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1124,12 +1124,24 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n' basic_funcs.append('extCall_%s' % sig) + def quote(prop): + if settings['CLOSURE_COMPILER'] == 2: + return "'" + prop + "'" + else: + return prop + + def access_quote(prop): + if settings['CLOSURE_COMPILER'] == 2: + return "['" + prop + "']" + else: + return '.' + prop + # calculate exports exported_implemented_functions = list(exported_implemented_functions) + metadata['initializers'] exported_implemented_functions.append('runPostSets') exports = [] for export in exported_implemented_functions + asm_runtime_funcs + function_tables: - exports.append("%s: %s" % (export, export)) + exports.append(quote(export) + ": " + export) exports = '{ ' + ', '.join(exports) + ' }' # calculate globals try: @@ -1141,9 +1153,9 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, global_funcs = list(set([key for key, value in forwarded_json['Functions']['libraryFunctions'].iteritems() if value != 2]).difference(set(global_vars)).difference(implemented_functions)) def math_fix(g): return g if not g.startswith('Math_') else g.split('_')[1] - asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global.' + g + ';\n' for g in maths]) + \ - ''.join([' var ' + g + '=env.' + math_fix(g) + ';\n' for g in basic_funcs + global_funcs]) - asm_global_vars = ''.join([' var ' + g + '=env.' + g + '|0;\n' for g in basic_vars + global_vars]) + asm_global_funcs = ''.join([' var ' + g.replace('.', '_') + '=global' + access_quote(g) + ';\n' for g in maths]) + \ + ''.join([' var ' + g + '=env' + access_quote(math_fix(g)) + ';\n' for g in basic_funcs + global_funcs]) + asm_global_vars = ''.join([' var ' + g + '=env' + access_quote(g) + '|0;\n' for g in basic_vars + global_vars]) # In linkable modules, we need to add some explicit globals for global variables that can be linked and used across modules if settings.get('MAIN_MODULE') or settings.get('SIDE_MODULE'): assert settings.get('TARGET_ASMJS_UNKNOWN_EMSCRIPTEN'), 'TODO: support x86 target when linking modules (needs offset of 4 and not 8 here)' @@ -1167,6 +1179,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, return real_''' + s + '''.apply(null, arguments); }; ''' for s in exported_implemented_functions if s not in ['_malloc', '_free', '_memcpy', '_memset']]) + receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables]) # finalize @@ -1184,15 +1197,23 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None, // EMSCRIPTEN_START_ASM var asm = (function(global, env, buffer) { %s - var HEAP8 = new global.Int8Array(buffer); - var HEAP16 = new global.Int16Array(buffer); - var HEAP32 = new global.Int32Array(buffer); - var HEAPU8 = new global.Uint8Array(buffer); - var HEAPU16 = new global.Uint16Array(buffer); - var HEAPU32 = new global.Uint32Array(buffer); - var HEAPF32 = new global.Float32Array(buffer); - var HEAPF64 = new global.Float64Array(buffer); - ''' % (asm_setup, "'use asm';" if not metadata.get('hasInlineJS') and not settings['SIDE_MODULE'] and settings['ASM_JS'] == 1 else "'almost asm';") + '\n' + asm_global_vars + ''' + var HEAP8 = new global%s(buffer); + var HEAP16 = new global%s(buffer); + var HEAP32 = new global%s(buffer); + var HEAPU8 = new global%s(buffer); + var HEAPU16 = new global%s(buffer); + var HEAPU32 = new global%s(buffer); + var HEAPF32 = new global%s(buffer); + var HEAPF64 = new global%s(buffer); + ''' % (asm_setup, "'use asm';" if not metadata.get('hasInlineJS') and not settings['SIDE_MODULE'] and settings['ASM_JS'] == 1 else "'almost asm';", + access_quote('Int8Array'), + access_quote('Int16Array'), + access_quote('Int32Array'), + access_quote('Uint8Array'), + access_quote('Uint16Array'), + access_quote('Uint32Array'), + access_quote('Float32Array'), + access_quote('Float64Array')) + '\n' + asm_global_vars + ''' var __THREW__ = 0; var threwValue = 0; var setjmpId = 0; diff --git a/site/build/text/docs/tools_reference/emcc.txt b/site/build/text/docs/tools_reference/emcc.txt index 0d181f6ae..70626c0c9 100644 --- a/site/build/text/docs/tools_reference/emcc.txt +++ b/site/build/text/docs/tools_reference/emcc.txt @@ -218,11 +218,16 @@ Options that are modified or new in *emcc* are listed below: * "1": Run closure compiler. This greatly reduces code size and may in some cases increase runtime speed (although the opposite can also occur). Note that it takes time to run, and - may require some changes to the code. + may require some changes to the code. In **asm.js** mode, + closure will only be used on the 'shell' code around the + compiled code (the compiled code will be processed by the + custom **asm.js** minifier). - In **asm.js** mode, closure will only be used on the 'shell' code - around the compiled code (the compiled code will be processed by - the custom **asm.js** minifier). + * "2": Run closure compiler on *all* the emitted code, even on + **asm.js** output in **asm.js** mode. This can further reduce + code size, but does prevent a significant amount of **asm.js** + optimizations, so it is not recommended unless you want to + reduce code size at all costs. Note: * If closure compiler hits an out-of-memory, try adjusting diff --git a/site/source/docs/tools_reference/emcc.rst b/site/source/docs/tools_reference/emcc.rst index 6a44fcb9f..1e6d533c0 100644 --- a/site/source/docs/tools_reference/emcc.rst +++ b/site/source/docs/tools_reference/emcc.rst @@ -158,9 +158,8 @@ Options that are modified or new in *emcc* are listed below: Runs the :term:`Closure Compiler`. Possible ``on`` values are: - ``0``: No closure compiler (default in ``-O2`` and below). - - ``1``: Run closure compiler. This greatly reduces code size and may in some cases increase runtime speed (although the opposite can also occur). Note that it takes time to run, and may require some changes to the code. - - In **asm.js** mode, closure will only be used on the 'shell' code around the compiled code (the compiled code will be processed by the custom **asm.js** minifier). + - ``1``: Run closure compiler. This greatly reduces code size and may in some cases increase runtime speed (although the opposite can also occur). Note that it takes time to run, and may require some changes to the code. In **asm.js** mode, closure will only be used on the 'shell' code around the compiled code (the compiled code will be processed by the custom **asm.js** minifier). + - ``2``: Run closure compiler on *all* the emitted code, even on **asm.js** output in **asm.js** mode. This can further reduce code size, but does prevent a significant amount of **asm.js** optimizations, so it is not recommended unless you want to reduce code size at all costs. .. note:: diff --git a/tests/test_other.py b/tests/test_other.py index 45fd7f2c0..5c2c626d4 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -3978,12 +3978,13 @@ main(const int argc, const char * const * const argv) test(['-O1']) def test_no_nuthin(self): - def test(opts, ratio): + def test(opts, ratio, absolute): print opts def get_size(name): return os.stat(name).st_size sizes = {} def do(name, moar_opts): + self.clear() Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', name + '.js'] + opts + moar_opts).communicate() sizes[name] = get_size(name + '.js') self.assertContained('hello, world!', run_js(name + '.js')) @@ -3997,10 +3998,12 @@ main(const int argc, const char * const * const argv) assert sizes['no_nuthin'] < sizes['no_fs'] assert sizes['no_nuthin'] < sizes['no_browser'] assert sizes['no_nuthin'] < ratio*sizes['normal'] - test([], 0.66) - test(['-O1'], 0.66) - test(['-O2'], 0.50) - test(['-O3', '--closure', '1'], 0.60) + assert sizes['no_nuthin'] < absolute + test([], 0.66, 250000) + test(['-O1'], 0.66, 225000) + test(['-O2'], 0.50, 75000) + test(['-O3', '--closure', '1'], 0.60, 60000) + test(['-O3', '--closure', '2'], 0.60, 40000) # might change now and then def test_stat_fail_alongtheway(self): open('src.cpp', 'w').write(r'''