From b57bbd2600aad1c965beccc395830313ffb97e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Mon, 16 Dec 2019 21:10:13 +0200 Subject: [PATCH 1/8] -O0 Hello world working with MINIMAL_RUNTIME. --- emscripten.py | 97 +++++++++++++++++++++++++++++++--------- src/postamble_minimal.js | 23 +++++++++- tests/test_core.py | 12 ++++- 3 files changed, 106 insertions(+), 26 deletions(-) diff --git a/emscripten.py b/emscripten.py index 6b3e12258fa09..57fdd035f736b 100644 --- a/emscripten.py +++ b/emscripten.py @@ -270,6 +270,25 @@ def get_asm_extern_primitives(pre): return [] +def compute_minimal_runtime_initializer_and_exports(post, initializers, exports, receiving): + # Generate invocations for all global initializers directly off the asm export object, e.g. asm['__GLOBAL__INIT'](); + post = post.replace('/*** RUN_GLOBAL_INITIALIZERS(); ***/', '\n'.join(["asm['" + x + "']();" for x in global_initializer_funcs(initializers)])) + + if shared.Settings.WASM: + # Declare all exports out to global JS scope so that JS library functions can access them in a way that minifies well with Closure + # e.g. var a,b,c,d,e,f; + exports_that_are_not_initializers = [x for x in exports if x not in initializers] + if shared.Settings.WASM_BACKEND: + # In Wasm backend the exports are still unmangled at this point, so mangle the names here + exports_that_are_not_initializers = [asmjs_mangle(x) for x in exports_that_are_not_initializers] + post = post.replace('/*** ASM_MODULE_EXPORTS_DECLARES ***/', 'var ' + ','.join(exports_that_are_not_initializers) + ';') + + # Generate assignments from all asm.js/wasm exports out to the JS variables above: e.g. a = asm['a']; b = asm['b']; + post = post.replace('/*** ASM_MODULE_EXPORTS ***/', receiving) + + return post + + def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, outfile, DEBUG): if DEBUG: logger.debug('emscript: python processing: function tables and exports') @@ -397,17 +416,8 @@ def define_asmjs_import_names(imports): post = apply_static_code_hooks(post) if shared.Settings.MINIMAL_RUNTIME: - # Generate invocations for all global initializers directly off the asm export object, e.g. asm['__GLOBAL__INIT'](); - post = post.replace('/*** RUN_GLOBAL_INITIALIZERS(); ***/', '\n'.join(["asm['" + x + "']();" for x in global_initializer_funcs(metadata['initializers'])])) - - if shared.Settings.WASM: - # Declare all exports out to global JS scope so that JS library functions can access them in a way that minifies well with Closure - # e.g. var a,b,c,d,e,f; - post = post.replace('/*** ASM_MODULE_EXPORTS_DECLARES ***/', 'var ' + ','.join(shared.Settings.MODULE_EXPORTS) + ';') - - # Generate assignments from all asm.js/wasm exports out to the JS variables above: e.g. a = asm['a']; b = asm['b']; - post = post.replace('/*** ASM_MODULE_EXPORTS ***/', receiving) - receiving = '' + post = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], shared.Settings.MODULE_EXPORTS, receiving) + receiving = '' function_tables_impls = make_function_tables_impls(function_table_data) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs @@ -2208,14 +2218,22 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, staticbump = shared.Settings.STATIC_BUMP + if shared.Settings.MINIMAL_RUNTIME: + # In minimal runtime, global initializers are run after the Wasm Module instantiation has finished. + global_initializers = '' + else: + # In regular runtime, global initializers are recorded in an __ATINIT__ array. + global_initializers = '''/* global initializers */ %s __ATINIT__.push(%s); +''' % ('if (!ENVIRONMENT_IS_PTHREAD)' if shared.Settings.USE_PTHREADS else '', + global_initializers) + pre = pre.replace('STATICTOP = STATIC_BASE + 0;', '''STATICTOP = STATIC_BASE + %d; -/* global initializers */ %s __ATINIT__.push(%s); -''' % (staticbump, - 'if (!ENVIRONMENT_IS_PTHREAD)' if shared.Settings.USE_PTHREADS else '', - global_initializers)) +%s +''' % (staticbump, global_initializers)) pre = apply_memory(pre) - pre = apply_static_code_hooks(pre) + pre = apply_static_code_hooks(pre) # In regular runtime, atinits etc. exist in the preamble part + post = apply_static_code_hooks(post) # In MINIMAL_RUNTIME, atinit exists in the postamble part if shared.Settings.RELOCATABLE and not shared.Settings.SIDE_MODULE: pre += 'var gb = GLOBAL_BASE, fb = 0;\n' @@ -2255,6 +2273,10 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, sending = create_sending_wasm(invoke_funcs, forwarded_json, metadata) receiving = create_receiving_wasm(exports) + if shared.Settings.MINIMAL_RUNTIME: + post = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], exports, receiving) + receiving = '' + module = create_module_wasm(sending, receiving, invoke_funcs, metadata) write_output_file(outfile, post, module) @@ -2608,7 +2630,7 @@ def fix_import_name(g): def create_receiving_wasm(exports): receiving = [] - if not shared.Settings.ASSERTIONS: + if shared.Settings.MINIMAL_RUNTIME or not shared.Settings.ASSERTIONS: runtime_assertions = '' else: runtime_assertions = RUNTIME_ASSERTIONS @@ -2623,8 +2645,36 @@ def create_receiving_wasm(exports): ''' % {'mangled': asmjs_mangle(e), 'e': e, 'assertions': runtime_assertions}) if not shared.Settings.SWAPPABLE_ASM_MODULE: - for e in exports: - receiving.append('var %(mangled)s = Module["%(mangled)s"] = asm["%(e)s"];' % {'mangled': asmjs_mangle(e), 'e': e}) + if shared.Settings.DECLARE_ASM_MODULE_EXPORTS: + if shared.Settings.WASM and shared.Settings.MINIMAL_RUNTIME: + # In Wasm exports are assigned inside a function to variables existing in top level JS scope, i.e. + # var _main; + # WebAssembly.instantiate(Module["wasm"], imports).then((function(output) { + # var asm = output.instance.exports; + # _main = asm["_main"]; + receiving += [asmjs_mangle(s) + ' = asm["' + s + '"];' for s in exports] + else: + if shared.Settings.MINIMAL_RUNTIME: + # In asm.js exports can be directly processed at top level, i.e. + # var asm = Module["asm"](asmGlobalArg, asmLibraryArg, buffer); + # var _main = asm["_main"]; + receiving += ['var ' + asmjs_mangle(s) + ' = asm["' + asmjs_mangle(s) + '"];' for s in exports] + else: + receiving += ['var ' + asmjs_mangle(s) + ' = Module["' + asmjs_mangle(s) + '"] = asm["' + s + '"];' for s in exports] + else: + if shared.Settings.target_environment_may_be('node') and shared.Settings.target_environment_may_be('web'): + global_object = '(typeof process !== "undefined" ? global : this)' + elif shared.Settings.target_environment_may_be('node'): + global_object = 'global' + else: + global_object = 'this' + + if shared.Settings.MINIMAL_RUNTIME: + module_assign = '' + else: + module_assign = 'Module[__exportedFunc] = ' + + receiving.append('for(var __exportedFunc in asm) ' + global_object + '[__exportedFunc] = ' + module_assign + 'asm[__exportedFunc];') else: receiving.append('Module["asm"] = asm;') for e in exports: @@ -2650,7 +2700,8 @@ def create_module_wasm(sending, receiving, invoke_funcs, metadata): if shared.Settings.ASYNCIFY and shared.Settings.ASSERTIONS: module.append('Asyncify.instrumentWasmImports(asmLibraryArg);\n') - module.append("var asm = createWasm();\n") + if not shared.Settings.MINIMAL_RUNTIME: + module.append("var asm = createWasm();\n") module.append(receiving) module.append(invoke_wrappers) @@ -2697,8 +2748,10 @@ def load_metadata_wasm(metadata_raw, DEBUG): exit_with_error('unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value - # Initializers call the global var version of the export, so they get the mangled name. - metadata['initializers'] = [asmjs_mangle(i) for i in metadata['initializers']] + if not shared.Settings.MINIMAL_RUNTIME: + # In regular runtime initializers call the global var version of the export, so they get the mangled name. + # In MINIMAL_RUNTIME, the initializers are called directly off the export object for minimal code size. + metadata['initializers'] = [asmjs_mangle(i) for i in metadata['initializers']] if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index d98d7d66de98c..7a884b59e2d2e 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -91,10 +91,29 @@ WebAssembly.instantiate(Module['wasm'], imports).then(function(output) { #if DECLARE_ASM_MODULE_EXPORTS == 0 +#if WASM_BACKEND + // XXX Hack: some function names need to be mangled when exporting them from wasm module, others do not. + // https://github.com/emscripten-core/emscripten/issues/10054 + // Keep in sync with emscripten.py function treat_as_user_function(name). + function asmjs_mangle(x) { + var unmangledSymbols = ['setTempRet0', 'getTempRet0', 'stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', '__growWasmMemory', '__heap_base', '__data_end']; + return x.indexOf('dynCall_') == 0 || unmangledSymbols.indexOf(x) != -1 ? x : '_' + x; + } + +#if ENVIRONMENT_MAY_BE_NODE + for(var i in asm) (typeof process !== "undefined" ? global : this)[asmjs_mangle(i)] = asm[i]; +#else + for(var i in asm) this[asmjs_mangle(i)] = asm[i]; +#endif + +#else + #if ENVIRONMENT_MAY_BE_NODE - for(var i in asm) (typeof process !== "undefined" ? global : this)[i] = Module[i] = asm[i]; + for(var i in asm) (typeof process !== "undefined" ? global : this)[i] = asm[i]; #else - for(var i in asm) this[i] = Module[i] = asm[i]; + for(var i in asm) this[i] = asm[i]; +#endif + #endif #else diff --git a/tests/test_core.py b/tests/test_core.py index 48e53225726c1..cae82db2e52c0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8139,10 +8139,18 @@ def test_minimal_runtime_no_declare_asm_module_exports(self): # Tests that -s MINIMAL_RUNTIME=1 works well in different build modes @no_emterpreter - @no_wasm_backend('MINIMAL_RUNTIME not yet available in Wasm backend') + @no_asan('TODO: ASan with MINIMAL_RUNTIME') def test_minimal_runtime_hello_world(self): - for args in [[], ['-s', 'MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION=1']]: + if self.get_setting('WASM_BACKEND') and not self.get_setting('WASM'): + return self.skipTest('TODO: wasm2js support not yet available with MINIMAL_RUNTIME') + if '-O2' in self.emcc_args or '-O3' in self.emcc_args or '-Os' in self.emcc_args or '-Oz' in self.emcc_args or '-fsanitize=leak' in self.emcc_args: + return self.skipTest('TODO: -O2 and higher with wasm backend') + if '-fsanitize=leak' in self.emcc_args: + return self.skipTest('TODO: LSan with wasm backend') + self.banned_js_engines = [V8_ENGINE, SPIDERMONKEY_ENGINE] # TODO: Support for non-Node.js shells has not yet been added to MINIMAL_RUNTIME + for args in [[], ['-s', 'MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION=1'], ['-s', 'DECLARE_ASM_MODULE_EXPORTS=0']]: self.emcc_args = ['-s', 'MINIMAL_RUNTIME=1'] + args + self.set_setting('MINIMAL_RUNTIME', 1) self.maybe_closure() self.do_run(open(path_from_root('tests', 'small_hello_world.c')).read(), 'hello') From d362e1534b43bf40af0a3b534ce9dd41df4a6862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Mon, 16 Dec 2019 23:43:29 +0200 Subject: [PATCH 2/8] Fix MINIMAL_RUNTIME on fastcomp --- emscripten.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/emscripten.py b/emscripten.py index 57fdd035f736b..d38c1c365b6ae 100644 --- a/emscripten.py +++ b/emscripten.py @@ -285,8 +285,9 @@ def compute_minimal_runtime_initializer_and_exports(post, initializers, exports, # Generate assignments from all asm.js/wasm exports out to the JS variables above: e.g. a = asm['a']; b = asm['b']; post = post.replace('/*** ASM_MODULE_EXPORTS ***/', receiving) + receiving = '' - return post + return post, receiving def function_tables_and_exports(funcs, metadata, mem_init, glue, forwarded_data, outfile, DEBUG): @@ -416,8 +417,7 @@ def define_asmjs_import_names(imports): post = apply_static_code_hooks(post) if shared.Settings.MINIMAL_RUNTIME: - post = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], shared.Settings.MODULE_EXPORTS, receiving) - receiving = '' + post, receiving = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], shared.Settings.MODULE_EXPORTS, receiving) function_tables_impls = make_function_tables_impls(function_table_data) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs @@ -2274,8 +2274,7 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, receiving = create_receiving_wasm(exports) if shared.Settings.MINIMAL_RUNTIME: - post = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], exports, receiving) - receiving = '' + post, receiving = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], exports, receiving) module = create_module_wasm(sending, receiving, invoke_funcs, metadata) From 5f6e9c208bb84660bbf5a774a6b2c794121ac284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 17 Dec 2019 21:46:44 +0200 Subject: [PATCH 3/8] Fix non-MINIMAL_RUNTIME asmjs name mangling when building with DECLARE_ASM_MODULE_EXPORTS=0 --- emscripten.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/emscripten.py b/emscripten.py index d38c1c365b6ae..3b60e91e7030e 100644 --- a/emscripten.py +++ b/emscripten.py @@ -2671,9 +2671,15 @@ def create_receiving_wasm(exports): if shared.Settings.MINIMAL_RUNTIME: module_assign = '' else: - module_assign = 'Module[__exportedFunc] = ' + module_assign = 'Module[asmjs_mangle(__exportedFunc)] = ' - receiving.append('for(var __exportedFunc in asm) ' + global_object + '[__exportedFunc] = ' + module_assign + 'asm[__exportedFunc];') + receiving.append(''' + function asmjs_mangle(x) { + var unmangledSymbols = ['setTempRet0', 'getTempRet0', 'stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', '__growWasmMemory', '__heap_base', '__data_end']; + return x.indexOf('dynCall_') == 0 || unmangledSymbols.indexOf(x) != -1 ? x : '_' + x; + } +''') + receiving.append('for(var __exportedFunc in asm) ' + global_object + '[asmjs_mangle(__exportedFunc)] = ' + module_assign + 'asm[__exportedFunc];') else: receiving.append('Module["asm"] = asm;') for e in exports: From b4b54bc977336b37d416ed5f11cfb8674a1f4875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 18 Dec 2019 19:57:20 +0200 Subject: [PATCH 4/8] Address review. Fix closured build of -s DECLARE_ASM_MODULE_EXPORTS=0 in wasm backend --- emscripten.py | 16 ++++++++-------- src/postamble_minimal.js | 2 +- src/settings_internal.js | 5 +++++ tests/test_core.py | 23 ++++++++++++++++++----- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/emscripten.py b/emscripten.py index 3b60e91e7030e..01be5bec8279a 100644 --- a/emscripten.py +++ b/emscripten.py @@ -2243,6 +2243,10 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, exports = metadata['exports'] + # Store exports for Closure copmiler to be able to track these as globals in + # -s DECLARE_ASM_MODULE_EXPORTS=0 builds. + shared.Settings.MODULE_EXPORTS = [asmjs_mangle(f) for f in exports] + if shared.Settings.ASYNCIFY: exports += ['asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind'] @@ -2654,7 +2658,7 @@ def create_receiving_wasm(exports): receiving += [asmjs_mangle(s) + ' = asm["' + s + '"];' for s in exports] else: if shared.Settings.MINIMAL_RUNTIME: - # In asm.js exports can be directly processed at top level, i.e. + # In wasm2js exports can be directly processed at top level, i.e. # var asm = Module["asm"](asmGlobalArg, asmLibraryArg, buffer); # var _main = asm["_main"]; receiving += ['var ' + asmjs_mangle(s) + ' = asm["' + asmjs_mangle(s) + '"];' for s in exports] @@ -2675,10 +2679,10 @@ def create_receiving_wasm(exports): receiving.append(''' function asmjs_mangle(x) { - var unmangledSymbols = ['setTempRet0', 'getTempRet0', 'stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', '__growWasmMemory', '__heap_base', '__data_end']; + var unmangledSymbols = %s; return x.indexOf('dynCall_') == 0 || unmangledSymbols.indexOf(x) != -1 ? x : '_' + x; } -''') +''' % shared.Settings.WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED) receiving.append('for(var __exportedFunc in asm) ' + global_object + '[asmjs_mangle(__exportedFunc)] = ' + module_assign + 'asm[__exportedFunc];') else: receiving.append('Module["asm"] = asm;') @@ -2782,13 +2786,9 @@ def create_invoke_wrappers(invoke_funcs): def treat_as_user_function(name): - library_functions_in_module = ('setTempRet0', 'getTempRet0', 'stackAlloc', - 'stackSave', 'stackRestore', - 'establishStackSpace', '__growWasmMemory', - '__heap_base', '__data_end') if name.startswith('dynCall_'): return False - if name in library_functions_in_module: + if name in shared.Settings.WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED: return False return True diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index 7a884b59e2d2e..b02d7d92553c2 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -96,7 +96,7 @@ WebAssembly.instantiate(Module['wasm'], imports).then(function(output) { // https://github.com/emscripten-core/emscripten/issues/10054 // Keep in sync with emscripten.py function treat_as_user_function(name). function asmjs_mangle(x) { - var unmangledSymbols = ['setTempRet0', 'getTempRet0', 'stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', '__growWasmMemory', '__heap_base', '__data_end']; + var unmangledSymbols = {{{ WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED }}}; return x.indexOf('dynCall_') == 0 || unmangledSymbols.indexOf(x) != -1 ? x : '_' + x; } diff --git a/src/settings_internal.js b/src/settings_internal.js index 336f71675a699..92f783be1e411 100644 --- a/src/settings_internal.js +++ b/src/settings_internal.js @@ -156,3 +156,8 @@ var MINIFY_ASMJS_IMPORT_NAMES = 0; // Internal: represents a browser version that is not supported at all. var TARGET_NOT_SUPPORTED = 0x7FFFFFFF; + +// Wasm backend does not apply C name mangling (== prefix with an underscore) to +// the following functions. (it also does not mangle any function that starts with +// string "dynCall_") +var WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED = ['setTempRet0', 'getTempRet0', 'stackAlloc', 'stackSave', 'stackRestore', 'establishStackSpace', '__growWasmMemory', '__heap_base', '__data_end']; diff --git a/tests/test_core.py b/tests/test_core.py index cae82db2e52c0..c5358a04dda31 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -216,6 +216,21 @@ def decorated(self, *args, **kwargs): return decorator +def no_lsan(note): + assert not callable(note) + + def decorator(f): + assert callable(f) + + @wraps(f) + def decorated(self, *args, **kwargs): + if '-fsanitize=leak' in self.emcc_args: + self.skipTest(note) + f(self, *args, **kwargs) + return decorated + return decorator + + class TestCoreBase(RunnerCore): def is_wasm2js(self): return self.is_wasm_backend() and not self.get_setting('WASM') @@ -8140,13 +8155,11 @@ def test_minimal_runtime_no_declare_asm_module_exports(self): # Tests that -s MINIMAL_RUNTIME=1 works well in different build modes @no_emterpreter @no_asan('TODO: ASan with MINIMAL_RUNTIME') + @no_lsan('TODO: LSan with MINIMAL_RUNTIME') + @no_wasm2js('TODO: MINIMAL_RUNTIME with WASM2JS') def test_minimal_runtime_hello_world(self): - if self.get_setting('WASM_BACKEND') and not self.get_setting('WASM'): - return self.skipTest('TODO: wasm2js support not yet available with MINIMAL_RUNTIME') - if '-O2' in self.emcc_args or '-O3' in self.emcc_args or '-Os' in self.emcc_args or '-Oz' in self.emcc_args or '-fsanitize=leak' in self.emcc_args: + if '-O2' in self.emcc_args or '-O3' in self.emcc_args or '-Os' in self.emcc_args or '-Oz' in self.emcc_args: return self.skipTest('TODO: -O2 and higher with wasm backend') - if '-fsanitize=leak' in self.emcc_args: - return self.skipTest('TODO: LSan with wasm backend') self.banned_js_engines = [V8_ENGINE, SPIDERMONKEY_ENGINE] # TODO: Support for non-Node.js shells has not yet been added to MINIMAL_RUNTIME for args in [[], ['-s', 'MINIMAL_RUNTIME_STREAMING_WASM_COMPILATION=1'], ['-s', 'DECLARE_ASM_MODULE_EXPORTS=0']]: self.emcc_args = ['-s', 'MINIMAL_RUNTIME=1'] + args From 2bff254ca6ccddfa901b98b9bcdd18f2a7b83def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 18 Dec 2019 21:42:31 +0200 Subject: [PATCH 5/8] Fix metaDCE for DECLARE_ASM_MODULE_EXPORTS=0 --- emscripten.py | 4 ++-- tools/shared.py | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/emscripten.py b/emscripten.py index 01be5bec8279a..94b5533515886 100644 --- a/emscripten.py +++ b/emscripten.py @@ -25,7 +25,7 @@ from tools import gen_struct_info from tools import jsrun from tools.response_file import substitute_response_files -from tools.shared import WINDOWS, asstr, path_from_root, exit_with_error +from tools.shared import WINDOWS, asstr, path_from_root, exit_with_error, asmjs_mangle from tools.toolchain_profiler import ToolchainProfiler from tools.minified_js_name_generator import MinifiedJsNameGenerator @@ -2245,7 +2245,7 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, # Store exports for Closure copmiler to be able to track these as globals in # -s DECLARE_ASM_MODULE_EXPORTS=0 builds. - shared.Settings.MODULE_EXPORTS = [asmjs_mangle(f) for f in exports] + shared.Settings.MODULE_EXPORTS = [f for f in exports] if shared.Settings.ASYNCIFY: exports += ['asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind'] diff --git a/tools/shared.py b/tools/shared.py index 451b3c55fa31e..70c9f490e691b 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1446,6 +1446,26 @@ def demangle_c_symbol_name(name): return name[1:] if name.startswith('_') else '$' + name +def treat_as_user_function(name): + if name.startswith('dynCall_'): + return False + if name in Settings.WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED: + return False + return True + + +def asmjs_mangle(name): + """Mangle a name the way asm.js/JSBackend globals are mangled. + + Prepends '_' and replaces non-alphanumerics with '_'. + Used by wasm backend for JS library consistency with asm.js. + """ + if treat_as_user_function(name): + return '_' + name + else: + return name + + # Building class Building(object): COMPILER = CLANG @@ -2564,7 +2584,7 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): logger.debug('running meta-DCE') temp_files = configuration.get_temp_files() # first, get the JS part of the graph - extra_info = '{ "exports": [' + ','.join(map(lambda x: '["' + x + '","' + x + '"]', Settings.MODULE_EXPORTS)) + ']}' + extra_info = '{ "exports": [' + ','.join(map(lambda x: '["' + (asmjs_mangle(x) if Settings.WASM_BACKEND else x) + '","' + x + '"]', Settings.MODULE_EXPORTS)) + ']}' txt = Building.acorn_optimizer(js_file, ['emitDCEGraph', 'noPrint'], return_output=True, extra_info=extra_info) graph = json.loads(txt) # add exports based on the backend output, that are not present in the JS From 99d7f4d6e7fa62e9728efa9f556ac6e051534e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 18 Dec 2019 22:28:27 +0200 Subject: [PATCH 6/8] Fix flake and MINIMAL_RUNTIME build --- emscripten.py | 22 +--------------------- src/parseTools.js | 9 +++++++++ src/postamble_minimal.js | 2 +- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/emscripten.py b/emscripten.py index 94b5533515886..2bee47fc0cf21 100644 --- a/emscripten.py +++ b/emscripten.py @@ -25,7 +25,7 @@ from tools import gen_struct_info from tools import jsrun from tools.response_file import substitute_response_files -from tools.shared import WINDOWS, asstr, path_from_root, exit_with_error, asmjs_mangle +from tools.shared import WINDOWS, asstr, path_from_root, exit_with_error, asmjs_mangle, treat_as_user_function from tools.toolchain_profiler import ToolchainProfiler from tools.minified_js_name_generator import MinifiedJsNameGenerator @@ -2785,26 +2785,6 @@ def create_invoke_wrappers(invoke_funcs): return invoke_wrappers -def treat_as_user_function(name): - if name.startswith('dynCall_'): - return False - if name in shared.Settings.WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED: - return False - return True - - -def asmjs_mangle(name): - """Mangle a name the way asm.js/JSBackend globals are mangled. - - Prepends '_' and replaces non-alphanumerics with '_'. - Used by wasm backend for JS library consistency with asm.js. - """ - if treat_as_user_function(name): - return '_' + name - else: - return name - - def normalize_line_endings(text): """Normalize to UNIX line endings. diff --git a/src/parseTools.js b/src/parseTools.js index 8b1e44f8be5a6..c4a7b4b43348c 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1631,3 +1631,12 @@ function makeRemovedFSAssert(fsName) { if (SYSTEM_JS_LIBRARIES.indexOf('library_' + lower + '.js') >= 0) return ''; return "var " + fsName + " = '" + fsName + " is no longer included by default; build with -l" + lower + ".js';"; } + +// Given an array of elements [elem1,elem2,elem3], returns a string "['elem1','elem2','elem3']" +function buildStringArray(array) { + if (array.length > 0) { + return "['" + array.join("','") + "']"; + } else { + return []; + } +} diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index b02d7d92553c2..2ef3cc95a3555 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -96,7 +96,7 @@ WebAssembly.instantiate(Module['wasm'], imports).then(function(output) { // https://github.com/emscripten-core/emscripten/issues/10054 // Keep in sync with emscripten.py function treat_as_user_function(name). function asmjs_mangle(x) { - var unmangledSymbols = {{{ WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED }}}; + var unmangledSymbols = {{{ buildStringArray(WASM_FUNCTIONS_THAT_ARE_NOT_NAME_MANGLED) }}}; return x.indexOf('dynCall_') == 0 || unmangledSymbols.indexOf(x) != -1 ? x : '_' + x; } From 0d5720bc62f5e3ab1db76355537709b99635a5d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 19 Dec 2019 09:15:06 +0200 Subject: [PATCH 7/8] Fix wasm2.test_no_declare_asm_module_exports --- emscripten.py | 7 ++++--- tools/shared.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/emscripten.py b/emscripten.py index 2bee47fc0cf21..e8cabcbcf0e01 100644 --- a/emscripten.py +++ b/emscripten.py @@ -1720,7 +1720,8 @@ def create_receiving(function_table_data, function_tables_defs, exported_impleme ''' % {'name': name, 'runtime_assertions': runtime_assertions}) receiving = '\n'.join(wrappers) - shared.Settings.MODULE_EXPORTS = module_exports = exported_implemented_functions + function_tables(function_table_data) + module_exports = exported_implemented_functions + function_tables(function_table_data) + shared.Settings.MODULE_EXPORTS = [(f, f) for f in module_exports] if not shared.Settings.SWAPPABLE_ASM_MODULE: if shared.Settings.DECLARE_ASM_MODULE_EXPORTS: @@ -2243,9 +2244,9 @@ def emscript_wasm_backend(infile, outfile, memfile, compiler_engine, exports = metadata['exports'] - # Store exports for Closure copmiler to be able to track these as globals in + # Store exports for Closure compiler to be able to track these as globals in # -s DECLARE_ASM_MODULE_EXPORTS=0 builds. - shared.Settings.MODULE_EXPORTS = [f for f in exports] + shared.Settings.MODULE_EXPORTS = [(asmjs_mangle(f), f) for f in exports] if shared.Settings.ASYNCIFY: exports += ['asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind'] diff --git a/tools/shared.py b/tools/shared.py index 70c9f490e691b..4031021a8a12f 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -2472,7 +2472,7 @@ def closure_compiler(filename, pretty=True, advanced=True, extra_closure_args=[] # externs file for the exports, Closure is able to reason about the exports. if Settings.MODULE_EXPORTS and not Settings.DECLARE_ASM_MODULE_EXPORTS: # Generate an exports file that records all the exported symbols from asm.js/wasm module. - module_exports_suppressions = '\n'.join(['/**\n * @suppress {duplicate, undefinedVars}\n */\nvar %s;\n' % i for i in Settings.MODULE_EXPORTS]) + module_exports_suppressions = '\n'.join(['/**\n * @suppress {duplicate, undefinedVars}\n */\nvar %s;\n' % i for i, j in Settings.MODULE_EXPORTS]) exports_file = configuration.get_temp_files().get('_module_exports.js') exports_file.write(module_exports_suppressions.encode()) exports_file.close() @@ -2584,7 +2584,7 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): logger.debug('running meta-DCE') temp_files = configuration.get_temp_files() # first, get the JS part of the graph - extra_info = '{ "exports": [' + ','.join(map(lambda x: '["' + (asmjs_mangle(x) if Settings.WASM_BACKEND else x) + '","' + x + '"]', Settings.MODULE_EXPORTS)) + ']}' + extra_info = '{ "exports": [' + ','.join(map(lambda x: '["' + x[0] + '","' + x[1] + '"]', Settings.MODULE_EXPORTS)) + ']}' txt = Building.acorn_optimizer(js_file, ['emitDCEGraph', 'noPrint'], return_output=True, extra_info=extra_info) graph = json.loads(txt) # add exports based on the backend output, that are not present in the JS @@ -2593,7 +2593,7 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): for item in graph: if 'export' in item: exports.add(item['export']) - for export in Settings.MODULE_EXPORTS: + for export, unminified in Settings.MODULE_EXPORTS: if export not in exports: graph.append({ 'export': export, From f5a8a9a1cae97eee0bd2ae66dfb88264e328b514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 19 Dec 2019 11:04:28 +0200 Subject: [PATCH 8/8] Fix fastcomp MINIMAL_RUNTIME --- emscripten.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index e8cabcbcf0e01..e54ceb26b9fb2 100644 --- a/emscripten.py +++ b/emscripten.py @@ -417,7 +417,7 @@ def define_asmjs_import_names(imports): post = apply_static_code_hooks(post) if shared.Settings.MINIMAL_RUNTIME: - post, receiving = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], shared.Settings.MODULE_EXPORTS, receiving) + post, receiving = compute_minimal_runtime_initializer_and_exports(post, metadata['initializers'], [mangled for mangled, unmangled in shared.Settings.MODULE_EXPORTS], receiving) function_tables_impls = make_function_tables_impls(function_table_data) final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs